forked from tomaka/cargo-emscripten
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathlib.rs
204 lines (170 loc) · 6.54 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
extern crate cargo;
use std::process::{Output, Command, Stdio};
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::{self, BufReader, BufRead, BufWriter, Write, copy};
use std::env::current_exe;
use std::mem::swap;
use cargo::ops::{ExecEngine, CommandPrototype, CommandType};
use cargo::util::{self, ProcessError, ProcessBuilder, Config};
use cargo::core::shell::Verbosity;
use cargo::shell;
pub struct BuildEngine {
pub target: Option<String>,
pub sysroot: Option<PathBuf>,
pub emcc: Option<PathBuf>,
pub opt: Option<PathBuf>,
pub emit: Option<String>,
}
impl ExecEngine for BuildEngine {
fn exec(&self, command: CommandPrototype) -> Result<(), ProcessError> {
exec(command, false, self).map(|_| ())
}
fn exec_with_output(&self, command: CommandPrototype) -> Result<Output, ProcessError> {
exec(command, true, self).map(|a| a.unwrap())
}
}
impl BuildEngine {
pub fn emit_needs_35(emit: &Option<String>) -> bool {
match *emit {
Some(ref emit) if emit.starts_with("llvm35-") || emit.starts_with("em-") => true,
_ => false,
}
}
}
fn exec(mut command: CommandPrototype, with_output: bool, engine: &BuildEngine) -> Result<Option<Output>, ProcessError> {
match command.get_type() {
&CommandType::Rustc => (),
_ => return do_exec(command.into_process_builder(), with_output),
}
let is_binary = command.get_args().windows(2)
.find(|&args| {
args[0].to_str() == Some("--crate-type") &&
args[1].to_str() == Some("bin")
}).is_some();
// finding crate name
let crate_name = command.get_args().windows(2)
.filter_map(|args| {
if args[0].to_str() == Some("--crate-name") {
Some(args[1].to_str().unwrap().to_string())
} else {
None
}
}).next().unwrap();
// finding out dir
let out_dir = command.get_args().windows(2)
.filter_map(|args| {
if args[0].to_str() == Some("--out-dir") {
Some(args[1].to_str().unwrap().to_string())
} else {
None
}
}).next().unwrap();
let has_target = command.get_args()
.iter().find(|&arg| {
arg.to_str() == Some("--target")
}).is_some();
// NOTE: this is a hack, I'm not sure if there's a better way to detect this...
// We don't want to inject --sysroot into build dependencies meant to run on the target machine
let is_build = crate_name == "build-script-build" || (!has_target && engine.target.is_some());
let (emit, rustc_emit, transform) = {
if is_binary && !is_build {
if BuildEngine::emit_needs_35(&engine.emit) {
(engine.emit.as_ref(), Some("llvm-ir"), true)
} else {
(engine.emit.as_ref(), engine.emit.as_ref().map(|v| &**v), false)
}
} else {
(None, None, false)
}
};
if let Some(rustc_emit) = rustc_emit {
let verb = Verbosity::Normal;
let shell = shell(verb);
let config = Config::new(shell).unwrap();
let mut new_command = CommandPrototype::new(command.get_type().clone(), &config).unwrap();
for arg in command.get_args().iter().filter(|a| !a.to_str().unwrap().starts_with("--emit")) {
new_command.arg(arg);
}
for (key, val) in command.get_envs().iter() {
new_command.env(&key[..], val.as_ref().unwrap());
}
new_command.cwd(command.get_cwd().clone());
new_command.arg("--emit").arg(&format!("dep-info,{}", rustc_emit));
if transform && is_binary && !is_build {
new_command.arg("-C").arg("lto");
}
swap(&mut command, &mut new_command);
}
if let Some(ref sysroot) = engine.sysroot {
if !is_build {
command.arg("--sysroot").arg(&sysroot);
}
}
let output = try!(do_exec(command.into_process_builder(), with_output));
let ll_output_file = PathBuf::from(&format!("{}/{}.ll", out_dir, crate_name));
if transform {
llvm35_transform(engine.opt.as_ref().map(|v| &**v).unwrap_or(&Path::new("opt")), &*ll_output_file).unwrap();
}
match emit {
Some(ref emit) if emit.starts_with("em-") => {
let extension = match &emit[..] {
"em-html" => "html",
"em-js" => "js",
_ => panic!("unsupported emscripten emit type"),
};
let mut process = util::process(engine.emcc.as_ref().unwrap_or(&PathBuf::from("emcc"))).unwrap();
process.arg(&ll_output_file)
.arg("-lGL").arg("-lSDL").arg("-s").arg("USE_SDL=2")
.arg("-o").arg(&format!("{}/{}.{}", out_dir, crate_name, extension));
do_exec(process, with_output)
},
_ => Ok(output),
}
}
fn do_exec(process: ProcessBuilder, with_output: bool) -> Result<Option<Output>, ProcessError> {
if with_output {
process.exec_with_output().map(|o| Some(o))
} else {
process.exec().map(|_| None)
}
}
fn llvm35_transform(opt: &Path, path: &Path) -> io::Result<()> {
let input = try!(File::open(path));
let input = BufReader::new(input);
// Prepare LLVM optimization passes to remove llvm.assume and integer overflow checks
let opt_path = current_exe().unwrap();
let opt_path = Path::new(&opt_path);
let mut opt = Command::new(opt);
opt.arg(&format!("-load={}", opt_path.parent().unwrap().join("RemoveAssume.so").display()))
.arg("-remove-assume")
.arg("-globaldce")
.arg("-S")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit());
let mut opt = opt.spawn().unwrap();
{
let mut output = BufWriter::new(opt.stdin.as_mut().unwrap());
// Run a string transform over the IR to fix metadata syntax for LLVM 3.5
for line in input.lines() {
let mut line = try!(line);
let line = if line.starts_with("!") {
line = line.replace("!", "metadata !");
line = line.replace("distinct metadata", "metadata");
&line[9..]
} else {
&line[..]
};
try!(output.write_all(line.as_bytes()));
try!(output.write_all(&['\n' as u8]));
}
}
drop(opt.stdin.take());
{
let mut output = try!(File::create(path));
try!(copy(opt.stdout.as_mut().unwrap(), &mut output));
}
assert!(opt.wait().unwrap().success());
Ok(())
}