use data::*;
use types;
use std::{env, path};
use std::collections::HashMap;
use std::io::prelude::*;
use std::fs::File;
use std::ffi::OsString;
use syntax::ast::Expr;
use syntax::ast::Expr_::*;
use syntax::ptr::P;
use rustc::lint::*;
use rustc::session::search_paths::SearchPaths;
use gcc;
fn super_hack_get_out_dir() -> OsString {
let mut out_dir = None;
let mut found_out_dir = false;
for a in env::args() {
if a == "--out-dir" {
found_out_dir = true;
continue;
}
if found_out_dir {
out_dir = Some(OsString::from(format!("{}", a)));
break;
}
}
out_dir.unwrap_or_else(|| {
env::current_dir().unwrap().into_os_string()
})
}
pub struct CppLintPass;
impl LintPass for CppLintPass {
fn get_lints(&self) -> LintArray {
lint_array!(types::BAD_CXX_TYPE)
}
fn check_expr(&mut self, cx: &Context, exp: &Expr) {
if let ExprCall(ref callee, ref args) = exp.node {
if let ExprPath(None, ref path) = callee.node {
if path.segments.len() == 1 {
let name = path.segments[0].identifier.name.as_str();
record_type_data(cx, name, exp, args);
}
}
}
}
}
fn record_type_data(cx: &Context, name: &str, call: &Expr, args: &[P<Expr>]) {
let mut headers = CPP_HEADERS.lock().unwrap();
let mut decls = CPP_FNDECLS.lock().unwrap();
let mut types = CPP_TYPEDATA.lock().unwrap();
if let Some(cppfn) = decls.get_mut(name) {
cppfn.ret_ty = Some(types::cpp_type_of(&mut types, cx.tcx, call, false)
.with_note(format!("Used in the return value of this cpp! block"),
Some(cppfn.span))
.into_name(cx));
for (i, arg) in args.iter().enumerate() {
if let ExprCast(ref e, _) = arg.node {
if let ExprCast(ref e, _) = e.node {
if let ExprAddrOf(_, ref e) = e.node {
let mut tn = types::cpp_type_of(&mut types, cx.tcx, e, true)
.with_note(format!("Used in the argument `{}` of this cpp! block",
cppfn.arg_idents[i].name), Some(cppfn.span));
tn.recover(); cppfn.arg_idents[i].ty = Some(tn.into_name(cx));
continue
}
}
}
panic!("Expected a double-casted reference as an argument.")
}
} else { return }
if decls.values().all(|x| x.ret_ty.is_some()) {
cx.sess().abort_if_errors();
finalize(cx, &mut headers, &mut types, &mut decls);
}
}
fn finalize(cx: &Context,
headers: &mut String,
types: &mut types::TypeData,
decls: &mut HashMap<String, CppFn>) {
let fndecls = decls.values().fold(String::new(), |acc, new| {
format!("{}\n{}\n", acc, new.to_string(cx.sess()))
});
let cppcode = format!(r#"
/******************************
* Code Generated by Rust-C++ *
******************************/
/* cstdint includes sane type definitions for integer types */
#include <cstdint>
/* the rs:: namespace contains rust-defined types */
namespace rs {{
/* A slice from rust code */
/* Can be used to interact with, pass around, and return Rust slices */
template<class T>
struct Slice {{
const T* data;
uintptr_t len;
}};
/* A string slice is simply a slice of utf-8 encoded characters */
typedef Slice<uint8_t> StrSlice;
/* A trait object is composed of a data pointer and a vtable */
struct TraitObject {{
void* data;
void* vtable;
}};
/* A dummy struct which is generated when incompatible types are closed-over */
struct __Dummy;
/* Typedefs for integral and floating point types */
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef {} usize;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef {} isize;
typedef float f32;
static_assert(sizeof(f32) == 4, "C++ `float` isn't 32 bits wide");
typedef double f64;
static_assert(sizeof(f64) == 8, "C++ `double` isn't 64 bits wide");
/* We use this bool type to ensure that our bools are 1 byte wide */
typedef i8 bool_;
}}
/* User-generated Headers */
{}
/* Generated types */
namespace rs {{
{}
}} // namespace rs
/* User-generated function declarations */
extern "C" {{{}}}
"#, cx.sess().target.uint_type, cx.sess().target.int_type, *headers, types.to_cpp(cx), fndecls);
let out_dir = super_hack_get_out_dir();
{
let path = path::Path::new(&out_dir).join("rust_cpp_tmp.cpp");
let mut f = File::create(path).unwrap();
f.write_all(cppcode.as_bytes()).unwrap();
}
unsafe {
let sp = &cx.sess().opts.search_paths;
let sp_mut: &mut SearchPaths = &mut *(sp as *const _ as *mut _);
sp_mut.add_path(&out_dir.to_str().unwrap());
}
env::set_var("TARGET", &cx.sess().target.target.llvm_target);
env::set_var("HOST", &cx.sess().host.llvm_target);
env::set_var("OPT_LEVEL", format!("{}", cx.sess().opts.cg.opt_level.unwrap_or(0)));
env::set_var("CARGO_MANIFEST_DIR", &out_dir);
env::set_var("OUT_DIR", &out_dir);
env::set_var("PROFILE", "");
env::set_var("CXXFLAGS", format!("{} {}", env::var("CXXFLAGS").unwrap_or(String::new()),
"-std=c++11"));
println!("########### Running GCC ###########");
gcc::Config::new()
.cpp(true)
.file("rust_cpp_tmp.cpp")
.compile("librust_cpp_tmp.a");
println!("########### Done Rust-C++ ############");
}