mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #16274 from chrysn-pull-requests/rust-application
Add some Rust building infrastructure and example
This commit is contained in:
commit
afdabcf9b6
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,5 +1,6 @@
|
||||
*.a binary
|
||||
*.patch binary
|
||||
Cargo.lock binary
|
||||
# Default conflict marker size is 7 which causes some of the headings in
|
||||
# release-notes.txt to trigger git diff --check: 'leftover conflict marker'
|
||||
# when the heading is exactly 7 characters long.
|
||||
|
@ -216,6 +216,8 @@ ifeq (,$(TOOLCHAIN))
|
||||
override TOOLCHAIN := gnu
|
||||
endif
|
||||
|
||||
include $(RIOTMAKE)/cargo-settings.inc.mk
|
||||
|
||||
GLOBAL_GOALS += buildtest \
|
||||
buildtest-indocker \
|
||||
info-boards-features-blacklisted \
|
||||
@ -1033,6 +1035,8 @@ endif
|
||||
|
||||
endif
|
||||
|
||||
include $(RIOTMAKE)/cargo-targets.inc.mk
|
||||
|
||||
# include RIOT_MAKEFILES_GLOBAL_POST configuration files
|
||||
# allows setting user specific system wide configuration parsed after the body
|
||||
# of $(RIOTBASE)/Makefile.include
|
||||
|
@ -66,37 +66,44 @@ config CPU_CORE_CORTEX_M0
|
||||
bool
|
||||
select CPU_ARCH_ARMV6M
|
||||
select CPU_CORE_CORTEX_M
|
||||
select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M0PLUS
|
||||
bool
|
||||
select CPU_ARCH_ARMV6M
|
||||
select CPU_CORE_CORTEX_M
|
||||
select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M23
|
||||
bool
|
||||
select CPU_ARCH_ARMV8M
|
||||
select CPU_CORE_CORTEX_M
|
||||
#select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M3
|
||||
bool
|
||||
select CPU_ARCH_ARMV7M
|
||||
select CPU_CORE_CORTEX_M
|
||||
select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M33
|
||||
bool
|
||||
select CPU_ARCH_ARMV8M
|
||||
select CPU_CORE_CORTEX_M
|
||||
#select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M4
|
||||
bool
|
||||
select CPU_ARCH_ARMV7M
|
||||
select CPU_CORE_CORTEX_M
|
||||
select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M4F
|
||||
bool
|
||||
select CPU_ARCH_ARMV7M
|
||||
select CPU_CORE_CORTEX_M
|
||||
select HAS_CORTEXM_FPU
|
||||
select HAS_RUST_TARGET
|
||||
|
||||
config CPU_CORE_CORTEX_M7
|
||||
bool
|
||||
|
@ -18,24 +18,38 @@ ifneq (,$(filter $(CPU_CORE),cortex-m4f cortex-m7))
|
||||
endif
|
||||
|
||||
# Set CPU_ARCH depending on the CPU_CORE
|
||||
#
|
||||
# RUST_TARGET is only used when building Rust code; any users need to require
|
||||
# the `rust_target` feature to esnure things are checked properly.
|
||||
ifeq ($(CPU_CORE),cortex-m0)
|
||||
CPU_ARCH := armv6m
|
||||
RUST_TARGET = thumbv6m-none-eabi
|
||||
else ifeq ($(CPU_CORE),cortex-m0plus)
|
||||
CPU_ARCH := armv6m
|
||||
RUST_TARGET = thumbv6m-none-eabi
|
||||
else ifeq ($(CPU_CORE),cortex-m23)
|
||||
CPU_ARCH := armv8m
|
||||
#RUST_TARGET = thumbv8m.base-none-eabi
|
||||
else ifeq ($(CPU_CORE),cortex-m3)
|
||||
CPU_ARCH := armv7m
|
||||
RUST_TARGET = thumbv7m-none-eabi
|
||||
else ifeq ($(CPU_CORE),cortex-m33)
|
||||
CPU_ARCH := armv8m
|
||||
#RUST_TARGET = thumbv8m.main-none-eabi
|
||||
else ifeq ($(CPU_CORE),cortex-m4)
|
||||
CPU_ARCH := armv7m
|
||||
RUST_TARGET = thumbv7em-none-eabi
|
||||
else ifeq ($(CPU_CORE),cortex-m4f)
|
||||
CPU_ARCH := armv7m
|
||||
RUST_TARGET = thumbv7em-none-eabihf
|
||||
else ifeq ($(CPU_CORE),cortex-m7)
|
||||
CPU_ARCH := armv7m
|
||||
else
|
||||
$(error Unkwnown cortexm core: $(CPU_CORE))
|
||||
endif
|
||||
|
||||
ifneq (,$(RUST_TARGET))
|
||||
FEATURES_PROVIDED += rust_target
|
||||
endif
|
||||
|
||||
FEATURES_PROVIDED += no_idle_thread
|
||||
|
@ -62,6 +62,7 @@ config NATIVE_OS_LINUX
|
||||
select HAS_PERIPH_GPIO
|
||||
select HAS_PERIPH_GPIO_IRQ
|
||||
select HAS_PERIPH_SPI
|
||||
select HAS_RUST_TARGET if "$(OS_ARCH)" = "x86_64"
|
||||
|
||||
config NATIVE_OS_FREEBSD
|
||||
bool
|
||||
|
@ -18,6 +18,9 @@ FEATURES_PROVIDED += periph_hwrng
|
||||
FEATURES_PROVIDED += periph_pm
|
||||
FEATURES_PROVIDED += periph_pwm
|
||||
FEATURES_PROVIDED += periph_timer_periodic
|
||||
ifeq ($(OS) $(OS_ARCH),Linux x86_64)
|
||||
FEATURES_PROVIDED += rust_target
|
||||
endif
|
||||
FEATURES_PROVIDED += ssp
|
||||
|
||||
ifeq ($(OS),Linux)
|
||||
|
@ -10,3 +10,8 @@ ifneq (,$(filter periph_can,$(USEMODULE)))
|
||||
endif
|
||||
|
||||
TOOLCHAINS_SUPPORTED = gnu llvm afl
|
||||
|
||||
# Platform triple as used by Rust
|
||||
ifeq ($(OS) $(OS_ARCH),Linux x86_64)
|
||||
RUST_TARGET = i686-unknown-linux-gnu
|
||||
endif
|
||||
|
@ -12,6 +12,7 @@ config CPU_ARCH_RISCV
|
||||
select HAS_NEWLIB
|
||||
select HAS_PERIPH_CORETIMER
|
||||
select HAS_PICOLIBC if '$(RIOT_CI_BUILD)' != '1'
|
||||
#select HAS_RUST_TARGET
|
||||
select HAS_SSP
|
||||
|
||||
select MODULE_MALLOC_THREAD_SAFE if TEST_KCONFIG
|
||||
|
@ -8,6 +8,7 @@ FEATURES_PROVIDED += cpp
|
||||
FEATURES_PROVIDED += libstdcpp
|
||||
FEATURES_PROVIDED += newlib
|
||||
FEATURES_PROVIDED += periph_coretimer
|
||||
#FEATURES_PROVIDED += rust_target
|
||||
FEATURES_PROVIDED += ssp
|
||||
|
||||
# RISC-V toolchain on CI does not work properly with picolibc yet
|
||||
|
3
dist/tools/codespell/ignored_words.txt
vendored
3
dist/tools/codespell/ignored_words.txt
vendored
@ -113,3 +113,6 @@ filp
|
||||
|
||||
# Ether (Scapy class name and means Ethernet in some parts of the code) => Either
|
||||
ether
|
||||
|
||||
# crate (Rust's package format) => create
|
||||
crate
|
||||
|
11
dist/tools/compile_commands/compile_commands.py
vendored
11
dist/tools/compile_commands/compile_commands.py
vendored
@ -191,6 +191,12 @@ def write_compile_command(state, compiler, src, flags, cdetails, path):
|
||||
arguments = [compiler, '-DRIOT_FILE_RELATIVE="' + os.path.join(cdetails.dir, src) + '"',
|
||||
'-DRIOT_FILE_NOPATH="' + src + '"']
|
||||
arguments += flags
|
||||
if '-c' in arguments:
|
||||
# bindgen is unhappy with multiple -c (that would be created by the -c
|
||||
# added later) and even with the -c showing up anywhere between other
|
||||
# arguments.
|
||||
assert arguments.count('-c') == 1, "Spurious duplicate -c arguments"
|
||||
arguments.remove('-c')
|
||||
arguments += ['-MQ', obj, '-MD', '-MP', '-c', '-o', obj, src]
|
||||
entry = {
|
||||
'arguments': arguments,
|
||||
@ -288,5 +294,8 @@ if __name__ == '__main__':
|
||||
if _args.clangd:
|
||||
_args.add_built_in_includes = True
|
||||
_args.add_libstdcxx_includes = True
|
||||
_args.filter_out = ['-Wformat-truncation', '-Wformat-overflow', '-mno-thumb-interwork']
|
||||
_args.filter_out = ['-Wformat-truncation', '-Wformat-overflow', '-mno-thumb-interwork',
|
||||
# Only even included for versions of GCC that support it
|
||||
'-malign-data=natural',
|
||||
]
|
||||
generate_compile_commands(_args)
|
||||
|
BIN
examples/rust-gcoap/Cargo.lock
generated
Normal file
BIN
examples/rust-gcoap/Cargo.lock
generated
Normal file
Binary file not shown.
16
examples/rust-gcoap/Cargo.toml
Normal file
16
examples/rust-gcoap/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "rust-gcoap"
|
||||
version = "0.1.0"
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
edition = "2018"
|
||||
resolver = "2"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
riot-wrappers = { version = "^0.7", features = [ "with_coap_message", "with_coap_handler" ] }
|
||||
|
||||
coap-message-demos = { git = "https://gitlab.com/chrysn/coap-message-demos/", default-features = false }
|
||||
coap-handler-implementations = "0.1"
|
||||
riot-coap-handler-demos = { git = "https://gitlab.com/etonomy/riot-module-examples/", features = [ "vfs" ] }
|
39
examples/rust-gcoap/Makefile
Normal file
39
examples/rust-gcoap/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
# name of your application
|
||||
APPLICATION = rust_gcoap
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# Basic networking, and gcoap
|
||||
USEMODULE += gcoap
|
||||
USEMODULE += netdev_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
USEMODULE += gnrc_icmpv6_echo
|
||||
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_usec
|
||||
USEMODULE += ztimer_msec
|
||||
USEMODULE += ztimer_sec
|
||||
|
||||
USEMODULE += vfs
|
||||
USEMODULE += constfs
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
DEVELHELP ?= 1
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# The name of crate (as per Cargo.toml package name, but with '-' replaced with '_')
|
||||
APPLICATION_RUST_MODULE = rust_gcoap
|
||||
BASELIBS += $(APPLICATION_RUST_MODULE).module
|
||||
|
||||
FEATURES_REQUIRED += rust_target
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
25
examples/rust-gcoap/Makefile.ci
Normal file
25
examples/rust-gcoap/Makefile.ci
Normal file
@ -0,0 +1,25 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
blackpill \
|
||||
bluepill \
|
||||
bluepill-stm32f030c8 \
|
||||
i-nucleo-lrwan1 \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-f302r8 \
|
||||
nucleo-f303k8 \
|
||||
nucleo-f334r8 \
|
||||
nucleo-l011k4 \
|
||||
nucleo-l031k6 \
|
||||
nucleo-l053r8 \
|
||||
samd10-xmini \
|
||||
saml10-xpro \
|
||||
saml11-xpro \
|
||||
slstk3400a \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
stm32f0discovery \
|
||||
stm32g0316-disco \
|
||||
stm32l0538-disco \
|
||||
stm32mp157c-dk2 \
|
||||
#
|
53
examples/rust-gcoap/README.md
Normal file
53
examples/rust-gcoap/README.md
Normal file
@ -0,0 +1,53 @@
|
||||
gcoap used with Rust
|
||||
====================
|
||||
|
||||
This is the advanced Rust example; see ../rust-hello-world/ for the basics.
|
||||
|
||||
In extension to the basic example, it shows:
|
||||
|
||||
* C code can be mixed with Rust code easily; any C file is built and linked
|
||||
as in applications without Rust.
|
||||
|
||||
While it's technically possible to have header files for that code,
|
||||
it is easier (and likewise often done in C applications)
|
||||
to just translate the entry function's signature manually,
|
||||
as is done with the `do_vfs_init()` function.
|
||||
|
||||
* Code of Rust applications can be spread out into modules,
|
||||
even if it builds on RIOT components.
|
||||
|
||||
The CoAP handler built in the main function
|
||||
combines generic CoAP components (from `coap_message_demos`)
|
||||
with RIOT specific components (from `riot-coap-handler-demos`).
|
||||
|
||||
* Many features of RIOT are exposed to Rust through the riot-wrappers crate,
|
||||
which provides safe wrappers around RIOT structures.
|
||||
|
||||
In this example, the abovementioned CoAP handler is run on the gcoap server,
|
||||
for which the wrappers provide adaptation to the platform independent handler interface.
|
||||
|
||||
Then, ztimer is used to sleep until the network interfaces are expected to be ready.
|
||||
|
||||
Finally, the available network interfaces are iterated over
|
||||
and queried for their IP addresses,
|
||||
which makes it easier (in absence of an interactive shell) to find which address CoAP requests can be directed at.
|
||||
|
||||
How to use
|
||||
----------
|
||||
|
||||
```
|
||||
$ make all flash term
|
||||
[...]
|
||||
main(): This is RIOT! (Version: 2022.01-devel-560-g7f8ed-rust-application)
|
||||
constfs mounted successfully
|
||||
CoAP server ready; waiting for interfaces to settle before reporting addresses...
|
||||
Active interface from PID KernelPID(6) ("gnrc_netdev_tap")
|
||||
Address fe80:0000:0000:0000:1234:56ff:fe78:90ab
|
||||
Address 2a02:0b18:c13b:8018:1234:56ff:fe78:90ab
|
||||
```
|
||||
|
||||
Once that is ready, in a parallel shell, run:
|
||||
|
||||
```
|
||||
$ aiocoap-client 'coap://[2a02:0b18:c13b:8018:1234:56ff:fe78:90ab]/.well-known/core'
|
||||
```
|
57
examples/rust-gcoap/src/lib.rs
Normal file
57
examples/rust-gcoap/src/lib.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2020 Christian Amsüss
|
||||
//
|
||||
// This file is subject to the terms and conditions of the GNU Lesser
|
||||
// General Public License v2.1. See the file LICENSE in the top level
|
||||
// directory for more details.
|
||||
#![no_std]
|
||||
|
||||
use riot_wrappers::{riot_main, println};
|
||||
use riot_wrappers::{gcoap, thread, ztimer, gnrc};
|
||||
|
||||
use coap_handler_implementations::{ReportingHandlerBuilder, HandlerBuilder};
|
||||
|
||||
riot_main!(main);
|
||||
|
||||
fn main() {
|
||||
extern "C" {
|
||||
fn do_vfs_init();
|
||||
}
|
||||
|
||||
unsafe { do_vfs_init() };
|
||||
|
||||
let handler = coap_message_demos::full_application_tree(None)
|
||||
.below(&["ps"], riot_coap_handler_demos::ps::ps_tree())
|
||||
.below(&["vfs"], riot_coap_handler_demos::vfs::vfs("/const"))
|
||||
.with_wkc()
|
||||
;
|
||||
let mut handler = riot_wrappers::coap_handler::GcoapHandler(handler);
|
||||
|
||||
let mut listener = gcoap::SingleHandlerListener::new_catch_all(&mut handler);
|
||||
|
||||
gcoap::scope(|greg| {
|
||||
greg.register(&mut listener);
|
||||
|
||||
println!("CoAP server ready; waiting for interfaces to settle before reporting addresses...");
|
||||
|
||||
let sectimer = ztimer::ZTimer::sec();
|
||||
sectimer.sleep_ticks(2);
|
||||
|
||||
for netif in gnrc::Netif::all() {
|
||||
println!("Active interface from PID {:?} ({:?})", netif.pid(), netif.pid().get_name().unwrap_or("unnamed"));
|
||||
match netif.ipv6_addrs() {
|
||||
Ok(addrs) => {
|
||||
for a in addrs.addresses() {
|
||||
println!(" Address {:?}", a);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!(" Does not support IPv6.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sending main thread to sleep; can't return or the Gcoap handler would need to be
|
||||
// deregistered (which it can't).
|
||||
loop { thread::sleep(); }
|
||||
})
|
||||
}
|
72
examples/rust-gcoap/vfs.c
Normal file
72
examples/rust-gcoap/vfs.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2018 OTA keys S.A.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file demonstrates how C code can be mixed with Rust code in an
|
||||
* application in an ad-hoc fashion.
|
||||
*/
|
||||
|
||||
#include <vfs.h>
|
||||
#include "fs/constfs.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define HELLO_WORLD_CONTENT "Hello World!\n"
|
||||
#define HELLO_RIOT_CONTENT "Hello RIOT!\n"
|
||||
|
||||
#define LARGE "1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n" \
|
||||
"1234567890---------\n"
|
||||
|
||||
static constfs_file_t constfs_files[] = {
|
||||
{
|
||||
.path = "/hello-world",
|
||||
.size = sizeof(HELLO_WORLD_CONTENT),
|
||||
.data = (const uint8_t *)HELLO_WORLD_CONTENT,
|
||||
},
|
||||
{
|
||||
.path = "/hello-riot",
|
||||
.size = sizeof(HELLO_RIOT_CONTENT),
|
||||
.data = (const uint8_t *)HELLO_RIOT_CONTENT,
|
||||
},
|
||||
{
|
||||
.path = "/large",
|
||||
.size = sizeof(LARGE),
|
||||
.data = (const uint8_t *)LARGE,
|
||||
}
|
||||
};
|
||||
|
||||
static constfs_t constfs_desc = {
|
||||
.nfiles = ARRAY_SIZE(constfs_files),
|
||||
.files = constfs_files,
|
||||
};
|
||||
|
||||
static vfs_mount_t const_mount = {
|
||||
.fs = &constfs_file_system,
|
||||
.mount_point = "/const",
|
||||
.private_data = &constfs_desc,
|
||||
};
|
||||
|
||||
void do_vfs_init(void) {
|
||||
int res = vfs_mount(&const_mount);
|
||||
if (res < 0) {
|
||||
puts("Error while mounting constfs");
|
||||
}
|
||||
else {
|
||||
puts("constfs mounted successfully");
|
||||
}
|
||||
}
|
BIN
examples/rust-hello-world/Cargo.lock
generated
Normal file
BIN
examples/rust-hello-world/Cargo.lock
generated
Normal file
Binary file not shown.
12
examples/rust-hello-world/Cargo.toml
Normal file
12
examples/rust-hello-world/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "rust-hello-world"
|
||||
version = "0.1.0"
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
edition = "2018"
|
||||
resolver = "2"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
riot-wrappers = "0.7"
|
24
examples/rust-hello-world/Makefile
Normal file
24
examples/rust-hello-world/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
# name of your application
|
||||
APPLICATION = hello-world
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
DEVELHELP ?= 1
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# The name of crate (as per Cargo.toml package name, but with '-' replaced with '_')
|
||||
APPLICATION_RUST_MODULE = rust_hello_world
|
||||
BASELIBS += $(APPLICATION_RUST_MODULE).module
|
||||
|
||||
FEATURES_REQUIRED += rust_target
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
24
examples/rust-hello-world/README.md
Normal file
24
examples/rust-hello-world/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
Hello World!
|
||||
============
|
||||
|
||||
This is a basic example how to use Rust to write your RIOT application.
|
||||
It prints out the famous text `Hello World!`.
|
||||
|
||||
This example should foremost give you an overview how an application built
|
||||
completely in Rust is structured:
|
||||
|
||||
* The Makefile resembles the regular application Makefile, see ../hello-world/
|
||||
for more introduction to that.
|
||||
|
||||
* The Cargo.toml file describes the Rust code, and declares its dependencies.
|
||||
|
||||
Prominently, it contains a `[lib]` / `crate-type = ["staticlib"]` section,
|
||||
which is necessary for how RIOT later links together the C and Rust portions.
|
||||
|
||||
* The file src/lib.rs (and any modules referenced by it) contain Rust code to
|
||||
be run.
|
||||
|
||||
It uses the `riot_main!` macro provided by the riot-wrappers crate to declare
|
||||
the entry point of the program.
|
||||
|
||||
The code itself looks like the usual Rust hello-world example.
|
15
examples/rust-hello-world/src/lib.rs
Normal file
15
examples/rust-hello-world/src/lib.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2020 Christian Amsüss
|
||||
//
|
||||
// This file is subject to the terms and conditions of the GNU Lesser
|
||||
// General Public License v2.1. See the file LICENSE in the top level
|
||||
// directory for more details.
|
||||
#![no_std]
|
||||
|
||||
use riot_wrappers::riot_main;
|
||||
use riot_wrappers::println;
|
||||
|
||||
riot_main!(main);
|
||||
|
||||
fn main() {
|
||||
println!("Hello Rust!");
|
||||
}
|
@ -42,6 +42,11 @@ config HAS_BACKUP_RAM
|
||||
help
|
||||
Indicates that Backup RAM is supported.
|
||||
|
||||
config HAS_RUST_TARGET
|
||||
bool
|
||||
help
|
||||
Indicates that a Rust target definition ("triple") is known.
|
||||
|
||||
config HAS_CPP
|
||||
bool
|
||||
help
|
||||
|
@ -54,3 +54,8 @@ CFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG) $(CFLAGS_OPT) $(CFLAGS_LINK)
|
||||
ASFLAGS += $(CFLAGS_CPU) $(CFLAGS_DBG)
|
||||
# export linker flags
|
||||
LINKFLAGS += $(CFLAGS_CPU) $(CFLAGS_LINK) $(CFLAGS_DBG) $(CFLAGS_OPT) -nostartfiles -Wl,--gc-sections -static -lgcc
|
||||
|
||||
# Platform triple as used by Rust
|
||||
RUST_TARGET = riscv32imac-unknown-none-elf
|
||||
# Workaround for https://github.com/rust-lang/rust-bindgen/issues/1555
|
||||
CARGO_EXTRACFLAGS += --target=riscv32
|
||||
|
43
makefiles/cargo-settings.inc.mk
Normal file
43
makefiles/cargo-settings.inc.mk
Normal file
@ -0,0 +1,43 @@
|
||||
# Rust's own version of the target triple / quadruple.
|
||||
#
|
||||
# This does not have a sane default, and needs to be set in the architecture
|
||||
# files.
|
||||
# RUST_TARGET = ...
|
||||
|
||||
# Flags that need to be added to the RIOT_CFLAGS passed to cargo in order to
|
||||
# make bindgen happy
|
||||
CARGO_EXTRACFLAGS ?=
|
||||
|
||||
# Setting anything other than "debug" or "release" will necessitate additional
|
||||
# -Z unstable-options as of 2021-03 nightlies.
|
||||
CARGO_PROFILE ?= release
|
||||
|
||||
# The Rust version to use.
|
||||
#
|
||||
# As long as C2Rust and riot-wrappers require nightly, the only alternative
|
||||
# here is to pick a particular nightly when something breaks.
|
||||
#
|
||||
# (Default is empty, because the riotbuild container picks a particular nightly
|
||||
# and sets it as a default; users without a nightly default need to either
|
||||
# override this here or in rustup)
|
||||
CARGO_CHANNEL ?=
|
||||
|
||||
# Note that if we did not set this explicitly, CARGO_LIB would have to
|
||||
# understand which value cargo uses in absence of CARGO_TARGET_DIR, which would
|
||||
# be $(APPDIR)/target.
|
||||
#
|
||||
# For many cases, it would be beneficial to base this on BINDIRBASE rather than
|
||||
# BINDIR, for that would allow different boards using the same CPU to share
|
||||
# compiled code (unless they they build conditionally on environment variables,
|
||||
# like riot-sys does). This is not done for two reasons:
|
||||
#
|
||||
# * Overriding BINDIR (like is done in Murdock) would not take effect,
|
||||
# requiring additional overrides to enable out-of-tree building.
|
||||
#
|
||||
# * Switching back and forth between two boards of the same CPU requires
|
||||
# riot-sys rebuilds. (On its own, this would be outweighed by the shared
|
||||
# compilation of other modules).
|
||||
CARGO_TARGET_DIR = $(BINDIR)/target
|
||||
|
||||
# The single Rust library to be built.
|
||||
CARGO_LIB = $(CARGO_TARGET_DIR)/$(RUST_TARGET)/${CARGO_PROFILE}/lib$(APPLICATION_RUST_MODULE).a
|
34
makefiles/cargo-targets.inc.mk
Normal file
34
makefiles/cargo-targets.inc.mk
Normal file
@ -0,0 +1,34 @@
|
||||
CARGO_COMPILE_COMMANDS = $(BINDIR)/cargo-compile-commands.json
|
||||
CARGO_COMPILE_COMMANDS_FLAGS = --clang
|
||||
|
||||
# This is duplicating the compile-commands rule because unlike in the use case
|
||||
# when a $(RIOTBASE)/compile_commands.json is built, we *want* this to be
|
||||
# per-board and per-application. (The large mechanisms are shared anyway).
|
||||
#
|
||||
# Changes relative to the compile-commands rule: This uses lazysponge to keep
|
||||
# Rust from rebuilding, and uses a custom output file and
|
||||
# CARGO_COMPILE_COMMAND_FLAGS.
|
||||
$(CARGO_COMPILE_COMMANDS): $(BUILDDEPS)
|
||||
$(Q)DIRS="$(DIRS)" APPLICATION_BLOBS="$(BLOBS)" \
|
||||
"$(MAKE)" -C $(APPDIR) -f $(RIOTMAKE)/application.inc.mk compile-commands
|
||||
$(Q)$(RIOTTOOLS)/compile_commands/compile_commands.py $(CARGO_COMPILE_COMMANDS_FLAGS) $(BINDIR) \
|
||||
| $(LAZYSPONGE) $@
|
||||
|
||||
|
||||
$(CARGO_LIB): $(RIOTBUILD_CONFIG_HEADER_C) $(BUILDDEPS) $(CARGO_COMPILE_COMMANDS) FORCE
|
||||
$(Q)[ x"${RUST_TARGET}" != x"" ] || (echo "Error: No RUST_TARGET was set for this platform. (Set FEATURES_REQUIRED+=rust_target to catch this earlier)."; exit 1)
|
||||
$(Q)CC= CFLAGS= CPPFLAGS= CXXFLAGS= RIOT_COMPILE_COMMANDS_JSON="$(CARGO_COMPILE_COMMANDS)" RIOT_USEMODULE="$(USEMODULE)" cargo $(patsubst +,,+${CARGO_CHANNEL}) build --target $(RUST_TARGET) `if [ x$(CARGO_PROFILE) = xrelease ]; then echo --release; else if [ x$(CARGO_PROFILE) '!=' xdebug ]; then echo "--profile $(CARGO_PROFILE)"; fi; fi` $(CARGO_OPTIONS)
|
||||
|
||||
$(APPLICATION_RUST_MODULE).module: $(CARGO_LIB) FORCE
|
||||
$(Q)# Ensure no old object files persist. These would lead to duplicate
|
||||
$(Q)# symbols, or worse, lingering behaivor of XFA entries.
|
||||
$(Q)rm -rf $(BINDIR)/$(APPLICATION_RUST_MODULE)/
|
||||
$(Q)mkdir -p $(BINDIR)/$(APPLICATION_RUST_MODULE)/
|
||||
|
||||
$(Q)# On cortex-m0 boards like airfy-beacon, the archive contains a
|
||||
$(Q)# bin/thumbv6m-none-eabi.o file; the directory must be present for
|
||||
$(Q)# ar to unpack it...
|
||||
$(Q)mkdir -p $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/
|
||||
$(Q)cd $(BINDIR)/$(APPLICATION_RUST_MODULE)/ && $(AR) x $<
|
||||
$(Q)# ... and move them back if any exist, careful to err if anything is duplicate
|
||||
$(Q)rmdir $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/ || (mv -n $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/* $(BINDIR)/$(APPLICATION_RUST_MODULE)/ && rmdir $(BINDIR)/$(APPLICATION_RUST_MODULE)/bin/)
|
@ -45,6 +45,7 @@ define board_unsatisfied_features
|
||||
undefine CPU_ARCH
|
||||
undefine CPU_CORE
|
||||
undefine CPU_FAM
|
||||
undefine RUST_TARGET
|
||||
|
||||
include $(RIOTBASE)/Makefile.features
|
||||
# always select provided architecture features
|
||||
|
@ -6,6 +6,7 @@ export QQ # as Q, but be more quiet
|
||||
export QUIET # The parameter to use whether to show verbose makefile commands or not.
|
||||
|
||||
export OS # The operating system of the build host
|
||||
export OS_ARCH # The build host's hardware architecture
|
||||
|
||||
export APPLICATION # The application, set in the Makefile which is run by the user.
|
||||
export APPLICATION_MODULE # The application module name.
|
||||
@ -41,6 +42,7 @@ export RIOTMAKE # Location of all supplemental Makefiles (such as t
|
||||
export RIOTKCONFIG # Location of all supplemental Kconfig files
|
||||
export BINDIRBASE # This is the folder where the application should be built in. For each BOARD a different subfolder is used.
|
||||
export BINDIR # This is the folder where the application should be built in.
|
||||
export CARGO_TARGET_DIR # This is the folder where Rust parts of the application should be built in.
|
||||
export BUILD_DIR # This is the base folder to store common build files and artifacts, e.g. test results.
|
||||
export APPDIR # The base folder containing the application
|
||||
export PKGDIRBASE # The base folder for building packages
|
||||
|
BIN
tests/rust_minimal/Cargo.lock
generated
Normal file
BIN
tests/rust_minimal/Cargo.lock
generated
Normal file
Binary file not shown.
12
tests/rust_minimal/Cargo.toml
Normal file
12
tests/rust_minimal/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "rust-minimal"
|
||||
version = "0.1.0"
|
||||
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||
edition = "2018"
|
||||
resolver = "2"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
riot-wrappers = "0.7"
|
8
tests/rust_minimal/Makefile
Normal file
8
tests/rust_minimal/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
APPLICATION_RUST_MODULE = rust_minimal
|
||||
BASELIBS += $(APPLICATION_RUST_MODULE).module
|
||||
|
||||
FEATURES_REQUIRED += rust_target
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
82
tests/rust_minimal/README.md
Normal file
82
tests/rust_minimal/README.md
Normal file
@ -0,0 +1,82 @@
|
||||
Test description
|
||||
----------------
|
||||
|
||||
This test ensures that code produced through Rust can actually run;
|
||||
it does that by simply printing "SUCCESS".
|
||||
|
||||
|
||||
When things fail here
|
||||
---------------------
|
||||
|
||||
This section is aimed at RIOT developers who are taken aback by this test failing
|
||||
after they changed something completely unrelated.
|
||||
|
||||
That can happen for two reasons:
|
||||
For one, RIOT's API is not as strict as library APIs
|
||||
(e.g. it is accepted that functions move between being `static inline` and not being),
|
||||
and then, Rust is sometimes stricter than C when it comes to APIs
|
||||
(e.g. no implicit numeric casting).
|
||||
|
||||
The general strategy for addressing build failures resulting from this is:
|
||||
|
||||
* Identify what breaks.
|
||||
|
||||
* Update the `riot-sys` and `riot-wrappers` crates as necessary
|
||||
to support both the old and the new version of the breaking piece.
|
||||
|
||||
If actual workarounds (like an explicit cast) are needed to accommodate the old version,
|
||||
a comment should indicate that this is for versions up to the current release,
|
||||
to be eventually cleaned out when the crate bumps its minimum supported RIOT version.
|
||||
|
||||
* Update the examples to use the new versions in a separate PR that just does
|
||||
`cargo update` in the examples, and consequentally contains only Cargo.lock changes.
|
||||
|
||||
Such a PR should be easy to get ACKed, as "CI says GO" is usually suffient for them.
|
||||
|
||||
* Rebase the breaking PR on the one updating Cargo.lock.
|
||||
|
||||
If old and new version can *not* be supported,
|
||||
the affected crate should get a major release --
|
||||
but it's also grounds to revisit whether the change is actually as small as originally thought.
|
||||
Then, the the Cargo.lock file can be updated in the very PR that introduces the breaking change.
|
||||
|
||||
When,
|
||||
for the above reasons or others,
|
||||
a major release of creates is done,
|
||||
their new versions should have a `.0-alpha.0` or similar release.
|
||||
and a full release of the crate is done after the changing PR is merged.
|
||||
|
||||
Common failure modes are:
|
||||
|
||||
* Primitive types changed.
|
||||
|
||||
This can trigger a change in Rust numeric internals,
|
||||
which should then just take the larger of the different versions' types.
|
||||
|
||||
If the type is publicly visible, it is usually already `usize` or a similar maximal type;
|
||||
if not, and if using the new RIOT version means that the crate's API needs to change,
|
||||
the crate needs to undergo a breaking release.
|
||||
|
||||
* Functions are moved between static inline and linked.
|
||||
|
||||
For the functions themselves, this is caught automatically because `riot-sys`
|
||||
presents a unified view on symbols produced from linked and static functions.
|
||||
If any such function takes a struct (even through a pointer), that struct's type changes.
|
||||
The `inline_cast` function can be used to transmute the pointers with some safety checks.
|
||||
|
||||
* Atomics are introduced.
|
||||
|
||||
The C2Rust transpiler that allows using all the non-linked parts of the RIOT API
|
||||
is incapable of handling atomics;
|
||||
a workaround in `riot-sys`'s `riot-c2rust.h` file contains a list of headers
|
||||
that do depend on `stdatomic.h` but don't really expose anything of that as their API;
|
||||
they get included with a crude fake of atomics.
|
||||
|
||||
If the addition of a board makes this test fail on that board only,
|
||||
it is likely because that board's `board.h` pulls in stdatomic through one of the not yet cleared headers.
|
||||
That file needs to be vetted for visible use of atomics,
|
||||
and added to that list.
|
||||
|
||||
<!-- The alternative strategy, of course,
|
||||
is to @-mention known Rust users in the issue
|
||||
and ask them to update the Rust side... -->
|
15
tests/rust_minimal/src/lib.rs
Normal file
15
tests/rust_minimal/src/lib.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2020 Christian Amsüss
|
||||
//
|
||||
// This file is subject to the terms and conditions of the GNU Lesser
|
||||
// General Public License v2.1. See the file LICENSE in the top level
|
||||
// directory for more details.
|
||||
#![no_std]
|
||||
|
||||
use riot_wrappers::riot_main;
|
||||
use riot_wrappers::println;
|
||||
|
||||
riot_main!(main);
|
||||
|
||||
fn main() {
|
||||
println!("SUCCESS");
|
||||
}
|
20
tests/rust_minimal/tests/01-run.py
Executable file
20
tests/rust_minimal/tests/01-run.py
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2021 Freie Universität Berlin
|
||||
# 2021 Inria
|
||||
# 2021 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect_exact('SUCCESS')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
Loading…
Reference in New Issue
Block a user