1mod deps;
2
3use std::env;
4use std::path::PathBuf;
5use std::process::Command;
6use std::process::Stdio;
7
8#[derive(Clone, Debug)]
9struct Dirs {
10 pub out: PathBuf,
11 pub root: PathBuf,
12}
13
14fn get_dirs(manifest_dir: Option<&str>) -> Dirs {
15 let out = env::var("OUT_DIR").map(PathBuf::from).unwrap();
23 let out = out
24 .parent()
25 .unwrap()
26 .parent()
27 .unwrap()
28 .parent()
29 .unwrap()
30 .to_owned();
31
32 let root = match manifest_dir {
33 Some(s) => env::current_dir().unwrap().join(s),
34 None => env::var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap(),
35 };
36
37 let mut dirs = Dirs { out, root };
38 maybe_symlink_root_dir(&mut dirs);
39 dirs
40}
41
42#[cfg(not(target_os = "windows"))]
43fn maybe_symlink_root_dir(_: &mut Dirs) {}
44
45#[cfg(target_os = "windows")]
46fn maybe_symlink_root_dir(dirs: &mut Dirs) {
47 use std::fs::remove_dir;
52 use std::os::windows::fs::symlink_dir;
53 use std::path::{Component, Path};
54
55 let get_prefix = |p: &Path| {
56 p.components()
57 .find_map(|c| match c {
58 Component::Prefix(p) => Some(p),
59 _ => None,
60 })
61 .map(|p| p.as_os_str().to_owned())
62 };
63
64 let Dirs { out, root } = dirs;
65 if get_prefix(out) != get_prefix(root) {
66 let symlink = &*out.join("gn_root");
67 let target = &*root.canonicalize().unwrap();
68
69 println!("Creating symlink {:?} to {:?}", &symlink, &root);
70
71 loop {
72 match symlink.canonicalize() {
73 Ok(existing) if existing == target => break,
74 Ok(_) => remove_dir(symlink).expect("remove_dir failed"),
75 Err(_) => {
76 break symlink_dir(target, symlink).expect("symlink_dir failed")
77 }
78 }
79 }
80
81 dirs.root = symlink.to_path_buf();
82 }
83}
84
85pub fn is_debug() -> bool {
86 let m = env::var("PROFILE").unwrap();
89 if m == "release" {
90 false
91 } else if m == "debug" {
92 true
93 } else {
94 panic!("unhandled PROFILE value {}", m)
95 }
96}
97
98fn gn() -> String {
99 env::var("GN").unwrap_or_else(|_| "gn".to_owned())
100}
101
102pub type NinjaEnv = Vec<(String, String)>;
103
104fn ninja(gn_out_dir: &PathBuf, maybe_env: Option<NinjaEnv>) -> Command {
105 let cmd_string = env::var("NINJA").unwrap_or_else(|_| "ninja".to_owned());
106 let mut cmd = Command::new(cmd_string);
107 cmd.arg("-C");
108 cmd.arg(&gn_out_dir);
109 if let Some(env) = maybe_env {
110 for item in env {
111 cmd.env(item.0, item.1);
112 }
113 }
114 cmd
115}
116
117pub type GnArgs = Vec<String>;
118
119pub fn maybe_gen(manifest_dir: &str, gn_args: GnArgs) -> PathBuf {
120 let dirs = get_dirs(Some(manifest_dir));
121 let gn_out_dir = dirs.out.join("gn_out");
122
123 if !gn_out_dir.exists() || !gn_out_dir.join("build.ninja").exists() {
124 let args = gn_args.join(" ");
125
126 let path = env::current_dir().unwrap();
127 println!("The current directory is {}", path.display());
128 println!(
129 "gn gen --root={} {}",
130 dirs.root.display(),
131 gn_out_dir.display()
132 );
133 let mut cmd = Command::new(gn());
134 cmd.arg(format!("--root={}", dirs.root.display()));
135 cmd.arg("gen");
136 cmd.arg(&gn_out_dir);
137 cmd.arg("--args=".to_owned() + &args);
138 cmd.stdout(Stdio::inherit());
139 cmd.stderr(Stdio::inherit());
140 cmd.envs(env::vars());
141 run(&mut cmd, "gn gen");
142 }
143 gn_out_dir
144}
145
146pub fn build(target: &str, maybe_env: Option<NinjaEnv>) {
147 let gn_out_dir = get_dirs(None).out.join("gn_out");
148
149 println!("cargo:rustc-env=GN_OUT_DIR={}", gn_out_dir.display());
151
152 let mut cmd = ninja(&gn_out_dir, maybe_env.clone());
153 cmd.arg(target);
154 run(&mut cmd, "ninja");
155
156 rerun_if_changed(&gn_out_dir, maybe_env, target);
157
158 println!(
161 "cargo:rustc-link-search=native={}/obj/",
162 gn_out_dir.display()
163 );
164}
165
166fn rerun_if_changed(
169 out_dir: &PathBuf,
170 maybe_env: Option<NinjaEnv>,
171 target: &str,
172) {
173 let deps = deps::ninja_get_deps(out_dir, maybe_env, target);
174 for d in deps {
175 let p = out_dir.join(d);
176 assert!(p.exists());
177 println!("cargo:rerun-if-changed={}", p.display());
178 }
179}
180
181fn run(cmd: &mut Command, program: &str) {
182 use std::io::ErrorKind;
183 println!("running: {:?}", cmd);
184 let status = match cmd.status() {
185 Ok(status) => status,
186 Err(ref e) if e.kind() == ErrorKind::NotFound => {
187 fail(&format!(
188 "failed to execute command: {}\nis `{}` not installed?",
189 e, program
190 ));
191 }
192 Err(e) => fail(&format!("failed to execute command: {}", e)),
193 };
194 if !status.success() {
195 fail(&format!(
196 "command did not execute successfully, got: {}",
197 status
198 ));
199 }
200}
201
202fn fail(s: &str) -> ! {
203 panic!("\n{}\n\nbuild script failed, must exit now", s)
204}