extern crate v8_api;
extern crate bindgen;
extern crate clang;
extern crate gcc;
use std::env;
use std::fmt;
use std::fs;
use std::io;
use std::path;
const LIBS: [&'static str; 6] = ["v8_base",
"v8_libbase",
"v8_libsampler",
"v8_nosnapshot",
"icui18n",
"icuuc"];
trait DisplayAsC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
struct C<'a, A>(&'a A) where A: 'a;
impl<'a, A> fmt::Display for C<'a, A> where A: DisplayAsC + 'a {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
DisplayAsC::fmt(self.0, f)
}
}
fn main() {
let out_dir_str = env::var_os("OUT_DIR").unwrap();
let out_dir_path = path::Path::new(&out_dir_str);
println!("cargo:rerun-if-changed=src/v8-trampoline.h");
println!("cargo:rerun-if-changed=src/v8-glue.h");
println!("cargo:rerun-if-changed=src/v8-glue.cc");
let api = read_api();
link_v8();
let header_path = out_dir_path.join("v8-glue-generated.h");
write_header(&api, &mut fs::File::create(&header_path).unwrap()).unwrap();
let cc_file_path = out_dir_path.join("v8-glue-generated.cc");
write_cc_file(&api, &mut fs::File::create(&cc_file_path).unwrap()).unwrap();
build_glue(out_dir_path);
let ffi_path = out_dir_path.join("ffi.rs");
gen_bindings(out_dir_path, &ffi_path);
}
fn read_api() -> v8_api::Api {
let extra_includes = if let Some(dir_str) = env::var_os("V8_SOURCE") {
vec![path::PathBuf::from(dir_str).join("include")]
} else {
vec![]
};
let trampoline_path = path::Path::new("src/v8-trampoline.h");
v8_api::read(trampoline_path, &extra_includes)
}
fn link_v8() {
if let Some(dir_str) = env::var_os("V8_BUILD") {
println!("V8_BUILD={:?}", dir_str);
let dir = path::Path::new(&dir_str);
maybe_search(dir);
// make+gyp-based build tree
maybe_search(dir.join("lib"));
maybe_search(dir.join("obj.target/src"));
maybe_search(dir.join("obj.target/third_party/icu"));
// ninja+gyp-based build tree
maybe_search(dir.join("lib"));
maybe_search(dir.join("obj/src"));
maybe_search(dir.join("obj/third_party/icu"));
// TODO: for GN-based builds it doesn't seem like the build
// produces static archives; maybe run ar here?
} else {
println!("V8_BUILD not set, searching system paths");
maybe_search("/usr/lib");
maybe_search("/usr/local/lib");
// TODO: hack: lazy way to fix the Travis build
maybe_search("/usr/lib/x86_64-linux-gnu");
maybe_search("/usr/local/lib/x86_64-linux-gnu");
maybe_search("/usr/lib/v8");
maybe_search("/usr/local/lib/v8");
}
if cfg!(feature = "shared") {
println!("cargo:rustc-link-lib=dylib=v8");
println!("cargo:rustc-link-lib=dylib=icui18n");
println!("cargo:rustc-link-lib=dylib=icuuc");
} else {
for lib in LIBS.iter() {
println!("cargo:rustc-link-lib=static={}", lib);
}
if fs::metadata("/usr/lib/x86_64-linux-gnu/libicudata.a").map(|m| m.is_file()).unwrap_or(false) {
println!("cargo:rustc-link-lib=static=icudata");
}
}
}
fn maybe_search
(dir: P) where P: AsRef {
let dir = dir.as_ref();
if fs::metadata(dir).map(|m| m.is_dir()).unwrap_or(false) {
println!("cargo:rustc-link-search=native={}", dir.to_string_lossy());
}
}
fn gen_bindings(out_dir_path: &path::Path, bindings_path: &path::Path) {
use std::io::Write;
let mut bindings = bindgen::Builder::new("src/v8-glue.h");
bindings.remove_prefix("v8_");
bindings.clang_arg("-Isrc");
bindings.clang_arg(format!("-I{}", out_dir_path.to_string_lossy()));
if let Some(dir_str) = env::var_os("V8_SOURCE") {
println!("V8_SOURCE={:?}", dir_str);
let dir = path::Path::new(&dir_str);
bindings.clang_arg(format!("-I{}", dir.join("include").to_string_lossy()));
} else {
println!("V8_SOURCE not set, searching system paths");
}
let generated_bindings = bindings.generate().unwrap();
let mut bindings_file = fs::File::create(bindings_path).unwrap();
writeln!(bindings_file, "mod ffi {{").unwrap();
generated_bindings.write(Box::new(&mut bindings_file)).unwrap();
writeln!(bindings_file, "}}").unwrap();
}
fn build_glue(out_dir_path: &path::Path) {
let mut config = gcc::Config::new();
if let Some(dir_str) = env::var_os("V8_SOURCE") {
let dir = path::Path::new(&dir_str);
config.include(dir.join("include"));
}
config.include("src");
config.include(out_dir_path);
config.cpp(true);
config.flag("-std=c++11");
config.flag("-Wall");
config.file("src/v8-glue.cc");
config.compile("libv8sysglue.a");
}
fn write_header(api: &v8_api::Api, mut out: W) -> io::Result<()>
where W: io::Write
{
try!(writeln!(out, "#pragma once"));
for class in api.classes.iter() {
try!(writeln!(out, ""));
try!(writeln!(out, "#if defined __cplusplus"));
try!(writeln!(out,
"typedef v8::{class} *{class}Ptr;",
class = class.name));
try!(writeln!(out,
"typedef v8::Persistent *{class}Ref;",
class = class.name));
try!(writeln!(out, "#else"));
try!(writeln!(out, "typedef struct _{class} *{class}Ptr;", class = class.name));
try!(writeln!(out, "typedef struct _{class}Ref *{class}Ref;", class = class.name));
try!(writeln!(out, "#endif /* defined __cplusplus */"));
}
for class in api.classes.iter() {
try!(writeln!(out, ""));
for method in class.methods.iter() {
try!(write!(out,
"{retty} v8_{class}_{method}(RustContext c",
retty = C(&method.ret_type),
class = class.name,
method = method.mangled_name));
if !method.is_static {
try!(write!(out, ", {class}Ref self", class = class.name));
}
for arg in method.args.iter() {
try!(write!(out, ", {arg}", arg = C(arg)));
}
try!(writeln!(out, ");"));
}
try!(writeln!(out,
"void v8_{class}_DestroyRef({class}Ref self);",
class = class.name));
try!(writeln!(out,
"void v8_{class}_DestroyPtr({class}Ptr self);",
class = class.name));
}
Ok(())
}
fn write_cc_file(api: &v8_api::Api, mut out: W) -> io::Result<()>
where W: io::Write
{
for class in api.classes.iter() {
for method in class.methods.iter() {
try!(writeln!(out, ""));
try!(write!(out,
"{retty} v8_{class}_{method}(RustContext c",
retty = C(&method.ret_type),
class = class.name,
method = method.mangled_name));
if !method.is_static {
try!(write!(out, ", {class}Ref self", class = class.name));
}
for arg in method.args.iter() {
try!(write!(out, ", {arg}", arg = C(arg)));
}
try!(writeln!(out, ") {{"));
try!(writeln!(out, " v8::HandleScope __handle_scope(c.isolate);"));
try!(writeln!(out, " v8::TryCatch __try_catch(c.isolate);"));
if let Some(arg) = method.args.iter().find(|ref a| a.arg_type == v8_api::Type::Ptr(Box::new(v8_api::Type::Class("Context".to_owned())))) {
// There should only be one context but who knows
try!(writeln!(out, " v8::Context::Scope {ctx}_scope(wrap(c.isolate, {ctx}));", ctx=arg.name));
}
if let v8_api::RetType::Direct(v8_api::Type::Void) = method.ret_type {
try!(write!(out, " "));
} else {
try!(write!(out, " auto result = "));
}
if method.is_static {
try!(write!(out,
"v8::{class}::{method}(",
class = class.name,
method = method.name));
} else {
try!(write!(out,
"self->Get(c.isolate)->{method}(",
method = method.name));
}
let mut needs_sep = false;
for arg in method.args.iter() {
if needs_sep {
try!(write!(out, ", "));
}
needs_sep = true;
try!(write!(out, "wrap(c.isolate, {arg})", arg = arg.name));
}
try!(writeln!(out, ");"));
if let v8_api::RetType::Direct(v8_api::Type::Void) = method.ret_type {
try!(writeln!(out, " handle_exception(c, __try_catch);"));
} else {
try!(writeln!(out, " handle_exception(c, __try_catch);"));
try!(writeln!(out, " return {unwrapper}(c.isolate, result);",
unwrapper = unwrapper(&method.ret_type)));
}
try!(writeln!(out, "}}"));
}
try!(writeln!(out, ""));
try!(writeln!(out,
"void v8_{class}_DestroyRef({class}Ref self) {{",
class = class.name));
try!(writeln!(out, " delete self;"));
try!(writeln!(out, "}}"));
try!(writeln!(out, ""));
try!(writeln!(out,
"void v8_{class}_DestroyPtr({class}Ptr self) {{",
class = class.name));
try!(writeln!(out, " delete self;"));
try!(writeln!(out, "}}"));
}
Ok(())
}
fn unwrapper(ret_type: &v8_api::RetType) -> &str {
use v8_api::*;
match *ret_type {
RetType::Maybe(Type::Bool) => "unwrap_maybe_bool",
RetType::Maybe(Type::Int) => "unwrap_maybe_int",
RetType::Maybe(Type::UInt) => "unwrap_maybe_uint",
RetType::Maybe(Type::Long) => "unwrap_maybe_long",
RetType::Maybe(Type::ULong) => "unwrap_maybe_ulong",
RetType::Maybe(Type::U32) => "unwrap_maybe_u32",
RetType::Maybe(Type::I32) => "unwrap_maybe_i32",
RetType::Maybe(Type::U64) => "unwrap_maybe_u64",
RetType::Maybe(Type::I64) => "unwrap_maybe_i64",
RetType::Maybe(Type::F64) => "unwrap_maybe_f64",
_ => "unwrap",
}
}
impl DisplayAsC for v8_api::Arg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", C(&self.arg_type), self.name)
}
}
impl DisplayAsC for v8_api::RetType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use v8_api::*;
match *self {
RetType::Direct(ref t) => DisplayAsC::fmt(t, f),
RetType::Maybe(Type::Bool) => write!(f, "MaybeBool"),
RetType::Maybe(Type::Int) => write!(f, "MaybeInt"),
RetType::Maybe(Type::UInt) => write!(f, "MaybeUInt"),
RetType::Maybe(Type::Long) => write!(f, "MaybeLong"),
RetType::Maybe(Type::ULong) => write!(f, "MaybeULong"),
RetType::Maybe(Type::F64) => write!(f, "MaybeF64"),
RetType::Maybe(Type::U64) => write!(f, "MaybeU64"),
RetType::Maybe(Type::I64) => write!(f, "MaybeI64"),
// TODO: potentially maybeify more types here
RetType::Maybe(ref t) => DisplayAsC::fmt(t, f),
}
}
}
impl DisplayAsC for v8_api::Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use v8_api::*;
match *self {
Type::Void => write!(f, "void"),
Type::Bool => write!(f, "bool"),
Type::Char => write!(f, "char"),
Type::ConstChar => write!(f, "const char"),
Type::UInt => write!(f, "unsigned int"),
Type::Int => write!(f, "int"),
Type::ULong => write!(f, "unsigned long"),
Type::Long => write!(f, "long"),
Type::U8 => write!(f, "uint8_t"),
Type::I8 => write!(f, "int8_t"),
Type::U16 => write!(f, "uint16_t"),
Type::I16 => write!(f, "int16_t"),
Type::U32 => write!(f, "uint32_t"),
Type::I32 => write!(f, "int32_t"),
Type::U64 => write!(f, "uint64_t"),
Type::I64 => write!(f, "int64_t"),
Type::F64 => write!(f, "double"),
Type::Class(ref name) => write!(f, "{}", name),
Type::Ref(ref target) => match **target {
Type::Class(ref name) => write!(f, "{}Ref", name),
ref t => write!(f, "&{}", C(t)),
},
Type::Ptr(ref target) => match **target {
Type::Class(ref name) => write!(f, "{}Ptr", name),
ref t => write!(f, "{} *", C(t)),
},
Type::Arr(ref target) => write!(f, "{}[]", C(&**target)),
}
}
}