Skip to content

Commit 17065f6

Browse files
committed
build.rs: abstract over dep-finding backend more explicitly
1 parent fafece1 commit 17065f6

File tree

2 files changed

+135
-54
lines changed

2 files changed

+135
-54
lines changed

.appveyor.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ install:
7676
- if "%target_env%" == "msvc" call %USERPROFILE%\vcpkg\bootstrap-vcpkg.bat
7777
- if "%target_env%" == "msvc" %USERPROFILE%\vcpkg\vcpkg install --triplet x64-windows-static fontconfig freetype harfbuzz[icu,graphite2]
7878
- if "%target_env%" == "msvc" set VCPKG_ROOT=%USERPROFILE%\vcpkg
79-
- if "%target_env%" == "msvc" set TECTONIC_VCPKG=1
79+
- if "%target_env%" == "msvc" set TECTONIC_DEP_BACKEND=vcpkg
8080
- if "%target_env%" == "msvc" set RUSTFLAGS=-Ctarget-feature=+crt-static
8181
- rustc -vV
8282
- cargo -vV

build.rs

+134-53
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,158 @@
11
// build.rs -- build helper script for Tectonic.
2-
// Copyright 2016-2018 the Tectonic Project
2+
// Copyright 2016-2019 the Tectonic Project
33
// Licensed under the MIT License.
4-
//
5-
// TODO: this surely needs to become much smarter and more flexible.
64

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.
710
use cc;
811
use pkg_config;
912
use vcpkg;
1013

1114
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";
1320

1421
// No fontconfig on MacOS:
1522
#[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"];
1735

1836
#[cfg(target_os = "macos")]
1937
const VCPKG_LIBS: &[&'static str] = &["harfbuzz", "freetype", "graphite2"];
2038

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+
}
2444

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+
}
2881

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()
33135
}
34136
}
35137

36138
fn main() {
37139
let target = env::var("TARGET").unwrap();
38140
let rustflags = env::var("RUSTFLAGS").unwrap_or(String::new());
39141

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),
51152
}
52153
} 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+
};
60156

61157
// Actually I'm not 100% sure that I can't compile the C and C++ code
62158
// into one library, but who cares?
@@ -245,17 +341,10 @@ fn main() {
245341
.file("tectonic/xetex-XeTeXOTMath.cpp")
246342
.include(".");
247343

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+
});
259348

260349
// Platform-specific adjustments:
261350

@@ -298,15 +387,7 @@ fn main() {
298387
ccfg.compile("libtectonic_c.a");
299388
cppcfg.compile("libtectonic_cpp.a");
300389

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);
310391

311392
// Tell cargo to rerun build.rs only if files in the tectonic/ directory have changed.
312393
for file in PathBuf::from("tectonic").read_dir().unwrap() {

0 commit comments

Comments
 (0)