|
1 | 1 | // build.rs -- build helper script for Tectonic.
|
2 |
| -// Copyright 2016-2018 the Tectonic Project |
| 2 | +// Copyright 2016-2019 the Tectonic Project |
3 | 3 | // Licensed under the MIT License.
|
4 |
| -// |
5 |
| -// TODO: this surely needs to become much smarter and more flexible. |
6 | 4 |
|
| 5 | +/// The Tectonic build script. Not only do we have internal C/C++ code, we |
| 6 | +/// also depend on several external C/C++ libraries, so there's a lot to do |
| 7 | +/// here. It would be great to streamline things. |
| 8 | +/// |
| 9 | +/// TODO: this surely needs to become much smarter and more flexible. |
7 | 10 | use cc;
|
8 | 11 | use pkg_config;
|
9 | 12 | use vcpkg;
|
10 | 13 |
|
11 | 14 | use std::env;
|
12 |
| -use std::path::PathBuf; |
| 15 | +use std::path::{Path, PathBuf}; |
| 16 | + |
| 17 | +#[cfg(not(target_os = "macos"))] |
| 18 | +const PKGCONFIG_LIBS: &'static str = |
| 19 | + "fontconfig harfbuzz >= 1.4 harfbuzz-icu icu-uc freetype2 graphite2 libpng zlib"; |
13 | 20 |
|
14 | 21 | // No fontconfig on MacOS:
|
15 | 22 | #[cfg(target_os = "macos")]
|
16 |
| -const LIBS: &'static str = "harfbuzz >= 1.4 harfbuzz-icu icu-uc freetype2 graphite2 libpng zlib"; |
| 23 | +const PKGCONFIG_LIBS: &'static str = |
| 24 | + "harfbuzz >= 1.4 harfbuzz-icu icu-uc freetype2 graphite2 libpng zlib"; |
| 25 | + |
| 26 | +/// Build-script state when using pkg-config as the backend. |
| 27 | +#[derive(Debug)] |
| 28 | +struct PkgConfigState { |
| 29 | + libs: pkg_config::Library, |
| 30 | +} |
| 31 | + |
| 32 | +// Need a way to check that the vcpkg harfbuzz port has graphite2 and icu options enabled. |
| 33 | +#[cfg(not(target_os = "macos"))] |
| 34 | +const VCPKG_LIBS: &[&'static str] = &["fontconfig", "harfbuzz", "freetype", "graphite2"]; |
17 | 35 |
|
18 | 36 | #[cfg(target_os = "macos")]
|
19 | 37 | const VCPKG_LIBS: &[&'static str] = &["harfbuzz", "freetype", "graphite2"];
|
20 | 38 |
|
21 |
| -#[cfg(not(target_os = "macos"))] |
22 |
| -const LIBS: &'static str = |
23 |
| - "fontconfig harfbuzz >= 1.4 harfbuzz-icu icu-uc freetype2 graphite2 libpng zlib"; |
| 39 | +/// Build-script state when using vcpkg as the backend. |
| 40 | +#[derive(Clone, Debug)] |
| 41 | +struct VcPkgState { |
| 42 | + include_paths: Vec<PathBuf>, |
| 43 | +} |
24 | 44 |
|
25 |
| -#[cfg(not(target_os = "macos"))] |
26 |
| -const VCPKG_LIBS: &[&'static str] = &["fontconfig", "harfbuzz", "freetype", "graphite2"]; |
27 |
| -// Need a way to check that the vcpkg harfbuzz port has graphite2 and icu options enabled. |
| 45 | +/// State for discovering and managing our dependencies, which may vary |
| 46 | +/// depending on the framework that we're using to discover them. |
| 47 | +/// |
| 48 | +/// The basic gameplan is that we probe our dependencies to check that they're |
| 49 | +/// available and pull out the C/C++ include directories; then we emit info |
| 50 | +/// for building our C/C++ libraries; then we emit info for our dependencies. |
| 51 | +/// Building stuff pretty much always requires some level of hackery, though, |
| 52 | +/// so we don't try to be purist about the details. |
| 53 | +#[derive(Debug)] |
| 54 | +enum DepState { |
| 55 | + /// pkg-config |
| 56 | + PkgConfig(PkgConfigState), |
| 57 | + |
| 58 | + /// vcpkg |
| 59 | + VcPkg(VcPkgState), |
| 60 | +} |
| 61 | + |
| 62 | +impl DepState { |
| 63 | + /// Probe for our dependent libraries using pkg-config. |
| 64 | + fn new_pkg_config() -> Self { |
| 65 | + let libs = pkg_config::Config::new() |
| 66 | + .cargo_metadata(false) |
| 67 | + .probe(PKGCONFIG_LIBS) |
| 68 | + .unwrap(); |
| 69 | + DepState::PkgConfig(PkgConfigState { libs }) |
| 70 | + } |
| 71 | + |
| 72 | + /// Probe for our dependent libraries using vcpkg. |
| 73 | + fn new_vcpkg() -> Self { |
| 74 | + let mut include_paths = vec![]; |
| 75 | + |
| 76 | + for dep in VCPKG_LIBS { |
| 77 | + let library = vcpkg::find_package(dep) |
| 78 | + .expect(&format!("failed to load package {} from vcpkg", dep)); |
| 79 | + include_paths.extend(library.include_paths.iter().cloned()); |
| 80 | + } |
28 | 81 |
|
29 |
| -fn load_vcpkg_deps(include_paths: &mut Vec<PathBuf>) { |
30 |
| - for dep in VCPKG_LIBS { |
31 |
| - let library = vcpkg::find_package(dep).expect("failed to load package from vcpkg"); |
32 |
| - include_paths.extend(library.include_paths.iter().cloned()); |
| 82 | + DepState::VcPkg(VcPkgState { include_paths }) |
| 83 | + } |
| 84 | + |
| 85 | + /// Invoke a callback for each C/C++ include directory injected by our |
| 86 | + /// dependencies. |
| 87 | + fn foreach_include_path<F>(&self, mut f: F) |
| 88 | + where |
| 89 | + F: FnMut(&Path), |
| 90 | + { |
| 91 | + match self { |
| 92 | + &DepState::PkgConfig(ref s) => { |
| 93 | + for p in &s.libs.include_paths { |
| 94 | + f(p); |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + &DepState::VcPkg(ref s) => { |
| 99 | + for p in &s.include_paths { |
| 100 | + f(p); |
| 101 | + } |
| 102 | + } |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + /// This function is called after we've emitted the cargo compilation info |
| 107 | + /// for our own libraries. Now we can emit any special information |
| 108 | + /// relating to our dependencies, which may depend on the dep-finding |
| 109 | + /// backend or the target. |
| 110 | + fn emit_late_extras(&self, target: &str) { |
| 111 | + match self { |
| 112 | + &DepState::PkgConfig(_) => { |
| 113 | + pkg_config::Config::new() |
| 114 | + .cargo_metadata(true) |
| 115 | + .probe(PKGCONFIG_LIBS) |
| 116 | + .unwrap(); |
| 117 | + } |
| 118 | + |
| 119 | + &DepState::VcPkg(_) => { |
| 120 | + if target.contains("-linux-") { |
| 121 | + // add icudata to the end of the list of libs as vcpkg-rs |
| 122 | + // does not order individual libraries as a single pass |
| 123 | + // linker requires. |
| 124 | + println!("cargo:rustc-link-lib=icudata"); |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +/// The default dependency-finding backend is pkg-config. |
| 132 | +impl Default for DepState { |
| 133 | + fn default() -> Self { |
| 134 | + DepState::new_pkg_config() |
33 | 135 | }
|
34 | 136 | }
|
35 | 137 |
|
36 | 138 | fn main() {
|
37 | 139 | let target = env::var("TARGET").unwrap();
|
38 | 140 | let rustflags = env::var("RUSTFLAGS").unwrap_or(String::new());
|
39 | 141 |
|
40 |
| - let use_vcpkg = env::var("TECTONIC_VCPKG").is_ok(); |
41 |
| - let mut deps = None; |
42 |
| - let mut vcpkg_includes = vec![]; |
43 |
| - if use_vcpkg { |
44 |
| - load_vcpkg_deps(&mut vcpkg_includes); |
45 |
| - eprintln!("{:?}", vcpkg_includes); |
46 |
| - |
47 |
| - if target.contains("-linux-") { |
48 |
| - // add icudata to the end of the list of libs as vcpkg-rs does not |
49 |
| - // order individual libraries as a single pass linker requires |
50 |
| - println!("cargo:rustc-link-lib=icudata"); |
| 142 | + // OK, how are we finding our dependencies? |
| 143 | + |
| 144 | + println!("cargo:rerun-if-env-changed=TECTONIC_DEP_BACKEND"); |
| 145 | + |
| 146 | + let dep_state = if let Ok(dep_backend_str) = env::var("TECTONIC_DEP_BACKEND") { |
| 147 | + match dep_backend_str.as_ref() { |
| 148 | + "pkg-config" => DepState::new_pkg_config(), |
| 149 | + "vcpkg" => DepState::new_vcpkg(), |
| 150 | + "default" => DepState::default(), |
| 151 | + other => panic!("unrecognized TECTONIC_DEP_BACKEND setting {:?}", other), |
51 | 152 | }
|
52 | 153 | } else {
|
53 |
| - // We (have to) rerun the search again below to emit the metadata at the right time. |
54 |
| - let deps_library = pkg_config::Config::new() |
55 |
| - .cargo_metadata(false) |
56 |
| - .probe(LIBS) |
57 |
| - .unwrap(); |
58 |
| - deps = Some(deps_library); |
59 |
| - } |
| 154 | + DepState::default() |
| 155 | + }; |
60 | 156 |
|
61 | 157 | // Actually I'm not 100% sure that I can't compile the C and C++ code
|
62 | 158 | // into one library, but who cares?
|
@@ -245,17 +341,10 @@ fn main() {
|
245 | 341 | .file("tectonic/xetex-XeTeXOTMath.cpp")
|
246 | 342 | .include(".");
|
247 | 343 |
|
248 |
| - if let Some(deps) = &deps { |
249 |
| - for p in &deps.include_paths { |
250 |
| - ccfg.include(p); |
251 |
| - cppcfg.include(p); |
252 |
| - } |
253 |
| - } else { |
254 |
| - for p in &vcpkg_includes { |
255 |
| - ccfg.include(p); |
256 |
| - cppcfg.include(p); |
257 |
| - } |
258 |
| - } |
| 344 | + dep_state.foreach_include_path(|p| { |
| 345 | + ccfg.include(p); |
| 346 | + cppcfg.include(p); |
| 347 | + }); |
259 | 348 |
|
260 | 349 | // Platform-specific adjustments:
|
261 | 350 |
|
@@ -298,15 +387,7 @@ fn main() {
|
298 | 387 | ccfg.compile("libtectonic_c.a");
|
299 | 388 | cppcfg.compile("libtectonic_cpp.a");
|
300 | 389 |
|
301 |
| - // Now that we've emitted the info for our own libraries, we can emit the |
302 |
| - // info for their dependents. |
303 |
| - |
304 |
| - if let Some(_deps) = &deps { |
305 |
| - pkg_config::Config::new() |
306 |
| - .cargo_metadata(true) |
307 |
| - .probe(LIBS) |
308 |
| - .unwrap(); |
309 |
| - } |
| 390 | + dep_state.emit_late_extras(&target); |
310 | 391 |
|
311 | 392 | // Tell cargo to rerun build.rs only if files in the tectonic/ directory have changed.
|
312 | 393 | for file in PathBuf::from("tectonic").read_dir().unwrap() {
|
|
0 commit comments