diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d2de177..1c42ab5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,8 +62,6 @@ jobs: - name: "Install Rustup Components" run: rustup component add rust-src llvm-tools-preview - - name: "Install cargo-xbuild" - run: cargo install cargo-xbuild --debug # install QEMU - name: Install QEMU (Linux) @@ -103,20 +101,20 @@ jobs: shell: bash {0} working-directory: example-kernels - - name: 'Run `cargo xrun` for "runner" kernel' + - name: 'Run `cargo run` for "runner" kernel' run: | - cargo xrun + cargo run if [ $? -eq 109 ]; then (exit 0); else (exit 1); fi shell: bash {0} working-directory: example-kernels/runner - - run: cargo xtest + - run: cargo test working-directory: example-kernels/runner-test - name: 'Run `cargo xtest` for "runner-test" kernel' + name: 'Run `cargo test` for "runner-test" kernel' - - run: cargo xtest -Z doctest-xcompile + - run: cargo test -Z doctest-xcompile working-directory: example-kernels/runner-doctest - name: 'Run `cargo xtest -Z doctest-xcompile` for "runner-doctest" kernel' + name: 'Run `cargo test -Z doctest-xcompile` for "runner-doctest" kernel' check_formatting: name: "Check Formatting" diff --git a/Readme.md b/Readme.md index 9757b45..be79a2e 100644 --- a/Readme.md +++ b/Readme.md @@ -31,7 +31,7 @@ Now you can build the kernel project and create a bootable disk image from it by cargo bootimage --target your_custom_target.json [other_args] ``` -The command will invoke [`cargo xbuild`](https://github.com/rust-osdev/cargo-xbuild), forwarding all passed options. Then it will build the specified bootloader together with the kernel to create a bootable disk image. +The command will invoke `cargo build`, forwarding all passed options. Then it will build the specified bootloader together with the kernel to create a bootable disk image. ### Running @@ -60,6 +60,10 @@ Configuration is done through a through a `[package.metadata.bootimage]` table i ```toml [package.metadata.bootimage] +# The cargo subcommand that will be used for building the kernel. +# +# For building using the `cargo-xbuild` crate, set this to `xbuild`. +build-command = ["build"] # The command invoked with the created bootimage (the "{}" will be replaced # with the path to the bootable disk image) # Applies to `bootimage run` and `bootimage runner` diff --git a/example-kernels/.cargo/config.toml b/example-kernels/.cargo/config.toml new file mode 100644 index 0000000..92cee48 --- /dev/null +++ b/example-kernels/.cargo/config.toml @@ -0,0 +1,2 @@ +[unstable] +build-std = ["core", "compiler_builtins"] diff --git a/example-kernels/Cargo.lock b/example-kernels/Cargo.lock index 27d7cb0..514e054 100644 --- a/example-kernels/Cargo.lock +++ b/example-kernels/Cargo.lock @@ -4,7 +4,7 @@ name = "basic" version = "0.1.0" dependencies = [ - "bootloader 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -20,14 +20,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bootloader" -version = "0.9.3" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rlibc" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "runner" version = "0.1.0" dependencies = [ - "bootloader 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -35,7 +43,7 @@ dependencies = [ name = "runner-doctest" version = "0.1.0" dependencies = [ - "bootloader 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -43,7 +51,7 @@ dependencies = [ name = "runner-test" version = "0.1.0" dependencies = [ - "bootloader 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -59,5 +67,6 @@ dependencies = [ [metadata] "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum bootloader 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "44ac0bdf4930c3c4d7f0d04eb6f15d7dcb9d5972b1ff9cd2bee0128112260fc7" +"checksum bootloader 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1e54086033cef68ed4b1729aeb1383a4d2e7d9bacd70db747a0a0bc0a420c831" +"checksum rlibc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" "checksum x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" diff --git a/example-kernels/basic/Cargo.toml b/example-kernels/basic/Cargo.toml index 60e18ca..76ddf5d 100644 --- a/example-kernels/basic/Cargo.toml +++ b/example-kernels/basic/Cargo.toml @@ -5,5 +5,5 @@ authors = ["Philipp Oppermann "] edition = "2018" [dependencies] -bootloader = "0.9.3" +bootloader = "0.9.7" x86_64 = "0.11.0" diff --git a/example-kernels/runner-doctest/Cargo.toml b/example-kernels/runner-doctest/Cargo.toml index 2e6dad8..d1610e2 100644 --- a/example-kernels/runner-doctest/Cargo.toml +++ b/example-kernels/runner-doctest/Cargo.toml @@ -5,8 +5,9 @@ authors = ["Philipp Oppermann "] edition = "2018" [dependencies] -bootloader = "0.9.3" +bootloader = "0.9.7" x86_64 = "0.11.0" +rlibc = "1.0.0" [package.metadata.bootimage] test-success-exit-code = 33 # (0x10 << 1) | 1 diff --git a/example-kernels/runner-doctest/src/lib.rs b/example-kernels/runner-doctest/src/lib.rs index 3cdb90f..437f374 100644 --- a/example-kernels/runner-doctest/src/lib.rs +++ b/example-kernels/runner-doctest/src/lib.rs @@ -4,6 +4,8 @@ #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] +extern crate rlibc; + /// add two numbers /// /// ``` diff --git a/example-kernels/runner-test/Cargo.toml b/example-kernels/runner-test/Cargo.toml index db005f8..854ceb9 100644 --- a/example-kernels/runner-test/Cargo.toml +++ b/example-kernels/runner-test/Cargo.toml @@ -9,8 +9,9 @@ name = "no-harness" harness = false [dependencies] -bootloader = "0.9.3" +bootloader = "0.9.7" x86_64 = "0.11.0" +rlibc = "1.0.0" [package.metadata.bootimage] test-success-exit-code = 33 # (0x10 << 1) | 1 diff --git a/example-kernels/runner-test/src/lib.rs b/example-kernels/runner-test/src/lib.rs index 64c2518..740e7a9 100644 --- a/example-kernels/runner-test/src/lib.rs +++ b/example-kernels/runner-test/src/lib.rs @@ -5,6 +5,8 @@ #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] +extern crate rlibc; + pub fn test_runner(tests: &[&dyn Fn()]) { for test in tests.iter() { test(); diff --git a/example-kernels/runner/Cargo.toml b/example-kernels/runner/Cargo.toml index 603ebc8..f1ab3db 100644 --- a/example-kernels/runner/Cargo.toml +++ b/example-kernels/runner/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Philipp Oppermann "] edition = "2018" [dependencies] -bootloader = "0.9.3" +bootloader = "0.9.7" x86_64 = "0.11.0" [package.metadata.bootimage] diff --git a/src/bin/cargo-bootimage.rs b/src/bin/cargo-bootimage.rs index 1737866..3ea1690 100644 --- a/src/bin/cargo-bootimage.rs +++ b/src/bin/cargo-bootimage.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use bootimage::{ args::{BuildArgs, BuildCommand}, builder::Builder, - help, + config, help, }; use std::{ env, @@ -43,9 +43,10 @@ pub fn main() -> Result<()> { fn build(args: BuildArgs) -> Result<()> { let mut builder = Builder::new(args.manifest_path().map(PathBuf::from))?; + let config = config::read_config(builder.manifest_path())?; let quiet = args.quiet(); - let executables = builder.build_kernel(&args.cargo_args(), quiet)?; + let executables = builder.build_kernel(&args.cargo_args(), &config, quiet)?; if executables.is_empty() { return Err(anyhow!("no executables built")); } diff --git a/src/builder/error.rs b/src/builder/error.rs index 86e6e14..5c0cab1 100644 --- a/src/builder/error.rs +++ b/src/builder/error.rs @@ -29,19 +29,19 @@ pub enum BuildKernelError { )] XbuildNotFound, - /// Running `cargo xbuild` failed. + /// Running `cargo build` failed. #[error("Kernel build failed.\nStderr: {}", String::from_utf8_lossy(.stderr))] - XbuildFailed { + BuildFailed { /// The standard error output. stderr: Vec, }, - /// The output of `cargo xbuild --message-format=json` was not valid UTF-8 + /// The output of `cargo build --message-format=json` was not valid UTF-8 #[error("Output of kernel build with --message-format=json is not valid UTF-8:\n{0}")] - XbuildJsonOutputInvalidUtf8(std::string::FromUtf8Error), - /// The output of `cargo xbuild --message-format=json` was not valid JSON + BuildJsonOutputInvalidUtf8(std::string::FromUtf8Error), + /// The output of `cargo build --message-format=json` was not valid JSON #[error("Output of kernel build with --message-format=json is not valid JSON:\n{0}")] - XbuildJsonOutputInvalidJson(json::Error), + BuildJsonOutputInvalidJson(json::Error), } /// Represents an error that occurred when creating a bootimage. @@ -59,7 +59,7 @@ pub enum CreateBootimageError { /// Building the bootloader failed #[error("Bootloader build failed.\nStderr: {}", String::from_utf8_lossy(.stderr))] BootloaderBuildFailed { - /// The `cargo xbuild` output to standard error + /// The `cargo build` output to standard error stderr: Vec, }, @@ -76,12 +76,12 @@ pub enum CreateBootimageError { error: io::Error, }, - /// The output of `cargo xbuild --message-format=json` was not valid UTF-8 + /// The output of `cargo build --message-format=json` was not valid UTF-8 #[error("Output of bootloader build with --message-format=json is not valid UTF-8:\n{0}")] - XbuildJsonOutputInvalidUtf8(std::string::FromUtf8Error), - /// The output of `cargo xbuild --message-format=json` was not valid JSON + BuildJsonOutputInvalidUtf8(std::string::FromUtf8Error), + /// The output of `cargo build --message-format=json` was not valid JSON #[error("Output of bootloader build with --message-format=json is not valid JSON:\n{0}")] - XbuildJsonOutputInvalidJson(json::Error), + BuildJsonOutputInvalidJson(json::Error), } /// There is something wrong with the bootloader dependency. diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 86afea6..46e9361 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -1,5 +1,6 @@ //! Provides functions to build the kernel and the bootloader. +use crate::config::Config; use cargo_metadata::Metadata; use error::{BootloaderError, BuildKernelError, BuilderError, CreateBootimageError}; use std::{ @@ -37,25 +38,26 @@ impl Builder { &self.manifest_path } - /// Builds the kernel by executing `cargo xbuild` with the given arguments. + /// Builds the kernel by executing `cargo build` with the given arguments. /// /// Returns a list of paths to all built executables. For crates with only a single binary, /// the returned list contains only a single element. /// /// If the quiet argument is set to true, all output to stdout is suppressed. pub fn build_kernel( - &self, + &mut self, args: &[String], + config: &Config, quiet: bool, ) -> Result, BuildKernelError> { if !quiet { println!("Building kernel"); } - // try to run cargo xbuild + // try to build kernel let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned()); let mut cmd = process::Command::new(&cargo); - cmd.arg("xbuild"); + cmd.args(&config.build_command); cmd.args(args); if !quiet { cmd.stdout(process::Stdio::inherit()); @@ -66,24 +68,26 @@ impl Builder { error: err, })?; if !output.status.success() { - // try executing `cargo xbuild --help` to check whether cargo-xbuild is installed - let mut help_command = process::Command::new("cargo"); - help_command.arg("xbuild").arg("--help"); - help_command.stdout(process::Stdio::null()); - help_command.stderr(process::Stdio::null()); - if let Ok(help_exit_status) = help_command.status() { - if !help_exit_status.success() { - return Err(BuildKernelError::XbuildNotFound); + if config.build_command.starts_with(&["xbuild".into()]) { + // try executing `cargo xbuild --help` to check whether cargo-xbuild is installed + let mut help_command = process::Command::new("cargo"); + help_command.arg("xbuild").arg("--help"); + help_command.stdout(process::Stdio::null()); + help_command.stderr(process::Stdio::null()); + if let Ok(help_exit_status) = help_command.status() { + if !help_exit_status.success() { + return Err(BuildKernelError::XbuildNotFound); + } } } - return Err(BuildKernelError::XbuildFailed { + return Err(BuildKernelError::BuildFailed { stderr: output.stderr, }); } // Retrieve binary paths let mut cmd = process::Command::new(cargo); - cmd.arg("xbuild"); + cmd.args(&config.build_command); cmd.args(args); cmd.arg("--message-format").arg("json"); let output = cmd.output().map_err(|err| BuildKernelError::Io { @@ -91,17 +95,17 @@ impl Builder { error: err, })?; if !output.status.success() { - return Err(BuildKernelError::XbuildFailed { + return Err(BuildKernelError::BuildFailed { stderr: output.stderr, }); } let mut executables = Vec::new(); for line in String::from_utf8(output.stdout) - .map_err(BuildKernelError::XbuildJsonOutputInvalidUtf8)? + .map_err(BuildKernelError::BuildJsonOutputInvalidUtf8)? .lines() { let mut artifact = - json::parse(line).map_err(BuildKernelError::XbuildJsonOutputInvalidJson)?; + json::parse(line).map_err(BuildKernelError::BuildJsonOutputInvalidJson)?; if let Some(executable) = artifact["executable"].take_string() { executables.push(PathBuf::from(executable)); } @@ -161,11 +165,11 @@ impl Builder { } let mut bootloader_elf_path = None; for line in String::from_utf8(output.stdout) - .map_err(CreateBootimageError::XbuildJsonOutputInvalidUtf8)? + .map_err(CreateBootimageError::BuildJsonOutputInvalidUtf8)? .lines() { let mut artifact = - json::parse(line).map_err(CreateBootimageError::XbuildJsonOutputInvalidJson)?; + json::parse(line).map_err(CreateBootimageError::BuildJsonOutputInvalidJson)?; if let Some(executable) = artifact["executable"].take_string() { if bootloader_elf_path .replace(PathBuf::from(executable)) diff --git a/src/config.rs b/src/config.rs index 44479d6..ed909e7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,6 +12,10 @@ use toml::Value; #[derive(Debug, Clone)] #[non_exhaustive] pub struct Config { + /// The cargo subcommand that is used for building the kernel for `cargo bootimage`. + /// + /// Defaults to `build`. + pub build_command: Vec, /// The run command that is invoked on `bootimage run` or `bootimage runner` /// /// The substring "{}" will be replaced with the path to the bootable disk image. @@ -75,35 +79,17 @@ fn read_config_inner(manifest_path: &Path) -> Result { ("test-success-exit-code", Value::Integer(exit_code)) => { config.test_success_exit_code = Some(exit_code as i32); } + ("build-command", Value::Array(array)) => { + config.build_command = Some(parse_string_array(array, "build-command")?); + } ("run-command", Value::Array(array)) => { - let mut command = Vec::new(); - for value in array { - match value { - Value::String(s) => command.push(s), - _ => return Err(anyhow!("run-command must be a list of strings")), - } - } - config.run_command = Some(command); + config.run_command = Some(parse_string_array(array, "run-command")?); } ("run-args", Value::Array(array)) => { - let mut args = Vec::new(); - for value in array { - match value { - Value::String(s) => args.push(s), - _ => return Err(anyhow!("run-args must be a list of strings")), - } - } - config.run_args = Some(args); + config.run_args = Some(parse_string_array(array, "run-args")?); } ("test-args", Value::Array(array)) => { - let mut args = Vec::new(); - for value in array { - match value { - Value::String(s) => args.push(s), - _ => return Err(anyhow!("test-args must be a list of strings")), - } - } - config.test_args = Some(args); + config.test_args = Some(parse_string_array(array, "test-args")?); } (key, value) => { return Err(anyhow!( @@ -118,8 +104,20 @@ fn read_config_inner(manifest_path: &Path) -> Result { Ok(config.into()) } +fn parse_string_array(array: Vec, prop_name: &str) -> Result> { + let mut parsed = Vec::new(); + for value in array { + match value { + Value::String(s) => parsed.push(s), + _ => return Err(anyhow!("{} must be a list of strings", prop_name)), + } + } + Ok(parsed) +} + #[derive(Default)] struct ConfigBuilder { + build_command: Option>, run_command: Option>, run_args: Option>, test_args: Option>, @@ -130,6 +128,7 @@ struct ConfigBuilder { impl Into for ConfigBuilder { fn into(self) -> Config { Config { + build_command: self.build_command.unwrap_or_else(|| vec!["build".into()]), run_command: self.run_command.unwrap_or_else(|| { vec![ "qemu-system-x86_64".into(), diff --git a/src/help/cargo_bootimage_help.txt b/src/help/cargo_bootimage_help.txt index f8cabbf..16647ef 100644 --- a/src/help/cargo_bootimage_help.txt +++ b/src/help/cargo_bootimage_help.txt @@ -11,3 +11,13 @@ BUILD_OPTS: is downloaded and built, and then combined with the kernel into a bootable disk image. +CONFIGURATION: + The behavior of `cargo bootimage` can be configured through a + `[package.metadata.bootimage]` table in the `Cargo.toml`. The + following options are available to configure the build behavior: + + [package.metadata.bootimage] + # The cargo subcommand that will be used for building the kernel. + # + # For building using the `cargo-xbuild` crate, set this to `xbuild`. + build-command = ["build"]