Skip to content

Commit b38c46b

Browse files
committed
Use C2Rust to convert static inlines
Depends on immunant/c2rust#291. Is exploratory work for rust-lang/rust-bindgen#1344.
1 parent 8010fbb commit b38c46b

File tree

5 files changed

+117
-3
lines changed

5 files changed

+117
-3
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ license = "LGPL-2.1"
1515
[build-dependencies]
1616
bindgen = "0.53.1"
1717
shlex = "0.1.1"
18+
serde_json = "1"

build.rs

+74-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use bindgen::builder;
55
use std::env;
66
use std::path::PathBuf;
77

8+
use serde_json::json;
9+
810
fn main() {
911
let cflags = env::var("RIOT_CFLAGS")
1012
.expect("Please pass in RIOT_CFLAGS; see README.md for details.");
@@ -14,7 +16,7 @@ fn main() {
1416
println!("cargo:rerun-if-env-changed=RIOT_CFLAGS");
1517
println!("cargo:rerun-if-changed=riot-all.h");
1618

17-
let cflags = cflags.iter().filter(|x| {
19+
let cflags: Vec<String> = cflags.into_iter().filter(|x| {
1820
match x.as_ref() {
1921
// non-clang flags showing up with arm cortex m3 (eg. stk3700 board)
2022
"-Werror" => false,
@@ -30,12 +32,12 @@ fn main() {
3032
// accept all others
3133
_ => true,
3234
}
33-
});
35+
}).collect();
3436

3537
let bindings = builder()
3638
.header("riot-all.h")
3739
.size_t_is_usize(true)
38-
.clang_args(cflags)
40+
.clang_args(&cflags)
3941
.use_core()
4042
.ctypes_prefix("libc")
4143
.impl_debug(true)
@@ -48,4 +50,73 @@ fn main() {
4850
bindings
4951
.write_to_file(out_path.join("bindings.rs"))
5052
.expect("Couldn't write bindings!");
53+
54+
// Build a compile_commands.json, and run C2Rust
55+
//
56+
// The output is cleared beforehand (for c2rust no-ops when an output file is present), and the
57+
// input is copied to OUT_DIR as that's the easiest way to get c2rust to put the output file in
58+
// a different place.
59+
60+
let headercopy = out_path.join("riot-c2rust.h");
61+
let output = out_path.join("riot_c2rust.rs");
62+
println!("cargo:rerun-if-changed=riot-c2rust.h");
63+
std::fs::copy("riot-c2rust.h", headercopy)
64+
.expect("Failed to copy over header file");
65+
match std::fs::remove_file(&output) {
66+
Ok(_) => (),
67+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => (),
68+
Err(e) => panic!("Failed to remove output file: {}", e),
69+
}
70+
71+
let arguments: Vec<_> = core::iter::once("any-cc".to_string())
72+
.chain(cflags.into_iter())
73+
.chain(core::iter::once("riot-c2rust.h".to_string()))
74+
.collect();
75+
let compile_commands = json!([{
76+
"arguments": arguments,
77+
"directory": out_path,
78+
"file": "riot-c2rust.h",
79+
}]);
80+
let compile_commands_name = out_path.join("compile_commands.json");
81+
82+
let mut compile_commands_file = std::fs::File::create(compile_commands_name.clone())
83+
.expect("Failed to create compile_commands.json");
84+
serde_json::to_writer_pretty(&mut compile_commands_file, &compile_commands)
85+
.expect("Failed to write to compile_commands.json");
86+
compile_commands_file.sync_all()
87+
.expect("Failed to write to compile_commands.json");
88+
89+
let compile_commands_name = compile_commands_name.to_str().expect("Inexpressible path name");
90+
// FIXME: This does not rat on the used files. Most are probably included from riot-all.h
91+
// anyway, tough.
92+
println!("Running C2Rust on {}", compile_commands_name);
93+
let status = std::process::Command::new("c2rust")
94+
.args(&["transpile", compile_commands_name, "--preserve-unused-functions", "--emit-modules", "--emit-no-std"])
95+
.status()
96+
.expect("C2Rust failed");
97+
if !status.success() {
98+
println!("cargo:warning=C2Rust failed with error code {}, exiting", status);
99+
std::process::exit(status.code().unwrap_or(1));
100+
}
101+
102+
// Some fix-ups to the C2Rust output
103+
// (could just as well call sed...)
104+
105+
use std::io::{Read, Write};
106+
107+
let mut rustcode = String::new();
108+
std::fs::File::open(output)
109+
.expect("Failed to open riot_c2rust.rs")
110+
.read_to_string(&mut rustcode)
111+
.expect("Failed to read from riot_c2rust.rs");
112+
113+
rustcode = rustcode.replace("use ::libc;\n", "");
114+
rustcode = rustcode.replace(r#"unsafe extern "C" fn "#, r#"pub unsafe extern "C" fn "#);
115+
116+
let output_replaced = out_path.join("riot_c2rust_replaced.rs");
117+
std::fs::File::create(output_replaced)
118+
.expect("Failed to create riot_c2rust_replaced.rs")
119+
.write(rustcode.as_bytes())
120+
.expect("Failed to write to riot_c2rust_replaced.rs");
121+
51122
}

riot-c2rust.h

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <shell.h>
2+
#include <thread.h>
3+
#include <irq.h>
4+
#include <stdio_base.h>
5+
#include <periph/adc.h>
6+
#include <periph/gpio.h>
7+
#include <periph/i2c.h>
8+
// #include <net/gnrc.h>
9+
// #include <net/gnrc/udp.h>
10+
#include <net/gnrc/pktbuf.h>
11+
// #include <net/gnrc/ipv6.h>
12+
#include <net/gnrc/nettype.h>
13+
#include <net/gnrc/netapi.h>
14+
15+
#include <saul.h>
16+
#include <saul_reg.h>
17+
18+
#include <board.h>
19+
// #include <xtimer.h>
20+
21+
// not in riot-all?
22+
#include <mutex.h>

src/inline.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// Contains header code converted to Rust by C2Rust
2+
///
3+
/// Types in here are distinct from those created in the main module (using bindgen); unifying
4+
/// those will be part of [bindgen's #1334], but it's a long way there.
5+
///
6+
/// [bindgen's #1334]: https://github.com/rust-lang/rust-bindgen/issues/1344
7+
///
8+
/// Use these functions through the re-export in the main module, for the C headers may flip-flop
9+
/// between static inline and linked.
10+
11+
use crate::libc;
12+
13+
include!(concat!(env!("OUT_DIR"), "/riot_c2rust_replaced.rs"));

src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,11 @@
55

66
pub mod libc;
77

8+
pub mod inline;
9+
pub use inline::*;
10+
11+
// This is not moved into a dedicated linked module that'd be reexported in analogy to the inline,
12+
// for that'd need explicit `pub use linked::mutex_t` etc for every type that's present in both and
13+
// thus not imported for either. As long as this is inlined here, linked types (which are
14+
// predominantly used so far) take precedence automatically.
815
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

0 commit comments

Comments
 (0)