diff --git a/Cargo.lock b/Cargo.lock index b734ce44ed..5b960274ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1876,7 +1876,6 @@ dependencies = [ "anyhow", "clap", "goblin", - "llvm-tools", "sysinfo", "ureq", "wait-timeout", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index dfb503850d..08019a3e47 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" anyhow = "1.0" clap = { version = "4", features = ["derive"] } goblin = { version = "0.8", default-features = false, features = ["archive", "elf32", "elf64", "std"] } -llvm-tools = "0.1" sysinfo = "0.31" ureq = "2" wait-timeout = "0.2" diff --git a/xtask/src/arch.rs b/xtask/src/arch.rs index 90da9898c3..76049091d8 100644 --- a/xtask/src/arch.rs +++ b/xtask/src/arch.rs @@ -1,6 +1,5 @@ use anyhow::Result; use clap::ValueEnum; -use xshell::cmd; /// Target architecture. #[derive(ValueEnum, Clone, Copy, PartialEq, Eq, Debug)] @@ -20,9 +19,13 @@ impl Arch { } pub fn install(&self) -> Result<()> { - let sh = crate::sh()?; - let triple = self.triple(); - cmd!(sh, "rustup target add {triple}").run()?; + let mut rustup = crate::rustup(); + rustup.args(["target", "add", self.triple()]); + + eprintln!("$ {rustup:?}"); + let status = rustup.status()?; + assert!(status.success()); + Ok(()) } diff --git a/xtask/src/archive.rs b/xtask/src/archive.rs index 9c11b6537c..de5a0dc375 100644 --- a/xtask/src/archive.rs +++ b/xtask/src/archive.rs @@ -49,7 +49,7 @@ impl Archive { }; let all_symbols = { - let nm = crate::binutil("nm")?; + let nm = crate::binutil("nm").unwrap(); let stdout = cmd!(sh, "{nm} --export-symbols {archive}").output()?.stdout; String::from_utf8(stdout)? }; @@ -73,7 +73,7 @@ impl Archive { let rename_path = archive.with_extension("redefine-syms"); sh.write_file(&rename_path, symbol_renames)?; - let objcopy = crate::binutil("objcopy")?; + let objcopy = crate::binutil("objcopy").unwrap(); cmd!(sh, "{objcopy} --redefine-syms={rename_path} {archive}").run()?; sh.remove_path(&rename_path)?; @@ -86,7 +86,7 @@ impl Archive { let archive = self.as_ref(); let file = file.as_ref(); - let ar = crate::binutil("ar")?; + let ar = crate::binutil("ar").unwrap(); cmd!(sh, "{ar} qL {archive} {file}").run()?; Ok(()) diff --git a/xtask/src/artifact.rs b/xtask/src/artifact.rs index c769c6baa2..2133f6abe3 100644 --- a/xtask/src/artifact.rs +++ b/xtask/src/artifact.rs @@ -1,9 +1,10 @@ -use std::path::{Path, PathBuf}; +use std::path::{self, PathBuf}; use clap::Args; use crate::arch::Arch; use crate::archive::Archive; +use crate::ci; #[derive(Args)] pub struct Artifact { @@ -15,6 +16,10 @@ pub struct Artifact { #[arg(long, id = "DIRECTORY")] pub target_dir: Option, + /// Copy final artifacts to this directory + #[arg(long, id = "PATH")] + pub artifact_dir: Option, + /// Build artifacts in release mode, with optimizations. #[arg(short, long)] pub release: bool, @@ -38,16 +43,21 @@ impl Artifact { } } - pub fn target_dir(&self) -> &Path { - self.target_dir - .as_deref() - .unwrap_or_else(|| Path::new("target")) + pub fn target_dir(&self) -> PathBuf { + if let Some(target_dir) = &self.target_dir { + return path::absolute(target_dir).unwrap(); + } + + crate::project_root().join("target") + } + + pub fn builtins_target_dir(&self) -> PathBuf { + self.target_dir().join("hermit-builtins") } pub fn builtins_archive(&self) -> Archive { [ - "hermit-builtins".as_ref(), - self.target_dir(), + self.builtins_target_dir().as_path(), self.arch.hermit_triple().as_ref(), "release".as_ref(), "libhermit_builtins.a".as_ref(), @@ -59,7 +69,7 @@ impl Artifact { pub fn build_archive(&self) -> Archive { [ - self.target_dir(), + self.target_dir().as_path(), self.arch.triple().as_ref(), self.profile_path_component().as_ref(), "libhermit.a".as_ref(), @@ -69,22 +79,28 @@ impl Artifact { .into() } - pub fn dist_archive(&self) -> Archive { + fn artifact_dir(&self) -> PathBuf { + if let Some(artifact_dir) = &self.artifact_dir { + return path::absolute(artifact_dir).unwrap(); + } + [ - self.target_dir(), + self.target_dir().as_path(), self.arch.name().as_ref(), self.profile_path_component().as_ref(), - "libhermit.a".as_ref(), ] .iter() - .collect::() - .into() + .collect() + } + + pub fn dist_archive(&self) -> Archive { + self.artifact_dir().join("libhermit.a").into() } pub fn ci_image(&self, package: &str) -> PathBuf { [ - "..".as_ref(), - self.target_dir(), + ci::parent_root(), + "target".as_ref(), self.arch.hermit_triple().as_ref(), self.profile_path_component().as_ref(), package.as_ref(), diff --git a/xtask/src/binutil.rs b/xtask/src/binutil.rs index e4ca73a886..be1fcbdbc3 100644 --- a/xtask/src/binutil.rs +++ b/xtask/src/binutil.rs @@ -1,22 +1,48 @@ +use std::io; use std::path::PathBuf; +use std::sync::LazyLock; -use anyhow::{anyhow, Result}; +pub fn binutil(name: &str) -> Option { + static LLVM_TOOLS: LazyLock = LazyLock::new(|| LlvmTools::new().unwrap()); -pub fn binutil(name: &str) -> Result { + LLVM_TOOLS.tool(name) +} + +struct LlvmTools { + bin: PathBuf, +} + +impl LlvmTools { + pub fn new() -> io::Result { + let mut rustc = crate::rustc(); + rustc.args(["--print", "sysroot"]); + + eprintln!("$ {rustc:?}"); + let output = rustc.output()?; + assert!(output.status.success()); + + let sysroot = String::from_utf8(output.stdout).unwrap(); + let rustlib = [sysroot.trim_end(), "lib", "rustlib"] + .iter() + .collect::(); + + let example_exe = exe("objdump"); + for entry in rustlib.read_dir()? { + let bin = entry?.path().join("bin"); + if bin.join(&example_exe).exists() { + return Ok(Self { bin }); + } + } + unreachable!() + } + + pub fn tool(&self, name: &str) -> Option { + let path = self.bin.join(exe(name)); + path.exists().then_some(path) + } +} + +fn exe(name: &str) -> String { let exe_suffix = std::env::consts::EXE_SUFFIX; - let exe = format!("llvm-{name}{exe_suffix}"); - - let path = llvm_tools::LlvmTools::new() - .map_err(|err| match err { - llvm_tools::Error::NotFound => anyhow!( - "Could not find llvm-tools component\n\ - \n\ - Maybe the rustup component `llvm-tools` is missing? Install it through: `rustup component add llvm-tools`" - ), - err => anyhow!("{err:?}"), - })? - .tool(&exe) - .ok_or_else(|| anyhow!("could not find {exe}"))?; - - Ok(path) + format!("llvm-{name}{exe_suffix}") } diff --git a/xtask/src/build.rs b/xtask/src/build.rs index dc4892eb92..faa488d0b1 100644 --- a/xtask/src/build.rs +++ b/xtask/src/build.rs @@ -3,7 +3,6 @@ use std::env::{self, VarError}; use anyhow::Result; use clap::Args; -use xshell::cmd; use crate::cargo_build::CargoBuild; @@ -34,13 +33,17 @@ impl Build { }; eprintln!("Building kernel"); - cmd!(sh, "cargo") + let mut cargo = crate::cargo(); + cargo .args(careful) .arg("build") .env("CARGO_ENCODED_RUSTFLAGS", self.cargo_encoded_rustflags()?) .args(self.cargo_build.artifact.arch.cargo_args()) - .args(self.cargo_build.cargo_build_args()) - .run()?; + .args(self.cargo_build.cargo_build_args()); + + eprintln!("$ {cargo:?}"); + let status = cargo.status()?; + assert!(status.success()); let build_archive = self.cargo_build.artifact.build_archive(); let dist_archive = self.cargo_build.artifact.dist_archive(); @@ -56,11 +59,16 @@ impl Build { self.export_syms()?; eprintln!("Building hermit-builtins"); - cmd!(sh, "cargo build --release") + let mut cargo = crate::cargo(); + cargo + .args(["build", "--release"]) .arg("--manifest-path=hermit-builtins/Cargo.toml") .args(self.cargo_build.artifact.arch.builtins_cargo_args()) - .args(self.cargo_build.target_dir_args()) - .run()?; + .args(self.cargo_build.builtins_target_dir_arg()); + + eprintln!("$ {cargo:?}"); + let status = cargo.status()?; + assert!(status.success()); eprintln!("Exporting hermit-builtins symbols"); let builtins = self.cargo_build.artifact.builtins_archive(); diff --git a/xtask/src/cargo_build.rs b/xtask/src/cargo_build.rs index 54f36b2ae4..5ce1cfde8e 100644 --- a/xtask/src/cargo_build.rs +++ b/xtask/src/cargo_build.rs @@ -1,3 +1,5 @@ +use std::ffi::OsString; + use clap::Args; use crate::artifact::Artifact; @@ -35,16 +37,27 @@ impl CargoBuild { } pub fn target_dir_args(&self) -> Vec { - if let Some(target_dir) = &self.artifact.target_dir { + if self.artifact.target_dir.is_some() { vec![ "--target-dir".to_string(), - target_dir.to_str().unwrap().to_string(), + self.artifact + .target_dir() + .into_os_string() + .into_string() + .unwrap(), ] } else { vec![] } } + pub fn builtins_target_dir_arg(&self) -> [OsString; 2] { + [ + OsString::from("--target-dir"), + self.artifact.builtins_target_dir().into_os_string(), + ] + } + fn release_args(&self) -> &'static [&'static str] { if self.artifact.release { &["--release"] diff --git a/xtask/src/ci/build.rs b/xtask/src/ci/build.rs index cc82db4dfb..4f80b20f41 100644 --- a/xtask/src/ci/build.rs +++ b/xtask/src/ci/build.rs @@ -1,6 +1,4 @@ -use std::env; use std::path::PathBuf; -use std::process::Command; use anyhow::Result; use clap::Args; @@ -25,7 +23,7 @@ impl Build { eprintln!("::group::cargo build") } - let mut cargo = cargo(); + let mut cargo = crate::cargo(); if self.package.contains("rftrace") { cargo.env( @@ -35,7 +33,7 @@ impl Build { }; cargo - .current_dir("..") + .current_dir(super::parent_root()) .arg("build") .args(self.cargo_build.artifact.arch.ci_cargo_args()) .args(self.cargo_build.cargo_build_args()) @@ -56,31 +54,3 @@ impl Build { self.cargo_build.artifact.ci_image(&self.package) } } - -fn cargo() -> Command { - let cargo = { - let exe = format!("cargo{}", env::consts::EXE_SUFFIX); - // On windows, the userspace toolchain ends up in front of the rustup proxy in $PATH. - // To reach the rustup proxy nonetheless, we explicitly query $CARGO_HOME. - let mut cargo_home = PathBuf::from(env::var_os("CARGO_HOME").unwrap()); - cargo_home.push("bin"); - cargo_home.push(&exe); - if cargo_home.exists() { - cargo_home - } else { - PathBuf::from(exe) - } - }; - - let mut cargo = Command::new(cargo); - - // Remove rust-toolchain-specific environment variables from kernel cargo - cargo.env_remove("LD_LIBRARY_PATH"); - env::vars() - .filter(|(key, _value)| key.starts_with("CARGO") || key.starts_with("RUST")) - .for_each(|(key, _value)| { - cargo.env_remove(&key); - }); - - cargo -} diff --git a/xtask/src/ci/mod.rs b/xtask/src/ci/mod.rs index 1a497241b3..02474d5a7e 100644 --- a/xtask/src/ci/mod.rs +++ b/xtask/src/ci/mod.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use anyhow::Result; use clap::Subcommand; @@ -29,3 +31,7 @@ impl Ci { fn in_ci() -> bool { std::env::var_os("CI") == Some("true".into()) } + +pub fn parent_root() -> &'static Path { + crate::project_root().parent().unwrap() +} diff --git a/xtask/src/ci/qemu.rs b/xtask/src/ci/qemu.rs index 5c1014a636..9503cdb5c6 100644 --- a/xtask/src/ci/qemu.rs +++ b/xtask/src/ci/qemu.rs @@ -390,7 +390,7 @@ fn check_rftrace(image: &Path) -> Result<()> { let sh = crate::sh()?; let image_name = image.file_name().unwrap().to_str().unwrap(); - let nm = crate::binutil("nm")?; + let nm = crate::binutil("nm").unwrap(); let symbols = cmd!(sh, "{nm} --numeric-sort {image}").output()?.stdout; sh.write_file(format!("shared/tracedir/{image_name}.sym"), symbols)?; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f8a6df8271..c0ac97827a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -10,7 +10,9 @@ mod ci; mod clippy; mod doc; -use std::path::Path; +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; use anyhow::Result; pub(crate) use binutil::binutil; @@ -44,7 +46,55 @@ fn main() -> Result<()> { pub fn sh() -> Result { let sh = Shell::new()?; - let project_root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap(); - sh.change_dir(project_root); + sh.change_dir(project_root()); Ok(sh) } + +pub fn project_root() -> &'static Path { + Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() +} + +pub fn rustup() -> Command { + sanitize("rustup") +} + +pub fn rustc() -> Command { + sanitize("rustc") +} + +pub fn cargo() -> Command { + sanitize("cargo") +} + +fn sanitize(cmd: &str) -> Command { + let cmd = { + let exe = format!("{cmd}{}", env::consts::EXE_SUFFIX); + // On windows, the userspace toolchain ends up in front of the rustup proxy in $PATH. + // To reach the rustup proxy nonetheless, we explicitly query $CARGO_HOME. + let mut cargo_home = PathBuf::from(env::var_os("CARGO_HOME").unwrap()); + cargo_home.push("bin"); + cargo_home.push(&exe); + if cargo_home.exists() { + cargo_home + } else { + PathBuf::from(exe) + } + }; + + let mut cmd = Command::new(cmd); + + cmd.current_dir(project_root()); + + // Remove rust-toolchain-specific environment variables from kernel cargo + cmd.env_remove("LD_LIBRARY_PATH"); + env::vars() + .filter(|(key, _value)| { + key.starts_with("CARGO") && !key.starts_with("CARGO_HOME") + || key.starts_with("RUST") && !key.starts_with("RUSTUP_HOME") + }) + .for_each(|(key, _value)| { + cmd.env_remove(&key); + }); + + cmd +}