Skip to content

Commit

Permalink
make: initial Rust language support
Browse files Browse the repository at this point in the history
The idea is to integrate rust into the existing build system as well as
possible and to not use a separate buildsystem like cargo or xargo.

Makefile.base has been modified to include support for main.rs and
lib.rs files and a Makefile.rust has been added which sets a few rust
related variables.

This is very loosly based on RIOT-OS#5740
  • Loading branch information
nmeum committed Nov 26, 2016
1 parent ccf8c27 commit 16eb2d6
Show file tree
Hide file tree
Showing 25 changed files with 327 additions and 6 deletions.
36 changes: 30 additions & 6 deletions Makefile.base
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ endif
ifeq ($(strip $(SRCXX)),)
SRCXX := $(wildcard *.cpp)
endif
ifeq ($(strip $(RUSTSRC)),)
ifneq (,$(wildcard main.rs))
CRATE_TYPE = bin
RUSTSRC = main.rs
else ifneq (,$(wildcard lib.rs))
CRATE_TYPE = lib
RUSTSRC = lib.rs
endif
endif
ifeq ($(strip $(ASMSRC)),)
ASMSRC := $(wildcard *.s)
endif
Expand All @@ -39,6 +48,7 @@ OBJC_LTO := $(SRC:%.c=$(BINDIR)/$(MODULE)/%.o)
OBJC_NOLTO := $(SRC_NOLTO:%.c=$(BINDIR)/$(MODULE)/%.o)
OBJC := $(OBJC_NOLTO) $(OBJC_LTO)
OBJCXX := $(SRCXX:%.cpp=$(BINDIR)/$(MODULE)/%.o)
RUSTOBJ := $(RUSTSRC:%.rs=$(BINDIR)/$(MODULE)/%.o)
ASMOBJ := $(ASMSRC:%.s=$(BINDIR)/$(MODULE)/%.o)
ASSMOBJ := $(ASSMSRC:%.S=$(BINDIR)/$(MODULE)/%.o)

Expand All @@ -48,12 +58,6 @@ DEP := $(OBJC:.o=.d) $(OBJCXX:.o=.d) $(ASSMOBJ:.o=.d)
$(BINDIR)/$(MODULE)/:
$(AD)mkdir -p $@

$(BINDIR)/$(MODULE).a $(OBJ): | $(BINDIR)/$(MODULE)/

$(BINDIR)/$(MODULE).a: $(OBJ) | ${DIRS:%=ALL--%}
$(AD)$(AR) $(ARFLAGS) $@ $?


CXXFLAGS = $(filter-out $(CXXUWFLAGS), $(CFLAGS)) $(CXXEXFLAGS)

# compile and generate dependency info
Expand All @@ -72,6 +76,26 @@ $(OBJCXX): $(BINDIR)/$(MODULE)/%.o: %.cpp $(RIOTBUILD_CONFIG_HEADER_C)
-DRIOT_FILE_NOPATH=\"$(notdir $<)\" \
$(CXXFLAGS) $(INCLUDES) $(CXXINCLUDES) -MD -MP -c -o $@ $(abspath $<)

ifdef CRATE_TYPE
include $(RIOTBASE)/Makefile.rust
endif

ifeq ($(CRATE_TYPE),bin)
$(RUSTOBJ): $(RUSTSRC) | $(BINDIR)/$(MODULE)/ ${DIRS:%=ALL--%}
$(AD)rustc $(RUSTC_FLAGS) --emit obj -o $@ $?
$(BINDIR)/$(MODULE).a: $(RUSTOBJ)
$(AD)$(AR) $(ARFLAGS) $@ $?
else ifeq ($(CRATE_TYPE),lib)
$(BINDIR)/$(LIBNAME).rlib: $(RUSTSRC) | $(BINDIR)/$(MODULE)/ ${DIRS:%=ALL--%}
$(AD)rustc $(RUSTC_FLAGS) -o $@ $?
$(BINDIR)/$(MODULE).a: $(BINDIR)/$(LIBNAME).rlib
$(AD)ln -fs $^ $@
else
$(BINDIR)/$(MODULE).a $(OBJ): | $(BINDIR)/$(MODULE)/
$(BINDIR)/$(MODULE).a: $(OBJ) | ${DIRS:%=ALL--%}
$(AD)$(AR) $(ARFLAGS) $@ $?
endif

$(ASMOBJ): $(BINDIR)/$(MODULE)/%.o: %.s
$(AD)$(AS) $(ASFLAGS) -o $@ $(abspath $<)

Expand Down
37 changes: 37 additions & 0 deletions Makefile.rust
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Function to get `lib*` name for a module. This is
# needed because rustc expects files passed to
# `--extern` to match the pattern `lib*.rlib`.
define libname
$(shell dirname $1)/lib$(shell basename $1)
endef

# Set module name and library name (see above).
MODULE ?= $(shell basename $(CURDIR))
ifeq (lib,$(CRATE_TYPE))
LIBNAME = $(call libname,$(MODULE))
endif

# Set crate metadata in case they aren't set.
CRATE_TYPE ?= bin
CRATE_NAME ?= $(shell basename $(MODULE))

# Make our custom targets known to rustc.
export RUST_TARGET_PATH ?= $(RIOTBASE)/dist/rust

# Set the rust target accordingly.
ifeq ($(BOARD),native)
RUST_TARGET=i586-unknown-linux-gnu
else
RUST_TARGET=$(CPU_ARCH)
endif

# Flags to pass to the rust compiler.
RUSTC_FLAGS += --crate-name $(CRATE_NAME) \
--crate-type $(CRATE_TYPE) \
--target $(RUST_TARGET) \
--extern core=$(BINDIR)/libcore.rlib

# Include dependencies in `RUSTC_FLAGS`.
RUSTMODULE ?= $(filter rust/%, $(USEMODULE))
RUSTC_FLAGS += $(foreach dep,$(RUSTMODULE),--extern \
$(shell basename $(dep))=$(BINDIR)/$(call libname,$(dep)).rlib)
3 changes: 3 additions & 0 deletions boards/native/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ FEATURES_PROVIDED += ethernet

# The board MPU family (used for grouping by the CI system)
FEATURES_MCU_GROUP = x86

# Native rust builds are supported
FEATURES_PROVIDED += rust_support
3 changes: 3 additions & 0 deletions cpu/Makefile.include.cortexm_common
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ include $(RIOTCPU)/cortexm_common/Makefile.include
USEMODULE += newlib_nano
export USE_NANO_SPECS = 1

# all Cortex-M are supported by RIOT's Rust integration
FEATURES_PROVIDED += rust_support

# Avoid overriding the default rule:
all:

Expand Down
13 changes: 13 additions & 0 deletions dist/rust/cortex-m0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arch": "arm",
"cpu": "cortex-m0",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"llvm-target": "thumbv6m-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
13 changes: 13 additions & 0 deletions dist/rust/cortex-m0plus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arch": "arm",
"cpu": "cortex-m0plus",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"llvm-target": "thumbv6m-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
13 changes: 13 additions & 0 deletions dist/rust/cortex-m1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arch": "arm",
"cpu": "cortex-m1",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"llvm-target": "thumbv6m-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
13 changes: 13 additions & 0 deletions dist/rust/cortex-m3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arch": "arm",
"cpu": "cortex-m3",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"llvm-target": "thumbv7m-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
14 changes: 14 additions & 0 deletions dist/rust/cortex-m4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"arch": "arm",
"cpu": "cortex-m4",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"features": "+soft-float",
"llvm-target": "thumbv7em-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
13 changes: 13 additions & 0 deletions dist/rust/cortex-m4f.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arch": "arm",
"cpu": "cortex-m4",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"llvm-target": "thumbv7em-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
14 changes: 14 additions & 0 deletions dist/rust/cortex-m7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"arch": "arm",
"cpu": "cortex-m7",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"features": "+soft-float",
"llvm-target": "thumbv7em-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
14 changes: 14 additions & 0 deletions dist/rust/cortex-m7f-sp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"arch": "arm",
"cpu": "cortex-m7",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"features": "+fp-only-sp",
"llvm-target": "thumbv7em-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
13 changes: 13 additions & 0 deletions dist/rust/cortex-m7f.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"arch": "arm",
"cpu": "cortex-m7",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"executables": true,
"llvm-target": "thumbv7em-none-eabi",
"no-compiler-rt": true,
"os": "none",
"pre-link-args": [],
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
2 changes: 2 additions & 0 deletions dist/tools/genconfigheader/genconfigheader.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ for arg in "$@"; do
-D*)
# Strip leading -D
d=${arg#-D}
# Replace '/' with '_'
d=$(echo $d | tr '/' '_')
if [ -z "${d##*=*}" ]; then
# key=value pairs
key=${d%%=*}
Expand Down
24 changes: 24 additions & 0 deletions examples/rust/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Name of your application.
APPLICATION = rust

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

# The boards needs to support rust.
FEATURES_REQUIRED += rust_support

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# Since we built rust with `#![no_std]` we need libcore.
USEPKG += libcore

# Module dependencies.
USEMODULE += rust/common # Provides lang_items
USEMODULE += rust/fmt # Wrapper for sys/fmt
USEMODULE += fmt # Needed by the wrapper

include $(RIOTBASE)/Makefile.include
25 changes: 25 additions & 0 deletions examples/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Introduction

This example shows how to write a RIOT application using Rust.

# Status

As Rust's libstd is too fat for our little MCUs, it is currently only
possible to write Rust applications using `#![no_std]`. However, crates
to map RIOT's API to rust are available in `sys/rust`.

# Prerequisites

Since building binary packages with `#![no_std]` is currently not
supported by the rust stable channel you need a nightly rust toolchain.

After installing the nightly rust toolchain including cargo you should
be good to go.

# Trying the example

As always,

# make BOARD=native all term

is all you need.
14 changes: 14 additions & 0 deletions examples/rust/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![no_main]
#![no_std]

extern crate fmt;
extern crate common;

use fmt::Stdout;
use core::fmt::Write;

#[no_mangle]
pub fn main() {
let mut out = Stdout {};
out.write_str("Hello World!\n");
}
17 changes: 17 additions & 0 deletions pkg/libcore/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PKG_NAME = libcore
PKG_URL = https://github.com/rust-lang/rust
PKG_VERSION = $(shell ./version.sh)

.PHONY: all

all: $(BINDIR)/$(PKG_NAME).a
$(BINDIR)/$(PKG_NAME).a: $(BINDIR)/$(PKG_NAME).rlib
$(AD)ln -fs $< $@

$(BINDIR)/$(PKG_NAME).rlib: git-download
$(AD)rustc --crate-name core --crate-type lib \
--target $(RUST_TARGET) -o $@ \
$(PKG_BUILDDIR)/src/libcore/lib.rs

include $(RIOTBASE)/Makefile.rust
include $(RIOTBASE)/pkg/pkg.mk
15 changes: 15 additions & 0 deletions pkg/libcore/version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/sh

RUST_VERSION=""
if ! command -v rustc >/dev/null 2>&1; then
echo "Missing rustc, please install it" 2>&1
exit 1
fi

RUST_VERSION="$(rustc --version | cut -d ' ' -f2-)"
if [ "$(echo ${RUST_VERSION} | cut -d '-' -f2 \
| cut -d ' ' -f1)" = "nightly" ]; then
RUST_VERSION="$(echo ${RUST_VERSION} | cut -d ' ' -f2 | tr -d '()')"
fi

echo "${RUST_VERSION}"
4 changes: 4 additions & 0 deletions sys/rust/Makefile.crate
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Modules in this directory should be prefixed with a `rust/`.
MODULE = rust/$(shell basename $(CURDIR))

include $(RIOTBASE)/Makefile.base
1 change: 1 addition & 0 deletions sys/rust/common/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/sys/rust/Makefile.crate
5 changes: 5 additions & 0 deletions sys/rust/common/lang_items.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[lang = "panic_fmt"]
extern fn panic_fmt() {}

#[lang = "eh_personality"]
fn eh_personality() {}
4 changes: 4 additions & 0 deletions sys/rust/common/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![feature(lang_items)]
#![no_std]

pub mod lang_items;
1 change: 1 addition & 0 deletions sys/rust/fmt/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/sys/rust/Makefile.crate
22 changes: 22 additions & 0 deletions sys/rust/fmt/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![no_std]

use core::fmt;
use core::fmt::Result;
use core::result;

extern {
fn print(ptr: *const u8, len: usize) -> ();
}

// A handle to the global standard output stream of the current process.
//
// This type is similar to `std::io::Stdout` except that it implements
// `core::fmt::Write` instead of `std::io::Write`.
pub struct Stdout;

impl fmt::Write for Stdout {
fn write_str(&mut self, s: &str) -> Result {
unsafe { print(s.as_ptr(), s.len()); }
result::Result::Ok(())
}
}

0 comments on commit 16eb2d6

Please sign in to comment.