Skip to content

Commit

Permalink
Use C2Rust to convert static inlines
Browse files Browse the repository at this point in the history
Depends on immunant/c2rust#291. Is exploratory
work for rust-lang/rust-bindgen#1344.
  • Loading branch information
chrysn committed Sep 3, 2020
1 parent 8010fbb commit b38c46b
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ license = "LGPL-2.1"
[build-dependencies]
bindgen = "0.53.1"
shlex = "0.1.1"
serde_json = "1"
77 changes: 74 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use bindgen::builder;
use std::env;
use std::path::PathBuf;

use serde_json::json;

fn main() {
let cflags = env::var("RIOT_CFLAGS")
.expect("Please pass in RIOT_CFLAGS; see README.md for details.");
Expand All @@ -14,7 +16,7 @@ fn main() {
println!("cargo:rerun-if-env-changed=RIOT_CFLAGS");
println!("cargo:rerun-if-changed=riot-all.h");

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

let bindings = builder()
.header("riot-all.h")
.size_t_is_usize(true)
.clang_args(cflags)
.clang_args(&cflags)
.use_core()
.ctypes_prefix("libc")
.impl_debug(true)
Expand All @@ -48,4 +50,73 @@ fn main() {
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");

// Build a compile_commands.json, and run C2Rust
//
// The output is cleared beforehand (for c2rust no-ops when an output file is present), and the
// input is copied to OUT_DIR as that's the easiest way to get c2rust to put the output file in
// a different place.

let headercopy = out_path.join("riot-c2rust.h");
let output = out_path.join("riot_c2rust.rs");
println!("cargo:rerun-if-changed=riot-c2rust.h");
std::fs::copy("riot-c2rust.h", headercopy)
.expect("Failed to copy over header file");
match std::fs::remove_file(&output) {
Ok(_) => (),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => (),
Err(e) => panic!("Failed to remove output file: {}", e),
}

let arguments: Vec<_> = core::iter::once("any-cc".to_string())
.chain(cflags.into_iter())
.chain(core::iter::once("riot-c2rust.h".to_string()))
.collect();
let compile_commands = json!([{
"arguments": arguments,
"directory": out_path,
"file": "riot-c2rust.h",
}]);
let compile_commands_name = out_path.join("compile_commands.json");

let mut compile_commands_file = std::fs::File::create(compile_commands_name.clone())
.expect("Failed to create compile_commands.json");
serde_json::to_writer_pretty(&mut compile_commands_file, &compile_commands)
.expect("Failed to write to compile_commands.json");
compile_commands_file.sync_all()
.expect("Failed to write to compile_commands.json");

let compile_commands_name = compile_commands_name.to_str().expect("Inexpressible path name");
// FIXME: This does not rat on the used files. Most are probably included from riot-all.h
// anyway, tough.
println!("Running C2Rust on {}", compile_commands_name);
let status = std::process::Command::new("c2rust")
.args(&["transpile", compile_commands_name, "--preserve-unused-functions", "--emit-modules", "--emit-no-std"])
.status()
.expect("C2Rust failed");
if !status.success() {
println!("cargo:warning=C2Rust failed with error code {}, exiting", status);
std::process::exit(status.code().unwrap_or(1));
}

// Some fix-ups to the C2Rust output
// (could just as well call sed...)

use std::io::{Read, Write};

let mut rustcode = String::new();
std::fs::File::open(output)
.expect("Failed to open riot_c2rust.rs")
.read_to_string(&mut rustcode)
.expect("Failed to read from riot_c2rust.rs");

rustcode = rustcode.replace("use ::libc;\n", "");
rustcode = rustcode.replace(r#"unsafe extern "C" fn "#, r#"pub unsafe extern "C" fn "#);

let output_replaced = out_path.join("riot_c2rust_replaced.rs");
std::fs::File::create(output_replaced)
.expect("Failed to create riot_c2rust_replaced.rs")
.write(rustcode.as_bytes())
.expect("Failed to write to riot_c2rust_replaced.rs");

}
22 changes: 22 additions & 0 deletions riot-c2rust.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <shell.h>
#include <thread.h>
#include <irq.h>
#include <stdio_base.h>
#include <periph/adc.h>
#include <periph/gpio.h>
#include <periph/i2c.h>
// #include <net/gnrc.h>
// #include <net/gnrc/udp.h>
#include <net/gnrc/pktbuf.h>
// #include <net/gnrc/ipv6.h>
#include <net/gnrc/nettype.h>
#include <net/gnrc/netapi.h>

#include <saul.h>
#include <saul_reg.h>

#include <board.h>
// #include <xtimer.h>

// not in riot-all?
#include <mutex.h>
13 changes: 13 additions & 0 deletions src/inline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// Contains header code converted to Rust by C2Rust
///
/// Types in here are distinct from those created in the main module (using bindgen); unifying
/// those will be part of [bindgen's #1334], but it's a long way there.
///
/// [bindgen's #1334]: https://github.com/rust-lang/rust-bindgen/issues/1344
///
/// Use these functions through the re-export in the main module, for the C headers may flip-flop
/// between static inline and linked.
use crate::libc;

include!(concat!(env!("OUT_DIR"), "/riot_c2rust_replaced.rs"));
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@

pub mod libc;

pub mod inline;
pub use inline::*;

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

0 comments on commit b38c46b

Please sign in to comment.