rust/src/build_helper/lib.rs

205 lines
6.3 KiB
Rust
Raw Normal View History

use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
2018-04-01 04:32:40 +02:00
use std::time::{SystemTime, UNIX_EPOCH};
2018-03-25 16:20:56 +02:00
use std::{env, fs};
/// A helper macro to `unwrap` a result except also print out details like:
///
/// * The file/line of the panic
/// * The expression that failed
/// * The error itself
///
/// This is currently used judiciously throughout the build system rather than
/// using a `Result` with `try!`, but this may change one day...
#[macro_export]
macro_rules! t {
2018-03-25 16:20:56 +02:00
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
// it can show extra info in the second parameter
($e:expr, $extra:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {} ({:?})", stringify!($e), e, $extra),
}
};
}
/// Reads an environment variable and adds it to dependencies.
/// Supposed to be used for all variables except those set for build scripts by cargo
2020-11-05 14:33:23 +01:00
/// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts>
pub fn tracked_env_var_os<K: AsRef<OsStr> + Display>(key: K) -> Option<OsString> {
println!("cargo:rerun-if-env-changed={}", key);
env::var_os(key)
}
// Because Cargo adds the compiler's dylib path to our library search path, llvm-config may
// break: the dylib path for the compiler, as of this writing, contains a copy of the LLVM
// shared library, which means that when our freshly built llvm-config goes to load it's
// associated LLVM, it actually loads the compiler's LLVM. In particular when building the first
// compiler (i.e., in stage 0) that's a problem, as the compiler's LLVM is likely different from
// the one we want to use. As such, we restore the environment to what bootstrap saw. This isn't
// perfect -- we might actually want to see something from Cargo's added library paths -- but
// for now it works.
pub fn restore_library_path() {
let key = tracked_env_var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR");
if let Some(env) = tracked_env_var_os("REAL_LIBRARY_PATH") {
env::set_var(&key, &env);
} else {
env::remove_var(&key);
}
}
/// Run the command, printing what we are running.
pub fn run_verbose(cmd: &mut Command) {
println!("running: {:?}", cmd);
run(cmd);
}
pub fn run(cmd: &mut Command) {
if !try_run(cmd) {
std::process::exit(1);
}
}
pub fn try_run(cmd: &mut Command) -> bool {
let status = match cmd.status() {
Ok(status) => status,
2019-12-22 23:42:04 +01:00
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
};
if !status.success() {
2018-03-25 16:20:56 +02:00
println!(
"\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n",
cmd, status
);
}
status.success()
}
pub fn run_suppressed(cmd: &mut Command) {
if !try_run_suppressed(cmd) {
std::process::exit(1);
}
}
pub fn try_run_suppressed(cmd: &mut Command) -> bool {
rustbuild: Add support for compiling Cargo This commit adds support to rustbuild for compiling Cargo as part of the release process. Previously rustbuild would simply download a Cargo snapshot and repackage it. With this change we should be able to turn off artifacts from the rust-lang/cargo repository and purely rely on the artifacts Cargo produces here. The infrastructure added here is intended to be extensible to other components, such as the RLS. It won't exactly be a one-line addition, but the addition of Cargo didn't require too much hooplah anyway. The process for release Cargo will now look like: * The rust-lang/rust repository has a Cargo submodule which is used to build a Cargo to pair with the rust-lang/rust release * Periodically we'll update the cargo submodule as necessary on rust-lang/rust's master branch * When branching beta we'll create a new branch of Cargo (as we do today), and the first commit to the beta branch will be to update the Cargo submodule to this exact revision. * When branching stable, we'll ensure that the Cargo submodule is updated and then make a stable release. Backports to Cargo will look like: * Send a PR to cargo's master branch * Send a PR to cargo's release branch (e.g. rust-1.16.0) * Send a PR to rust-lang/rust's beta branch updating the submodule * Eventually send a PR to rust-lang/rust's master branch updating the submodule For reference, the process to add a new component to the rust-lang/rust release would look like: * Add `$foo` as a submodule in `src/tools` * Add a `tool-$foo` step which compiles `$foo` with the specified compiler, likely mirroring what Cargo does. * Add a `dist-$foo` step which uses `src/tools/$foo` and the `tool-$foo` output to create a rust-installer package for `$foo` likely mirroring what Cargo does. * Update the `dist-extended` step with a new dependency on `dist-$foo` * Update `src/tools/build-manifest` for the new component.
2017-02-16 00:57:06 +01:00
let output = match cmd.output() {
Ok(status) => status,
2019-12-22 23:42:04 +01:00
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
rustbuild: Add support for compiling Cargo This commit adds support to rustbuild for compiling Cargo as part of the release process. Previously rustbuild would simply download a Cargo snapshot and repackage it. With this change we should be able to turn off artifacts from the rust-lang/cargo repository and purely rely on the artifacts Cargo produces here. The infrastructure added here is intended to be extensible to other components, such as the RLS. It won't exactly be a one-line addition, but the addition of Cargo didn't require too much hooplah anyway. The process for release Cargo will now look like: * The rust-lang/rust repository has a Cargo submodule which is used to build a Cargo to pair with the rust-lang/rust release * Periodically we'll update the cargo submodule as necessary on rust-lang/rust's master branch * When branching beta we'll create a new branch of Cargo (as we do today), and the first commit to the beta branch will be to update the Cargo submodule to this exact revision. * When branching stable, we'll ensure that the Cargo submodule is updated and then make a stable release. Backports to Cargo will look like: * Send a PR to cargo's master branch * Send a PR to cargo's release branch (e.g. rust-1.16.0) * Send a PR to rust-lang/rust's beta branch updating the submodule * Eventually send a PR to rust-lang/rust's master branch updating the submodule For reference, the process to add a new component to the rust-lang/rust release would look like: * Add `$foo` as a submodule in `src/tools` * Add a `tool-$foo` step which compiles `$foo` with the specified compiler, likely mirroring what Cargo does. * Add a `dist-$foo` step which uses `src/tools/$foo` and the `tool-$foo` output to create a rust-installer package for `$foo` likely mirroring what Cargo does. * Update the `dist-extended` step with a new dependency on `dist-$foo` * Update `src/tools/build-manifest` for the new component.
2017-02-16 00:57:06 +01:00
};
if !output.status.success() {
2018-03-25 16:20:56 +02:00
println!(
"\n\ncommand did not execute successfully: {:?}\n\
expected success, got: {}\n\n\
stdout ----\n{}\n\
stderr ----\n{}\n\n",
cmd,
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
output.status.success()
rustbuild: Add support for compiling Cargo This commit adds support to rustbuild for compiling Cargo as part of the release process. Previously rustbuild would simply download a Cargo snapshot and repackage it. With this change we should be able to turn off artifacts from the rust-lang/cargo repository and purely rely on the artifacts Cargo produces here. The infrastructure added here is intended to be extensible to other components, such as the RLS. It won't exactly be a one-line addition, but the addition of Cargo didn't require too much hooplah anyway. The process for release Cargo will now look like: * The rust-lang/rust repository has a Cargo submodule which is used to build a Cargo to pair with the rust-lang/rust release * Periodically we'll update the cargo submodule as necessary on rust-lang/rust's master branch * When branching beta we'll create a new branch of Cargo (as we do today), and the first commit to the beta branch will be to update the Cargo submodule to this exact revision. * When branching stable, we'll ensure that the Cargo submodule is updated and then make a stable release. Backports to Cargo will look like: * Send a PR to cargo's master branch * Send a PR to cargo's release branch (e.g. rust-1.16.0) * Send a PR to rust-lang/rust's beta branch updating the submodule * Eventually send a PR to rust-lang/rust's master branch updating the submodule For reference, the process to add a new component to the rust-lang/rust release would look like: * Add `$foo` as a submodule in `src/tools` * Add a `tool-$foo` step which compiles `$foo` with the specified compiler, likely mirroring what Cargo does. * Add a `dist-$foo` step which uses `src/tools/$foo` and the `tool-$foo` output to create a rust-installer package for `$foo` likely mirroring what Cargo does. * Update the `dist-extended` step with a new dependency on `dist-$foo` * Update `src/tools/build-manifest` for the new component.
2017-02-16 00:57:06 +01:00
}
pub fn gnu_target(target: &str) -> &str {
match target {
"i686-pc-windows-msvc" => "i686-pc-win32",
"x86_64-pc-windows-msvc" => "x86_64-pc-win32",
"i686-pc-windows-gnu" => "i686-w64-mingw32",
"x86_64-pc-windows-gnu" => "x86_64-w64-mingw32",
s => s,
}
}
pub fn make(host: &str) -> PathBuf {
2019-12-22 23:42:04 +01:00
if host.contains("dragonfly")
|| host.contains("freebsd")
|| host.contains("netbsd")
|| host.contains("openbsd")
2018-03-25 16:20:56 +02:00
{
PathBuf::from("gmake")
} else {
PathBuf::from("make")
}
}
pub fn output(cmd: &mut Command) -> String {
let output = match cmd.stderr(Stdio::inherit()).output() {
Ok(status) => status,
2019-12-22 23:42:04 +01:00
Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
};
if !output.status.success() {
2018-03-25 16:20:56 +02:00
panic!(
"command did not execute successfully: {:?}\n\
expected success, got: {}",
cmd, output.status
);
}
String::from_utf8(output.stdout).unwrap()
}
pub fn rerun_if_changed_anything_in_dir(dir: &Path) {
2019-12-22 23:42:04 +01:00
let mut stack = dir
.read_dir()
2018-03-25 16:20:56 +02:00
.unwrap()
.map(|e| e.unwrap())
.filter(|e| &*e.file_name() != ".git")
.collect::<Vec<_>>();
while let Some(entry) = stack.pop() {
let path = entry.path();
if entry.file_type().unwrap().is_dir() {
stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
} else {
println!("cargo:rerun-if-changed={}", path.display());
}
}
}
/// Returns the last-modified time for `path`, or zero if it doesn't exist.
2018-04-01 04:32:40 +02:00
pub fn mtime(path: &Path) -> SystemTime {
2019-12-22 23:42:04 +01:00
fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
}
2019-02-08 14:53:55 +01:00
/// Returns `true` if `dst` is up to date given that the file or files in `src`
/// are used to generate it.
///
/// Uses last-modified time checks to verify this.
pub fn up_to_date(src: &Path, dst: &Path) -> bool {
if !dst.exists() {
return false;
}
let threshold = mtime(dst);
let meta = match fs::metadata(src) {
Ok(meta) => meta,
Err(e) => panic!("source {:?} failed to get metadata: {}", src, e),
};
if meta.is_dir() {
2018-04-01 04:32:40 +02:00
dir_up_to_date(src, threshold)
} else {
2018-04-01 04:32:40 +02:00
meta.modified().unwrap_or(UNIX_EPOCH) <= threshold
}
}
2018-04-01 04:32:40 +02:00
fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
let meta = t!(e.metadata());
if meta.is_dir() {
dir_up_to_date(&e.path(), threshold)
} else {
2018-04-01 04:32:40 +02:00
meta.modified().unwrap_or(UNIX_EPOCH) < threshold
}
})
}
fn fail(s: &str) -> ! {
println!("\n\n{}\n\n", s);
std::process::exit(1);
}