mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-12-23 15:50:11 -05:00
Merge cargo_gn into this crate (#604)
This commit is contained in:
parent
64aa11e34d
commit
3d26ffbf17
3 changed files with 319 additions and 12 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -6,12 +6,6 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cargo_gn"
|
||||
version = "0.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ba7d7f7b201dfcbc314b14f2176c92f8ba521dab538b40e426ffed25ed7cd80"
|
||||
|
||||
[[package]]
|
||||
name = "fslock"
|
||||
version = "0.1.6"
|
||||
|
@ -69,7 +63,6 @@ name = "rusty_v8"
|
|||
version = "0.17.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cargo_gn",
|
||||
"fslock",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
|
|
@ -56,7 +56,6 @@ libc = "0.2.80"
|
|||
bitflags = "1.2.1"
|
||||
|
||||
[build-dependencies]
|
||||
cargo_gn = "0.0.15"
|
||||
which = "4.0.2"
|
||||
fslock = "0.1"
|
||||
|
||||
|
|
323
build.rs
323
build.rs
|
@ -1,11 +1,13 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use fslock::LockFile;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use which::which;
|
||||
|
||||
fn main() {
|
||||
|
@ -73,13 +75,13 @@ fn build_v8() {
|
|||
}
|
||||
|
||||
// On windows, rustc cannot link with a V8 debug build.
|
||||
let mut gn_args = if cargo_gn::is_debug() && !cfg!(target_os = "windows") {
|
||||
let mut gn_args = if is_debug() && !cfg!(target_os = "windows") {
|
||||
vec!["is_debug=true".to_string()]
|
||||
} else {
|
||||
vec!["is_debug=false".to_string()]
|
||||
};
|
||||
|
||||
if !cargo_gn::is_debug() {
|
||||
if !is_debug() {
|
||||
gn_args.push("v8_enable_handle_zapping=false".to_string());
|
||||
}
|
||||
|
||||
|
@ -127,10 +129,10 @@ fn build_v8() {
|
|||
|
||||
let gn_root = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
let gn_out = cargo_gn::maybe_gen(&gn_root, gn_args);
|
||||
let gn_out = maybe_gen(&gn_root, gn_args);
|
||||
assert!(gn_out.exists());
|
||||
assert!(gn_out.join("args.gn").exists());
|
||||
cargo_gn::build("rusty_v8", None);
|
||||
build("rusty_v8", None);
|
||||
}
|
||||
|
||||
fn maybe_install_sysroot(arch: &str) {
|
||||
|
@ -364,3 +366,316 @@ fn cc_wrapper(gn_args: &mut Vec<String>, sccache_path: &Path) {
|
|||
gn_args.push("treat_warnings_as_errors=false".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
struct Dirs {
|
||||
pub out: PathBuf,
|
||||
pub root: PathBuf,
|
||||
}
|
||||
|
||||
fn get_dirs(manifest_dir: Option<&str>) -> Dirs {
|
||||
// The OUT_DIR is going to be a crate-specific directory like
|
||||
// "target/debug/build/cargo_gn_example-eee5160084460b2c"
|
||||
// But we want to share the GN build amongst all crates
|
||||
// and return the path "target/debug". So to find it, we walk up three
|
||||
// directories.
|
||||
// TODO(ry) This is quite brittle - if Cargo changes the directory structure
|
||||
// this could break.
|
||||
let out = env::var("OUT_DIR").map(PathBuf::from).unwrap();
|
||||
let out = out
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
|
||||
let root = match manifest_dir {
|
||||
Some(s) => env::current_dir().unwrap().join(s),
|
||||
None => env::var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap(),
|
||||
};
|
||||
|
||||
let mut dirs = Dirs { out, root };
|
||||
maybe_symlink_root_dir(&mut dirs);
|
||||
dirs
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn maybe_symlink_root_dir(_: &mut Dirs) {}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn maybe_symlink_root_dir(dirs: &mut Dirs) {
|
||||
// GN produces invalid paths if the source (a.k.a. root) directory is on a
|
||||
// different drive than the output. If this is the case we'll create a
|
||||
// symlink called "gn_root' in the out directory, next to 'gn_out', so it
|
||||
// appears as if they're both on the same drive.
|
||||
use std::fs::remove_dir;
|
||||
use std::os::windows::fs::symlink_dir;
|
||||
|
||||
let get_prefix = |p: &Path| {
|
||||
p.components()
|
||||
.find_map(|c| match c {
|
||||
std::path::Component::Prefix(p) => Some(p),
|
||||
_ => None,
|
||||
})
|
||||
.map(|p| p.as_os_str().to_owned())
|
||||
};
|
||||
|
||||
let Dirs { out, root } = dirs;
|
||||
if get_prefix(out) != get_prefix(root) {
|
||||
let symlink = &*out.join("gn_root");
|
||||
let target = &*root.canonicalize().unwrap();
|
||||
|
||||
println!("Creating symlink {:?} to {:?}", &symlink, &root);
|
||||
|
||||
loop {
|
||||
match symlink.canonicalize() {
|
||||
Ok(existing) if existing == target => break,
|
||||
Ok(_) => remove_dir(symlink).expect("remove_dir failed"),
|
||||
Err(_) => {
|
||||
break symlink_dir(target, symlink).expect("symlink_dir failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirs.root = symlink.to_path_buf();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_debug() -> bool {
|
||||
// Cargo sets PROFILE to either "debug" or "release", which conveniently
|
||||
// matches the build modes we support.
|
||||
let m = env::var("PROFILE").unwrap();
|
||||
if m == "release" {
|
||||
false
|
||||
} else if m == "debug" {
|
||||
true
|
||||
} else {
|
||||
panic!("unhandled PROFILE value {}", m)
|
||||
}
|
||||
}
|
||||
|
||||
fn gn() -> String {
|
||||
env::var("GN").unwrap_or_else(|_| "gn".to_owned())
|
||||
}
|
||||
|
||||
type NinjaEnv = Vec<(String, String)>;
|
||||
|
||||
fn ninja(gn_out_dir: &PathBuf, maybe_env: Option<NinjaEnv>) -> Command {
|
||||
let cmd_string = env::var("NINJA").unwrap_or_else(|_| "ninja".to_owned());
|
||||
let mut cmd = Command::new(cmd_string);
|
||||
cmd.arg("-C");
|
||||
cmd.arg(&gn_out_dir);
|
||||
if let Some(env) = maybe_env {
|
||||
for item in env {
|
||||
cmd.env(item.0, item.1);
|
||||
}
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
pub type GnArgs = Vec<String>;
|
||||
|
||||
pub fn maybe_gen(manifest_dir: &str, gn_args: GnArgs) -> PathBuf {
|
||||
let dirs = get_dirs(Some(manifest_dir));
|
||||
let gn_out_dir = dirs.out.join("gn_out");
|
||||
|
||||
if !gn_out_dir.exists() || !gn_out_dir.join("build.ninja").exists() {
|
||||
let args = gn_args.join(" ");
|
||||
|
||||
let path = env::current_dir().unwrap();
|
||||
println!("The current directory is {}", path.display());
|
||||
println!(
|
||||
"gn gen --root={} {}",
|
||||
dirs.root.display(),
|
||||
gn_out_dir.display()
|
||||
);
|
||||
let mut cmd = Command::new(gn());
|
||||
cmd.arg(format!("--root={}", dirs.root.display()));
|
||||
cmd.arg("gen");
|
||||
cmd.arg(&gn_out_dir);
|
||||
cmd.arg("--args=".to_owned() + &args);
|
||||
cmd.stdout(Stdio::inherit());
|
||||
cmd.stderr(Stdio::inherit());
|
||||
cmd.envs(env::vars());
|
||||
run(&mut cmd, "gn gen");
|
||||
}
|
||||
gn_out_dir
|
||||
}
|
||||
|
||||
pub fn build(target: &str, maybe_env: Option<NinjaEnv>) {
|
||||
let gn_out_dir = get_dirs(None).out.join("gn_out");
|
||||
|
||||
// This helps Rust source files locate the snapshot, source map etc.
|
||||
println!("cargo:rustc-env=GN_OUT_DIR={}", gn_out_dir.display());
|
||||
|
||||
let mut cmd = ninja(&gn_out_dir, maybe_env.clone());
|
||||
cmd.arg(target);
|
||||
run(&mut cmd, "ninja");
|
||||
|
||||
rerun_if_changed(&gn_out_dir, maybe_env, target);
|
||||
|
||||
// TODO This is not sufficent. We need to use "gn desc" to query the target
|
||||
// and figure out what else we need to add to the link.
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}/obj/",
|
||||
gn_out_dir.display()
|
||||
);
|
||||
}
|
||||
|
||||
/// build.rs does not get re-run unless we tell cargo about what files we
|
||||
/// depend on. This outputs a bunch of rerun-if-changed lines to stdout.
|
||||
fn rerun_if_changed(
|
||||
out_dir: &PathBuf,
|
||||
maybe_env: Option<NinjaEnv>,
|
||||
target: &str,
|
||||
) {
|
||||
let deps = ninja_get_deps(out_dir, maybe_env, target);
|
||||
for d in deps {
|
||||
let p = out_dir.join(d);
|
||||
assert!(p.exists());
|
||||
println!("cargo:rerun-if-changed={}", p.display());
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cmd: &mut Command, program: &str) {
|
||||
use std::io::ErrorKind;
|
||||
println!("running: {:?}", cmd);
|
||||
let status = match cmd.status() {
|
||||
Ok(status) => status,
|
||||
Err(ref e) if e.kind() == ErrorKind::NotFound => {
|
||||
fail(&format!(
|
||||
"failed to execute command: {}\nis `{}` not installed?",
|
||||
e, program
|
||||
));
|
||||
}
|
||||
Err(e) => fail(&format!("failed to execute command: {}", e)),
|
||||
};
|
||||
if !status.success() {
|
||||
fail(&format!(
|
||||
"command did not execute successfully, got: {}",
|
||||
status
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn fail(s: &str) -> ! {
|
||||
panic!("\n{}\n\nbuild script failed, must exit now", s)
|
||||
}
|
||||
|
||||
fn ninja_get_deps(
|
||||
out_dir: &PathBuf,
|
||||
maybe_env: Option<NinjaEnv>,
|
||||
target: &str,
|
||||
) -> HashSet<String> {
|
||||
let mut cmd = ninja(out_dir, maybe_env.clone());
|
||||
cmd.arg("-t");
|
||||
cmd.arg("graph");
|
||||
cmd.arg(target);
|
||||
let output = cmd.output().expect("ninja -t graph failed");
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
let graph_files = parse_ninja_graph(&stdout);
|
||||
|
||||
let mut cmd = ninja(out_dir, maybe_env);
|
||||
cmd.arg(target);
|
||||
cmd.arg("-t");
|
||||
cmd.arg("deps");
|
||||
let output = cmd.output().expect("ninja -t deps failed");
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
let deps_files = parse_ninja_deps(&stdout);
|
||||
|
||||
// TODO(ry) There's probably a simpler way to union two HashSet<String>
|
||||
// objects.
|
||||
let mut out = HashSet::<String>::new();
|
||||
for x in graph_files.union(&deps_files) {
|
||||
out.insert(x.to_string());
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn parse_ninja_deps(s: &str) -> HashSet<String> {
|
||||
let mut out = HashSet::new();
|
||||
for line in s.lines() {
|
||||
if line.starts_with(" ") {
|
||||
let filename = line.trim().to_string();
|
||||
out.insert(filename);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// A parser for the output of "ninja -t graph". It returns all of the input
|
||||
/// files.
|
||||
pub fn parse_ninja_graph(s: &str) -> HashSet<String> {
|
||||
let mut out = HashSet::new();
|
||||
// This is extremely hacky and likely to break.
|
||||
for line in s.lines() {
|
||||
//println!("line {}", line);
|
||||
if line.starts_with('\"')
|
||||
&& line.contains("label=")
|
||||
&& !line.contains("shape=")
|
||||
&& !line.contains(" -> ")
|
||||
{
|
||||
let filename = line.split('\"').nth(3).unwrap();
|
||||
if !filename.starts_with("..") {
|
||||
continue;
|
||||
}
|
||||
out.insert(filename.to_string());
|
||||
println!("filename {}", filename);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
const MOCK_GRAPH: &str = r#"
|
||||
digraph ninja {
|
||||
rankdir="LR"
|
||||
node [fontsize=10, shape=box, height=0.25]
|
||||
edge [fontsize=10]
|
||||
"0x7fc3c040c210" [label="default"]
|
||||
"0x7fc3c040a7f0" -> "0x7fc3c040c210" [label=" phony"]
|
||||
"0x7fc3c040a7f0" [label="obj/default.stamp"]
|
||||
"0x7fc3c040a790" [label="stamp", shape=ellipse]
|
||||
"0x7fc3c040a790" -> "0x7fc3c040a7f0"
|
||||
"0x7fc3c040a6c0" -> "0x7fc3c040a790" [arrowhead=none]
|
||||
"0x7fc3c040a8a0" -> "0x7fc3c040a790" [arrowhead=none]
|
||||
"0x7fc3c040a920" -> "0x7fc3c040a790" [arrowhead=none]
|
||||
"0x7fc3c040a6c0" [label="obj/count_bytes.stamp"]
|
||||
"0x7fc3c040a4d0" -> "0x7fc3c040a6c0" [label=" stamp"]
|
||||
"0x7fc3c040a4d0" [label="gen/output.txt"]
|
||||
"0x7fc3c040a400" [label="___count_bytes___build_toolchain_mac_clang_x64__rule", shape=ellipse]
|
||||
"0x7fc3c040a400" -> "0x7fc3c040a4d0"
|
||||
"0x7fc3c040a580" -> "0x7fc3c040a400" [arrowhead=none]
|
||||
"0x7fc3c040a620" -> "0x7fc3c040a400" [arrowhead=none]
|
||||
"0x7fc3c040a580" [label="../../../example/src/count_bytes.py"]
|
||||
"0x7fc3c040a620" [label="../../../example/src/input.txt"]
|
||||
"0x7fc3c040a8a0" [label="foo"]
|
||||
"0x7fc3c040b5e0" [label="link", shape=ellipse]
|
||||
"0x7fc3c040b5e0" -> "0x7fc3c040a8a0"
|
||||
"0x7fc3c040b5e0" -> "0x7fc3c040b6d0"
|
||||
"0x7fc3c040b5e0" -> "0x7fc3c040b780"
|
||||
"0x7fc3c040b5e0" -> "0x7fc3c040b820"
|
||||
"0x7fc3c040b020" -> "0x7fc3c040b5e0" [arrowhead=none]
|
||||
"0x7fc3c040a920" -> "0x7fc3c040b5e0" [arrowhead=none]
|
||||
"0x7fc3c040b020" [label="obj/foo/foo.o"]
|
||||
"0x7fc3c040b0d0" -> "0x7fc3c040b020" [label=" cxx"]
|
||||
"0x7fc3c040b0d0" [label="../../../example/src/foo.cc"]
|
||||
"0x7fc3c040a920" [label="obj/libhello.a"]
|
||||
"0x7fc3c040be00" -> "0x7fc3c040a920" [label=" alink"]
|
||||
"0x7fc3c040be00" [label="obj/hello/hello.o"]
|
||||
"0x7fc3c040beb0" -> "0x7fc3c040be00" [label=" cxx"]
|
||||
"0x7fc3c040beb0" [label="../../../example/src/hello.cc"]
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn test_parse_ninja_graph() {
|
||||
let files = parse_ninja_graph(MOCK_GRAPH);
|
||||
assert!(files.contains("../../../example/src/input.txt"));
|
||||
assert!(files.contains("../../../example/src/count_bytes.py"));
|
||||
assert!(!files.contains("obj/hello/hello.o"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue