extern crate cpp_common;
extern crate cpp_synom as synom;
extern crate cpp_syn as syn;
extern crate cpp_synmap;
extern crate gcc;
#[macro_use]
extern crate lazy_static;
use std::env;
use std::path::{Path, PathBuf};
use std::fs::{remove_dir_all, create_dir, File};
use std::io::prelude::*;
use syn::visit::Visitor;
use syn::{Mac, Span, Spanned, DUMMY_SPAN};
use cpp_common::{parsing, Closure, ClosureSig, Capture, Macro, LIB_NAME, STRUCT_METADATA_MAGIC,
VERSION};
use cpp_synmap::SourceMap;
fn warnln_impl(a: String) {
for s in a.lines() {
println!("cargo:warning={}", s);
}
}
macro_rules! warnln {
($($all:tt)*) => {
$crate::warnln_impl(format!($($all)*));
}
}
const INTERNAL_CPP_STRUCTS: &'static str = r#"
/* THIS FILE IS GENERATED BY rust-cpp. DO NOT EDIT */
#include "stdint.h"
"#;
lazy_static! {
static ref OUT_DIR: PathBuf =
PathBuf::from(env::var("OUT_DIR").expect(r#"
-- rust-cpp fatal error --
The OUT_DIR environment variable was not set.
NOTE: rust-cpp's build function must be run in a build script."#));
static ref CPP_DIR: PathBuf = OUT_DIR.join("rust_cpp");
}
fn gen_cpp_lib(visitor: &Handle) -> PathBuf {
let result_path = CPP_DIR.join("cpp_closures.cpp");
let mut output = File::create(&result_path).expect("Unable to generate temporary C++ file");
write!(output, "{}", INTERNAL_CPP_STRUCTS).unwrap();
write!(output, "{}\n\n", &visitor.snippets).unwrap();
let mut sizealign = vec![];
for &Closure { ref body, ref sig } in &visitor.closures {
let &ClosureSig {
ref captures,
ref cpp,
..
} = sig;
let hash = sig.name_hash();
let name = sig.extern_name();
if cpp != "void" {
sizealign.push(format!("{{
{hash}ull,
sizeof({type}),
rustcpp::AlignOf<{type}>::value
}}", hash=hash, type=cpp));
} else {
sizealign.push(format!("{{
{hash}ull,
0,
1
}}",
hash = hash));
}
for &Capture { ref cpp, .. } in captures {
sizealign.push(format!("{{
{hash}ull,
sizeof({type}),
rustcpp::AlignOf<{type}>::value
}}", hash=hash, type=cpp));
}
let params = captures
.iter()
.map(|&Capture {
mutable,
ref name,
ref cpp,
}| if mutable {
format!("{} & {}", cpp, name)
} else {
format!("{} const& {}", cpp, name)
})
.collect::<Vec<_>>()
.join(", ");
write!(output,
r#"
extern "C" {{
{} {}({}) {{
{}
}}
}}
"#,
cpp,
&name,
params,
body.node)
.unwrap();
}
let mut magic = vec![];
for mag in STRUCT_METADATA_MAGIC.iter() {
magic.push(format!("{}", mag));
}
write!(output,
r#"
namespace rustcpp {{
template<typename T>
struct AlignOf {{
struct Inner {{
char a;
T b;
}};
static const uintptr_t value = sizeof(Inner) - sizeof(T);
}};
struct SizeAlign {{
uint64_t hash;
uint64_t size;
uint64_t align;
}};
struct MetaData {{
uint8_t magic[128];
uint8_t version[16];
uint64_t length;
SizeAlign data[{length}];
}};
MetaData metadata = {{
{{ {magic} }},
"{version}",
{length},
{{ {data} }}
}};
}} // namespace rustcpp
"#,
data = sizealign.join(", "),
length = sizealign.len(),
magic = magic.join(", "),
version = VERSION)
.unwrap();
result_path
}
fn clean_artifacts() {
if CPP_DIR.is_dir() {
remove_dir_all(&*CPP_DIR).expect(r#"
-- rust-cpp fatal error --
Failed to remove existing build artifacts from output directory."#);
}
create_dir(&*CPP_DIR).expect(r#"
-- rust-cpp fatal error --
Failed to create output object directory."#);
}
pub struct Config {
gcc: gcc::Config,
}
impl Config {
pub fn new() -> Config {
let mut gcc = gcc::Config::new();
gcc.cpp(true);
Config { gcc: gcc }
}
pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
self.gcc.include(dir);
self
}
pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Self {
self.gcc.define(var, val);
self
}
pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Self {
self.gcc.object(obj);
self
}
pub fn flag(&mut self, flag: &str) -> &mut Self {
self.gcc.flag(flag);
self
}
pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Self {
self.gcc.file(p);
self
}
pub fn cpp_link_stdlib(&mut self, cpp_link_stdlib: Option<&str>) -> &mut Self {
self.gcc.cpp_link_stdlib(cpp_link_stdlib);
self
}
pub fn cpp_set_stdlib(&mut self, cpp_set_stdlib: Option<&str>) -> &mut Self {
self.gcc.cpp_set_stdlib(cpp_set_stdlib);
self
}
pub fn host(&mut self, host: &str) -> &mut Self {
self.gcc.host(host);
self
}
pub fn opt_level(&mut self, opt_level: u32) -> &mut Self {
self.gcc.opt_level(opt_level);
self
}
pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Self {
self.gcc.opt_level_str(opt_level);
self
}
pub fn debug(&mut self, debug: bool) -> &mut Self {
self.gcc.debug(debug);
self
}
pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Self {
self.gcc.compiler(compiler);
self
}
pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Self {
self.gcc.archiver(archiver);
self
}
pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Self {
self.gcc.cargo_metadata(cargo_metadata);
self
}
pub fn pic(&mut self, pic: bool) -> &mut Self {
self.gcc.pic(pic);
self
}
pub fn build<P: AsRef<Path>>(&mut self, crate_root: P) {
assert_eq!(env!("CARGO_PKG_VERSION"),
VERSION,
"Internal Error: mismatched cpp_common and cpp_build versions");
clean_artifacts();
let mut sm = SourceMap::new();
let krate = match sm.add_crate_root(crate_root) {
Ok(krate) => krate,
Err(err) => {
warnln!(r#"-- rust-cpp parse error --
There was an error parsing the crate for the rust-cpp build script:
{}
In order to provide a better error message, the build script will exit
successfully, such that rustc can provide an error message."#,
err);
return;
}
};
let mut visitor = Handle {
closures: Vec::new(),
snippets: String::new(),
sm: &sm,
};
visitor.visit_crate(&krate);
let filename = gen_cpp_lib(&visitor);
self.gcc.file(filename).compile(LIB_NAME);
}
}
pub fn build<P: AsRef<Path>>(path: P) {
Config::new().build(path)
}
struct Handle<'a> {
closures: Vec<Closure>,
snippets: String,
sm: &'a SourceMap,
}
fn extract_with_span(mut spanned: &mut Spanned<String>, src: &str, offset: usize, sm: &SourceMap) {
if spanned.span != DUMMY_SPAN {
let src_slice = &src[spanned.span.lo..spanned.span.hi];
spanned.span.lo += offset;
spanned.span.hi += offset;
let loc = sm.locinfo(spanned.span).unwrap();
spanned.node = format!("#line {} {:?}\n", loc.line, loc.path);
for _ in 0..loc.col {
spanned.node.push(' ');
}
spanned.node.push_str(src_slice);
}
}
impl<'a> Visitor for Handle<'a> {
fn visit_mac(&mut self, mac: &Mac) {
if mac.path.segments.len() != 1 {
return;
}
if mac.path.segments[0].ident.as_ref() == "cpp" {
let tts = &mac.tts;
assert!(tts.len() >= 1);
let span = Span {
lo: tts[0].span().lo,
hi: tts[tts.len() - 1].span().hi,
};
let src = self.sm.source_text(span).unwrap();
let input = synom::ParseState::new(&src);
match parsing::build_macro(input).expect("cpp! macro") {
Macro::Closure(mut c) => {
extract_with_span(&mut c.body, &src, span.lo, self.sm);
self.closures.push(c);
}
Macro::Lit(mut l) => {
extract_with_span(&mut l, &src, span.lo, self.sm);
self.snippets.push('\n');
self.snippets.push_str(&l.node);
}
}
}
}
}