-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbuild.rs
349 lines (296 loc) · 15.7 KB
/
build.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
//Depending on the Cargo features, some parts of this file are not run
#![allow(dead_code)]
#![allow(unreachable_code)]
use std::fs; // Used to copy the C headers
use std::path::{PathBuf, Path};
extern crate cc;
#[cfg(feature = "link_shared_librosie")]
extern crate pkg_config;
//TODO: Test on Windows. I suspect it won't work, and I may need to follow the example from Teseract,
// using the vcpkg crate, here:
//https://github.com/ccouzens/tesseract-sys/blob/master/build.rs
fn main() -> Result<(), i32> {
//If both "build-mode" features are specified then it's an error
#[cfg(all(feature = "link_shared_librosie", feature = "build_static_librosie"))]
{
// println!("cargo:warning=Error: both link_shared_librosie and build_static_librosie are specified");
println!("Error: both link_shared_librosie and build_static_librosie are specified");
return Err(-1);
}
//See if we're linking a shared librosie
#[cfg(feature = "link_shared_librosie")]
{
//First, see if we canc locate the library using pkg_config
let librosie = pkg_config::Config::new()
.cargo_metadata(true)
.print_system_libs(true)
.atleast_version("1.3.0")
.probe("rosie");
if librosie.is_ok() {
//pkg_config should output the necessary output for cargo
return Ok(());
}
//If pkg_config didn't find librosie, try one more time to see if it's installed by trying to build something that links it
if librosie_installed() {
println!("cargo:rustc-link-lib=rosie");
return Ok(());
}
//We got here because we failed to find an existing librosie
println!("Error: link_shared_librosie specified, but librosie 1.3.0 or higher couldn't be found");
return Err(-1);
}
//If we're not linking the shared lib, then we're building a private copy
#[cfg(feature = "build_static_librosie")]
{
//Build librosie from source
if librosie_src_build() {
println!("cargo:rustc-link-lib=rosie");
return Ok(());
}
//We got here because we failed to find or build librosie.
println!("Error: librosie build failure");
return Err(-1);
}
//If neither env vars are set then it's an error
#[cfg(not(any(feature = "link_shared_librosie", feature = "build_static_librosie")))]
{
println!("Error: either the link_shared_librosie or build_static_librosie feature must be specified");
return Err(-1);
}
//If we didn't take any of the above paths then exit, having built nothing,
//NOTE: This will never be hit, given the logic above
// println!("cargo:warning=ROSIE NOT BUILT. Either the link_shared_librosie or build_static_librosie feature must be specified to build librosie");
// return Ok(());
}
//If we haven't found it using one of the pkg trackers, try to compile the smoke.c file to "smoke it out"
//Thanks to the zlib crate for this idea: https://github.com/rust-lang/libz-sys/blob/main/build.rs
fn librosie_installed() -> bool {
let smoke_file : PathBuf = ["src", "smoke.c"].iter().collect();
let mut cfg = cc::Build::new();
cfg.cargo_metadata(false);
cfg.file(smoke_file);
if cfg.try_compile("smoke").is_ok() {
return true;
}
false
}
//Build the librosie library from source
//
// It looks like the "make-cmd" crate is in really sad shape. No updates for the last 6 years, and it's just
// a very thin wrapper around std::process::Command anyway.
//
// I'm at a crossroads. Should I (1.) use the Rust cc crate directly or should I (2.) call the host system's
// `make` using the std::process::Command?
//
// 1. Rust cc is better supported by Cargo and will likely be more robust against platform iregularities, but
// I lose out on getting to leverage the maintainence done by the librosie (and rpeg) Makefiles.
// 2. Running the Makefile using std::process::Command preserves the rosie Makefile work, but then I have to
// do the "autoconfigure" work myself, which sounds like a nightmare.
//
// I think I'll choose 1, because it's the devil I know. In other words, I don't know what I don't know about
// possible configs this might end up needing to run on. But in the case of 1., I can verify that everything
// is building as expected.
fn librosie_src_build() -> bool {
//Incoming from Cargo
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
//The build products
let rosie_lib_dir = out_dir.clone(); //cc's default is to put the build lib in the out_dir, and that's fine with us
let rosie_include_dir = out_dir.join("include");
let rosie_home_dir = out_dir.join("rosie_home");
//The C source file locations
let lua_cjson_dir : PathBuf = ["src", "lua-cjson"].iter().collect();
let rpeg_src_dir : PathBuf = ["src", "rpeg"].iter().collect();
let rpeg_compiler_dir = rpeg_src_dir.join("compiler");
let rpeg_runtime_dir = rpeg_src_dir.join("runtime");
let librosie_src_dir : PathBuf = ["src", "librosie"].iter().collect();
//The extra artifacts, included in the manifest
let rosie_include_src_dir = manifest_dir.join(&librosie_src_dir);
let rosie_home_src_dir = manifest_dir.join("src").join("rosie_home");
//Locate the LUA headers and library, which should have been built by the mlua crate dependency
let lua_include_dir = PathBuf::from(std::env::var_os("DEP_LUA_INCLUDE").unwrap());
let lua_lib_dir = PathBuf::from(std::env::var_os("DEP_LUA_LIB").unwrap());
// let lua_a : PathBuf = lua_lib_dir.join("liblua5.3.a");
//This is needed so the librosie we're about to build will succeed in linking with the mlua output lib
println!("cargo:rustc-link-search={}", lua_lib_dir.display());
println!("cargo:rustc-link-lib=static=lua5.3");
//The C src files needed to build librosie
let compile_src_files : Vec<PathBuf> = vec![
// lua-cjson
lua_cjson_dir.join("fpconv.c"),
lua_cjson_dir.join("strbuf.c"),
lua_cjson_dir.join("lua_cjson.c"),
// rpeg compiler
rpeg_compiler_dir.join("rbuf.c"),
rpeg_compiler_dir.join("lpcap.c"),
rpeg_compiler_dir.join("lptree.c"),
rpeg_compiler_dir.join("lpcode.c"),
rpeg_compiler_dir.join("lpprint.c"),
// rpeg runtime
rpeg_runtime_dir.join("buf.c"),
rpeg_runtime_dir.join("capture.c"),
rpeg_runtime_dir.join("file.c"),
rpeg_runtime_dir.join("json.c"),
rpeg_runtime_dir.join("ktable.c"),
rpeg_runtime_dir.join("rplx.c"),
rpeg_runtime_dir.join("vm.c"),
// librosie
librosie_src_dir.join("librosie.c"),
];
//Invoke the C compiler to perform the librosie build
let mut cfg = cc::Build::new();
cfg.static_flag(true);
cfg.include(lua_include_dir);
cfg.include(rpeg_src_dir.join("include"));
cfg.define("LPEG_DEBUG", None); // Needed by the rpeg compiler build
cfg.define("NDEBUG", None); // Needed by the rpeg compiler and librosie build
cfg.define("LUA_COMPAT_5_2", None); // Needed by the librosie build
cfg.define("_GNU_SOURCE", None); // Required to access libdl on Linux and harmless on Mac
cfg.files(compile_src_files.iter());
cfg.compile("rosie");
println!("cargo:rerun-if-changed=src");
//rosie_home files, to copy into the build output. Currently these are just copies of the
//files built by the main Rosie project's Makefile. This is ok because they all have
//platform-independent formats.
let rosie_home_files : Vec<PathBuf> = vec![
PathBuf::from("CHANGELOG"),
PathBuf::from("CONTRIBUTORS"),
PathBuf::from("LICENSE"),
PathBuf::from("README"),
PathBuf::from("VERSION"),
//QUESTION: It's highly debatable whether we should include the "extra" files in this crate.
//CONCLUSION: NO. They're almsot 1MB, and they aren't used automatically by anything else in
// rosie-sys or rosie-rs. If people want them, it's easy enough to get them from the upstream
// rosie repository.
//"extra" directory within rosie_home. Kept for completeness.
//
// PathBuf::from("extra").join("WSL").join("rosie_install.sh"),
// PathBuf::from("extra").join("docker").join("README.md"),
// PathBuf::from("extra").join("docker").join("arch"),
// PathBuf::from("extra").join("docker").join("centos"),
// PathBuf::from("extra").join("docker").join("elementary"),
// PathBuf::from("extra").join("docker").join("fedora"),
// PathBuf::from("extra").join("docker").join("nixos"),
// PathBuf::from("extra").join("docker").join("run"),
// PathBuf::from("extra").join("docker").join("syslog-2018-09-05.rpl"),
// PathBuf::from("extra").join("docker").join("ubuntu"),
// PathBuf::from("extra").join("docker").join("valgrind-fedora"),
// PathBuf::from("extra").join("emacs").join("rpl-mode.el"),
// PathBuf::from("extra").join("examples").join("README.md"),
// PathBuf::from("extra").join("examples").join("anbmcndm.rpl"),
// PathBuf::from("extra").join("examples").join("anbn.rpl"),
// PathBuf::from("extra").join("examples").join("anbncn.rpl"),
// PathBuf::from("extra").join("examples").join("anbncndn.rpl"),
// PathBuf::from("extra").join("examples").join("backref.rpl"),
// PathBuf::from("extra").join("examples").join("distinct.rpl"),
// PathBuf::from("extra").join("examples").join("dyck.rpl"),
// PathBuf::from("extra").join("examples").join("html.rpl"),
// PathBuf::from("extra").join("examples").join("ipv4.py"),
// PathBuf::from("extra").join("examples").join("reverse.rpl"),
// PathBuf::from("extra").join("examples").join("sloc.py"),
// PathBuf::from("extra").join("examples").join("sloc_C.sh"),
// PathBuf::from("extra").join("examples").join("sloc_lua.sh"),
// PathBuf::from("extra").join("examples").join("images").join("p1.gif"),
// PathBuf::from("extra").join("examples").join("images").join("readme-fig1.png"),
// PathBuf::from("extra").join("examples").join("images").join("readme-fig2.png"),
// PathBuf::from("extra").join("examples").join("images").join("readme-fig3.png"),
// PathBuf::from("extra").join("examples").join("images").join("readme-fig4.png"),
// PathBuf::from("extra").join("examples").join("images").join("readme-fig5.png"),
// PathBuf::from("extra").join("vim").join("ftdetect").join("rosie.vim"),
// PathBuf::from("extra").join("vim").join("syntax").join("rosie.vim"),
PathBuf::from("lib").join("argparse.luac"),
PathBuf::from("lib").join("ast.luac"),
PathBuf::from("lib").join("boot.luac"),
PathBuf::from("lib").join("builtins.luac"),
PathBuf::from("lib").join("cli-common.luac"),
PathBuf::from("lib").join("cli-match.luac"),
PathBuf::from("lib").join("cli-parser.luac"),
PathBuf::from("lib").join("cli.luac"),
PathBuf::from("lib").join("color.luac"),
PathBuf::from("lib").join("common.luac"),
PathBuf::from("lib").join("compile.luac"),
PathBuf::from("lib").join("engine_module.luac"),
PathBuf::from("lib").join("environment.luac"),
PathBuf::from("lib").join("expand.luac"),
PathBuf::from("lib").join("expr.luac"),
PathBuf::from("lib").join("infix.luac"),
PathBuf::from("lib").join("init.luac"),
PathBuf::from("lib").join("list.luac"),
PathBuf::from("lib").join("loadpkg.luac"),
PathBuf::from("lib").join("parse.luac"),
PathBuf::from("lib").join("parse_core.luac"),
PathBuf::from("lib").join("rcfile.luac"),
PathBuf::from("lib").join("recordtype.luac"),
PathBuf::from("lib").join("repl.luac"),
PathBuf::from("lib").join("strict.luac"),
PathBuf::from("lib").join("submodule.luac"),
PathBuf::from("lib").join("thread.luac"),
PathBuf::from("lib").join("trace.luac"),
PathBuf::from("lib").join("ui.luac"),
PathBuf::from("lib").join("unittest.luac"),
PathBuf::from("lib").join("ustring.luac"),
PathBuf::from("lib").join("util.luac"),
PathBuf::from("lib").join("violation.luac"),
PathBuf::from("lib").join("writer.luac"),
PathBuf::from("rpl").join("all.rpl"),
PathBuf::from("rpl").join("char.rpl"),
PathBuf::from("rpl").join("csv.rpl"),
PathBuf::from("rpl").join("date.rpl"),
PathBuf::from("rpl").join("id.rpl"),
PathBuf::from("rpl").join("json.rpl"),
PathBuf::from("rpl").join("net.rpl"),
PathBuf::from("rpl").join("num.rpl"),
PathBuf::from("rpl").join("os.rpl"),
PathBuf::from("rpl").join("re.rpl"),
PathBuf::from("rpl").join("time.rpl"),
PathBuf::from("rpl").join("ts.rpl"),
PathBuf::from("rpl").join("ver.rpl"),
PathBuf::from("rpl").join("word.rpl"),
PathBuf::from("rpl").join("Unicode").join("Ascii.rpl"),
PathBuf::from("rpl").join("Unicode").join("Block.rpl"),
PathBuf::from("rpl").join("Unicode").join("Category.rpl"),
PathBuf::from("rpl").join("Unicode").join("GraphemeBreak.rpl"),
PathBuf::from("rpl").join("Unicode").join("LineBreak.rpl"),
PathBuf::from("rpl").join("Unicode").join("NumericType.rpl"),
PathBuf::from("rpl").join("Unicode").join("Property.rpl"),
PathBuf::from("rpl").join("Unicode").join("Script.rpl"),
PathBuf::from("rpl").join("Unicode").join("SentenceBreak.rpl"),
PathBuf::from("rpl").join("Unicode").join("WordBreak.rpl"),
PathBuf::from("rpl").join("builtin").join("prelude.rpl"),
PathBuf::from("rpl").join("rosie").join("rcfile.rpl"),
PathBuf::from("rpl").join("rosie").join("rpl_1_1.rpl"),
PathBuf::from("rpl").join("rosie").join("rpl_1_2.rpl"),
PathBuf::from("rpl").join("rosie").join("rpl_1_3.rpl"),
];
//Copy the rosie_home files to the build artifacts output dir
create_empty_dir(&rosie_home_dir).unwrap();
for file in &rosie_home_files {
let dest_path = rosie_home_dir.join(file);
create_parent_dir(&dest_path).unwrap();
fs::copy(rosie_home_src_dir.join(file), dest_path).unwrap();
}
//Set the env variable that we'll use to communicate the location of the rosie_home dir, to embed in the return value
//for the rosie_home_default call. This is inelegant, but should work for now.
//TODO: In the future, we should embed the CONTENTS of the rosie_home into the binary, not just the path
println!("cargo:rustc-env=ROSIE_HOME={}", rosie_home_dir.display());
//Copy the rosie header file(s) so anyone who needs to use this crate for a C dependency build against it
create_empty_dir(&rosie_include_dir).unwrap();
for include_file in &["librosie.h"] {
fs::copy(rosie_include_src_dir.join(include_file), rosie_include_dir.join(include_file)).unwrap();
}
//Tell cargo how to set `DEP_ROSIE_INCLUDE`
println!("cargo:include={}", rosie_include_dir.display());
//Tell cargo so anyone who depends on this crate can find our lib. I.e. so cargo will set `DEP_ROSIE_LIB`
println!("cargo:lib={}", rosie_lib_dir.display());
true
}
fn create_empty_dir<P: AsRef<Path>>(path: P) -> Result<(), std::io::Error> {
if path.as_ref().exists() {
fs::remove_dir_all(&path)?;
}
fs::create_dir_all(&path)
}
fn create_parent_dir<P: AsRef<Path>>(path: P) -> Result<(), std::io::Error> {
let parent = path.as_ref().parent().ok_or(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("Can't get parent of path: {}", path.as_ref().display())))?;
fs::create_dir_all(&parent)
}