diff --git a/.cargo/config.toml b/.cargo/config.toml index 07eeae72..35049cbc 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,16 +1,2 @@ -[unstable] -build-std = ["core", "alloc"] -build-std-features = ["compiler-builtins-mem"] - -[target.x86_64-unknown-hermit-loader] -rustflags = [ - "-C", "link-arg=-Tsrc/arch/x86_64/link.ld" -] - -[target.aarch64-unknown-hermit-loader] -rustflags = [ - "-C", "link-arg=-Tsrc/arch/aarch64/link.ld" -] - -[build] -target = "targets/x86_64-unknown-hermit-loader.json" +[alias] +xtask = "run --package xtask --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19b95e27..34652100 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: sudo apt-get install nasm - uses: actions/checkout@v3 - name: Clippy - run: cargo clippy + run: cargo xtask clippy env: RUSTFLAGS: -Dwarnings @@ -55,18 +55,20 @@ jobs: run: | brew update brew install qemu nasm - - name: Install QEMU, NASM, GNU make (windows) + - name: Install QEMU, NASM (windows) if: matrix.os == 'windows-latest' run: | choco install qemu --version 2021.12.15 echo "C:\Program Files\qemu" >> $GITHUB_PATH - choco install nasm make + choco install nasm echo "C:\Program Files\NASM" >> $GITHUB_PATH - uses: actions/checkout@v3 with: lfs: true - name: Build - run: make arch=${{ matrix.arch }} + run: cargo xtask build --arch ${{ matrix.arch }} + env: + HERMIT_APP: ${{ github.workspace }}/data/${{ matrix.arch }}/hello_world - name: Run loader (x86_64) if: matrix.arch == 'x86_64' run: | @@ -75,7 +77,7 @@ jobs: -smp 1 -m 64M \ -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ -display none -serial stdio \ - -kernel target/x86_64-unknown-hermit-loader/debug/rusty-loader \ + -kernel target/x86_64/debug/rusty-loader \ -initrd data/x86_64/hello_world - name: Run loader (aarch64) if: matrix.arch == 'aarch64' @@ -83,9 +85,11 @@ jobs: qemu-system-aarch64 \ -machine virt,gic-version=max -cpu max -smp 1 -m 512M \ -display none -serial stdio -semihosting \ - -kernel target/aarch64-unknown-hermit-loader/debug/rusty-loader + -kernel target/aarch64/debug/rusty-loader - name: Build (release) - run: make arch=${{ matrix.arch }} release=1 + run: cargo xtask build --arch ${{ matrix.arch }} --release + env: + HERMIT_APP: ${{ github.workspace }}/data/${{ matrix.arch }}/hello_world - name: Run loader (release, x86_64) if: matrix.arch == 'x86_64' run: | @@ -94,7 +98,7 @@ jobs: -smp 1 -m 64M \ -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ -display none -serial stdio \ - -kernel target/x86_64-unknown-hermit-loader/release/rusty-loader \ + -kernel target/x86_64/release/rusty-loader \ -initrd data/x86_64/hello_world - name: Run loader (release, aarch64) if: matrix.arch == 'aarch64' @@ -102,7 +106,7 @@ jobs: qemu-system-aarch64 \ -machine virt,gic-version=max -cpu max -smp 1 -m 512M \ -display none -serial stdio -semihosting \ - -kernel target/aarch64-unknown-hermit-loader/release/rusty-loader + -kernel target/aarch64/release/rusty-loader diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index acf6fb33..6a421d11 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,10 +32,10 @@ build:loader: stage: build image: ${CI_REGISTRY_IMAGE} script: - - make release=1 + - cargo xtask build --arch x86_64 --release artifacts: paths: - - target/x86_64-unknown-hermit-loader/release/rusty-loader + - target/x86_64/release/rusty-loader .deploy:loader: &loader stage: deploy @@ -51,7 +51,7 @@ build:loader: cat << END > ${DOCKER_FILE} FROM alpine:3.14 RUN apk add --no-cache qemu-system-x86_64 qemu-modules - ADD target/x86_64-unknown-hermit-loader/release/rusty-loader /hermit/ + ADD target/x86_64/release/rusty-loader /hermit/ END - docker build -f ${DOCKER_FILE} -t ${DOCKER_IMAGE}/loader:latest . - docker push ${DOCKER_IMAGE}/loader:latest diff --git a/Cargo.lock b/Cargo.lock index da50e6bc..e01ed190 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" +[[package]] +name = "anyhow" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" + [[package]] name = "bit_field" version = "0.10.1" @@ -112,6 +118,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rusty-loader" version = "0.2.6" @@ -144,6 +159,12 @@ dependencies = [ "syn", ] +[[package]] +name = "semver" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" + [[package]] name = "static-alloc" version = "0.2.3" @@ -186,3 +207,43 @@ dependencies = [ "bitflags", "raw-cpuid", ] + +[[package]] +name = "xflags" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f14fe1ed41a5a2b5ef3f565586c4a8a559ee55d3953faab360a771135bdee00" +dependencies = [ + "xflags-macros", +] + +[[package]] +name = "xflags-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d11d5fc2a97287eded8b170ca80533b3c42646dd7fa386a5eb045817921022" + +[[package]] +name = "xshell" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4884417669886d3abff14feec797179526ade713f212e54ec08b19bc6bdc86aa" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d92065701c3611323f96eac5475b995421fc7eb2bcba1336cdd80b9b2fb68f" + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "rustc_version", + "xflags", + "xshell", +] diff --git a/Cargo.toml b/Cargo.toml index 3154a24a..ac43fa46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,8 @@ opt-level = 1 # `opt-level = 0` makes bootloader to large for bootstrapping strip = "debuginfo" lto = true codegen-units = 1 + +[workspace] +members = [ + "xtask", +] diff --git a/Makefile b/Makefile deleted file mode 100644 index e973e785..00000000 --- a/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -arch ?= x86_64 -target ?= $(arch)-unknown-hermit -release ?= 0 - -opt := -rdir := debug - -ifeq ($(release), 1) -opt := --release -rdir := release -endif - -# Todo - make this feature toggleable -ifeq ($(arch), aarch64) -export HERMIT_APP ?= $(PWD)/data/$(arch)/hello_world -endif - -CONVERT := -RN := -ifdef COMSPEC -RM := del -else -RM := rm -rf -endif -SYSROOT := $(shell rustc --print sysroot) -OBJCOPY := $(shell find $(SYSROOT) -name llvm-objcopy -o -name llvm-objcopy.exe) -ifeq ($(arch), x86_64) -CONVERT := $(OBJCOPY) -O elf32-i386 target/$(target)-loader/$(rdir)/rusty-loader -endif - -.PHONY: all loader clean docs - -all: loader - -clean: - @cargo clean - -docs: - @echo DOC - @cargo doc - -loader: - @echo Build loader - echo "hermit app: $(HERMIT_APP)" - cargo build $(opt) -Z build-std=core,alloc -Z build-std-features=compiler-builtins-mem --target targets/$(target)-loader.json - $(CONVERT) diff --git a/README.md b/README.md index 153dedcd..58681361 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ You also need `nasm` and `ar` installed on your machine. Afterwards, the loader can be built as follows: ```bash -$ make +$ cargo xtask build --arch x86_64 ``` -Afterwards, the loader is stored in `target/x86_64-unknown-hermit-loader/debug/` as `rusty-loader`. +Afterwards, the loader is stored in `target/x86_64/debug/` as `rusty-loader`. As final step the unikernel application `app` can be booted with following command: ```bash diff --git a/xtask/.gitignore b/xtask/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/xtask/.gitignore @@ -0,0 +1 @@ +/target diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 00000000..1068a500 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +rustc_version = "0.4" +xflags = "0.2" +xshell = "0.2" diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs new file mode 100644 index 00000000..59ddbebb --- /dev/null +++ b/xtask/src/flags.rs @@ -0,0 +1,75 @@ +use std::path::PathBuf; + +xflags::xflags! { + src "./src/flags.rs" + + /// Run custom build command. + cmd xtask { + default cmd help { + /// Print help information. + optional -h, --help + } + + /// Build the kernel. + cmd build + { + /// Build for the architecture. + required --arch arch: String + /// Directory for all generated artifacts. + optional --target-dir target_dir: PathBuf + /// Build artifacts in release mode, with optimizations. + optional -r, --release + /// Build artifacts with the specified profile. + optional --profile profile: String + } + + /// Run clippy for all targets. + cmd clippy {} + } +} + +// generated start +// The following code is generated by `xflags` macro. +// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. +#[derive(Debug)] +pub struct Xtask { + pub subcommand: XtaskCmd, +} + +#[derive(Debug)] +pub enum XtaskCmd { + Help(Help), + Build(Build), + Clippy(Clippy), +} + +#[derive(Debug)] +pub struct Help { + pub help: bool, +} + +#[derive(Debug)] +pub struct Build { + pub arch: String, + pub target_dir: Option, + pub release: bool, + pub profile: Option, +} + +#[derive(Debug)] +pub struct Clippy; + +impl Xtask { + pub const HELP: &'static str = Self::HELP_; + + #[allow(dead_code)] + pub fn from_env() -> xflags::Result { + Self::from_env_() + } + + #[allow(dead_code)] + pub fn from_vec(args: Vec) -> xflags::Result { + Self::from_vec_(args) + } +} +// generated end diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 00000000..24522a61 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,214 @@ +//! See . + +mod flags; +mod rustc; + +use std::{ + env::{self, VarError}, + ffi::OsString, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, Result}; +use xshell::{cmd, Shell}; + +fn main() -> Result<()> { + flags::Xtask::from_env()?.run() +} + +impl flags::Xtask { + fn run(self) -> Result<()> { + match self.subcommand { + flags::XtaskCmd::Help(_) => { + println!("{}", flags::Xtask::HELP); + Ok(()) + } + flags::XtaskCmd::Build(build) => build.run(), + flags::XtaskCmd::Clippy(clippy) => clippy.run(), + } + } +} + +impl flags::Build { + fn run(self) -> Result<()> { + let sh = sh()?; + + eprintln!("Building loader"); + cmd!(sh, "cargo build") + .env("CARGO_ENCODED_RUSTFLAGS", self.cargo_encoded_rustflags()?) + .args(target_args(&self.arch)?) + .args(self.target_dir_args()) + .args(self.release_args()) + .args(self.profile_args()) + .run()?; + + eprintln!("Converting binary"); + self.convert_binary()?; + + eprintln!("Loader available at {}", self.dist_binary().display()); + Ok(()) + } + + fn cargo_encoded_rustflags(&self) -> Result { + let outer_rustflags = match env::var("CARGO_ENCODED_RUSTFLAGS") { + Ok(s) => Some(s), + Err(VarError::NotPresent) => None, + Err(err) => return Err(err.into()), + }; + + let mut rustflags = outer_rustflags.map(|s| vec![s]).unwrap_or_default(); + let arch = self.arch.as_str(); + rustflags.push(format!("-Clink-arg=-Tsrc/arch/{arch}/link.ld")); + Ok(rustflags.join("\x1f")) + } + + fn target_dir_args(&self) -> Vec { + vec!["--target-dir".into(), self.target_dir().into()] + } + + fn release_args(&self) -> &[&str] { + if self.release { + &["--release"] + } else { + &[] + } + } + + fn profile_args(&self) -> Vec<&str> { + match self.profile.as_deref() { + Some(profile) => vec!["--profile", profile], + None => vec![], + } + } + + fn convert_binary(&self) -> Result<()> { + let sh = sh()?; + + let input = self.build_binary(); + let output = self.dist_binary(); + sh.create_dir(output.parent().unwrap())?; + sh.copy_file(&input, &output)?; + + let objcopy = binutil("objcopy")?; + + if self.arch == "x86_64" { + cmd!(sh, "{objcopy} -O elf32-i386 {output}").run()?; + } + + Ok(()) + } + + fn profile(&self) -> &str { + self.profile + .as_deref() + .unwrap_or(if self.release { "release" } else { "dev" }) + } + + fn target_dir(&self) -> &Path { + self.target_dir + .as_deref() + .unwrap_or_else(|| Path::new("target")) + } + + fn out_dir(&self) -> PathBuf { + let mut out_dir = self.target_dir().to_path_buf(); + out_dir.push(target(&self.arch).unwrap()); + out_dir.push(match self.profile() { + "dev" => "debug", + profile => profile, + }); + out_dir + } + + fn dist_dir(&self) -> PathBuf { + let mut out_dir = self.target_dir().to_path_buf(); + out_dir.push(&self.arch); + out_dir.push(match self.profile() { + "dev" => "debug", + profile => profile, + }); + out_dir + } + + fn build_binary(&self) -> PathBuf { + let mut build_binary = self.out_dir(); + build_binary.push("rusty-loader"); + build_binary + } + + fn dist_binary(&self) -> PathBuf { + let mut dist_binary = self.dist_dir(); + dist_binary.push("rusty-loader"); + dist_binary + } +} + +impl flags::Clippy { + fn run(self) -> Result<()> { + let sh = sh()?; + + // TODO: Enable clippy for aarch64 + // https://github.com/hermitcore/rusty-loader/issues/78 + #[allow(clippy::single_element_loop)] + for target in ["x86_64"] { + let target_arg = target_args(target)?; + let hermit_app = { + let mut hermit_app = project_root().to_path_buf(); + hermit_app.push("data"); + hermit_app.push(target); + hermit_app.push("hello_world"); + hermit_app + }; + cmd!(sh, "cargo clippy {target_arg...}") + .env("HERMIT_APP", &hermit_app) + .run()?; + } + + cmd!(sh, "cargo clippy --package xtask").run()?; + + Ok(()) + } +} + +fn target(arch: &str) -> Result<&'static str> { + match arch { + "x86_64" => Ok("x86_64-unknown-hermit-loader"), + "aarch64" => Ok("aarch64-unknown-hermit-loader"), + arch => Err(anyhow!("Unsupported arch: {arch}")), + } +} + +fn target_args(arch: &str) -> Result<&'static [&'static str]> { + match arch { + "x86_64" => Ok(&[ + "--target=targets/x86_64-unknown-hermit-loader.json", + "-Zbuild-std=core,alloc", + "-Zbuild-std-features=compiler-builtins-mem", + ]), + "aarch64" => Ok(&[ + "--target=targets/aarch64-unknown-hermit-loader.json", + "-Zbuild-std=core,alloc", + "-Zbuild-std-features=compiler-builtins-mem", + ]), + arch => Err(anyhow!("Unsupported arch: {arch}")), + } +} + +fn binutil(name: &str) -> Result { + let exe_suffix = env::consts::EXE_SUFFIX; + let exe = format!("llvm-{name}{exe_suffix}"); + + let mut path = rustc::rustlib()?; + path.push(exe); + Ok(path) +} + +fn sh() -> Result { + let sh = Shell::new()?; + sh.change_dir(project_root()); + Ok(sh) +} + +fn project_root() -> &'static Path { + Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() +} diff --git a/xtask/src/rustc.rs b/xtask/src/rustc.rs new file mode 100644 index 00000000..632cd91c --- /dev/null +++ b/xtask/src/rustc.rs @@ -0,0 +1,25 @@ +//! Taken from . + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +use anyhow::Result; + +pub fn sysroot() -> Result { + let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); + let output = Command::new(rustc).arg("--print").arg("sysroot").output()?; + // Note: We must trim() to remove the `\n` from the end of stdout + Ok(String::from_utf8(output.stdout)?.trim().to_owned()) +} + +// See: https://github.com/rust-lang/rust/blob/564758c4c329e89722454dd2fbb35f1ac0b8b47c/src/bootstrap/dist.rs#L2334-L2341 +pub fn rustlib() -> Result { + let sysroot = sysroot()?; + let mut pathbuf = PathBuf::from(sysroot); + pathbuf.push("lib"); + pathbuf.push("rustlib"); + pathbuf.push(rustc_version::version_meta()?.host); // TODO: Prevent calling rustc_version::version_meta() multiple times + pathbuf.push("bin"); + Ok(pathbuf) +}