Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xtask): add ci subcommand #879

Merged
merged 11 commits into from
Sep 11, 2023
239 changes: 48 additions & 191 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

561 changes: 544 additions & 17 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 0 additions & 13 deletions fc-config.json

This file was deleted.

5 changes: 4 additions & 1 deletion xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ edition = "2021"

[dependencies]
anyhow = "1.0"
clap = { version = "4", features = ["derive"] }
goblin = { version = "0.7", default-features = false, features = ["archive", "elf32", "elf64", "std"] }
llvm-tools = "0.1"
xflags = "0.3"
sysinfo = "0.29"
ureq = "2"
wait-timeout = "0.2"
xshell = "0.2"
47 changes: 25 additions & 22 deletions xtask/src/arch.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
use std::str::FromStr;
use clap::ValueEnum;

use anyhow::anyhow;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// Target architecture.
#[derive(ValueEnum, Clone, Copy, PartialEq, Eq, Debug)]
#[value(rename_all = "snake_case")]
pub enum Arch {
/// x86-64
X86_64,
AArch64,
/// AArch64
Aarch64,
}

impl Arch {
pub fn name(&self) -> &'static str {
match self {
Self::X86_64 => "x86_64",
Self::AArch64 => "aarch64",
Self::Aarch64 => "aarch64",
}
}

pub fn triple(&self) -> &'static str {
match self {
Self::X86_64 => "x86_64-unknown-none",
Self::AArch64 => "aarch64-unknown-none-softfloat",
Self::Aarch64 => "aarch64-unknown-none-softfloat",
}
}

pub fn hermit_triple(&self) -> &'static str {
match self {
Arch::X86_64 => "x86_64-unknown-hermit",
Arch::AArch64 => "aarch64-unknown-hermit",
Arch::Aarch64 => "aarch64-unknown-hermit",
}
}

Expand All @@ -37,7 +39,7 @@ impl Arch {
"-Zbuild-std=core",
"-Zbuild-std-features=compiler-builtins-mem",
],
Arch::AArch64 => &[
Arch::Aarch64 => &[
"--target=aarch64-unknown-hermit",
"-Zbuild-std=core",
"-Zbuild-std-features=compiler-builtins-mem",
Expand All @@ -48,7 +50,7 @@ impl Arch {
pub fn cargo_args(&self) -> &'static [&'static str] {
match self {
Self::X86_64 => &["--target=x86_64-unknown-none"],
Self::AArch64 => &[
Self::Aarch64 => &[
"--target=aarch64-unknown-none-softfloat",
// We can't use prebuilt std for aarch64 because it is built with
// relocation-model=static and we need relocation-model=pic
Expand All @@ -58,22 +60,23 @@ impl Arch {
}
}

pub fn rustflags(&self) -> &'static [&'static str] {
pub fn ci_cargo_args(&self) -> &'static [&'static str] {
match self {
Self::X86_64 => &[],
Self::AArch64 => &["-Crelocation-model=pic"],
Arch::X86_64 => &[
"--target=x86_64-unknown-hermit",
"-Zbuild-std=std,panic_abort",
],
Arch::Aarch64 => &[
"--target=aarch64-unknown-hermit",
"-Zbuild-std=std,panic_abort",
],
}
}
}

impl FromStr for Arch {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"x86_64" => Ok(Self::X86_64),
"aarch64" => Ok(Self::AArch64),
s => Err(anyhow!("Unsupported arch: {s}")),
pub fn rustflags(&self) -> &'static [&'static str] {
match self {
Self::X86_64 => &[],
Self::Aarch64 => &["-Crelocation-model=pic"],
}
}
}
95 changes: 95 additions & 0 deletions xtask/src/artifact.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use std::path::{Path, PathBuf};

use clap::Args;

use crate::arch::Arch;
use crate::archive::Archive;

#[derive(Args)]
pub struct Artifact {
/// Target architecture.
#[arg(value_enum, long)]
pub arch: Arch,

/// Directory for all generated artifacts.
#[arg(long, id = "DIRECTORY")]
pub target_dir: Option<PathBuf>,

/// Build artifacts in release mode, with optimizations.
#[arg(short, long)]
pub release: bool,

/// Build artifacts with the specified profile.
#[arg(long, id = "PROFILE-NAME")]
pub profile: Option<String>,
}

impl Artifact {
pub fn profile(&self) -> &str {
self.profile
.as_deref()
.unwrap_or(if self.release { "release" } else { "dev" })
}

pub fn profile_path_component(&self) -> &str {
match self.profile() {
"dev" => "debug",
profile => profile,
}
}

pub fn target_dir(&self) -> &Path {
self.target_dir
.as_deref()
.unwrap_or_else(|| Path::new("target"))
}

pub fn builtins_archive(&self) -> Archive {
[
"hermit-builtins".as_ref(),
self.target_dir(),
self.arch.hermit_triple().as_ref(),
"release".as_ref(),
"libhermit_builtins.a".as_ref(),
]
.iter()
.collect::<PathBuf>()
.into()
}

pub fn build_archive(&self) -> Archive {
[
self.target_dir(),
self.arch.triple().as_ref(),
self.profile_path_component().as_ref(),
"libhermit.a".as_ref(),
]
.iter()
.collect::<PathBuf>()
.into()
}

pub fn dist_archive(&self) -> Archive {
[
self.target_dir(),
self.arch.name().as_ref(),
self.profile_path_component().as_ref(),
"libhermit.a".as_ref(),
]
.iter()
.collect::<PathBuf>()
.into()
}

pub fn ci_image(&self, package: &str) -> PathBuf {
[
"..".as_ref(),
self.target_dir(),
self.arch.hermit_triple().as_ref(),
self.profile_path_component().as_ref(),
package.as_ref(),
]
.iter()
.collect()
}
}
123 changes: 123 additions & 0 deletions xtask/src/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::env::{self, VarError};

use anyhow::Result;
use clap::Args;
use xshell::cmd;

use crate::cargo_build::{CargoBuild, CmdExt};

/// Build the kernel.
#[derive(Args)]
pub struct Build {
#[command(flatten)]
cargo_build: CargoBuild,

/// Enable the `-Z instrument-mcount` flag.
#[arg(long)]
pub instrument_mcount: bool,

/// Enable the `-Z randomize-layout` flag.
#[arg(long)]
pub randomize_layout: bool,
}

impl Build {
pub fn run(self) -> Result<()> {
let sh = crate::sh()?;

eprintln!("Building kernel");
cmd!(sh, "cargo build")
.env("CARGO_ENCODED_RUSTFLAGS", self.cargo_encoded_rustflags()?)
.args(self.cargo_build.artifact.arch.cargo_args())
.cargo_build_args(&self.cargo_build)
.run()?;

let build_archive = self.cargo_build.artifact.build_archive();
let dist_archive = self.cargo_build.artifact.dist_archive();
eprintln!(
"Copying {} to {}",
build_archive.as_ref().display(),
dist_archive.as_ref().display()
);
sh.create_dir(dist_archive.as_ref().parent().unwrap())?;
sh.copy_file(&build_archive, &dist_archive)?;

eprintln!("Exporting symbols");
self.export_syms()?;

eprintln!("Building hermit-builtins");
cmd!(sh, "cargo build --release")
.arg("--manifest-path=hermit-builtins/Cargo.toml")
.args(self.cargo_build.artifact.arch.builtins_cargo_args())
.target_dir_args(&self.cargo_build)
.run()?;

eprintln!("Exporting hermit-builtins symbols");
let builtins = self.cargo_build.artifact.builtins_archive();
let builtin_symbols = sh.read_file("hermit-builtins/exports")?;
builtins.retain_symbols(builtin_symbols.lines())?;

dist_archive.append(&builtins)?;

eprintln!("Setting OSABI");
dist_archive.set_osabi()?;

eprintln!("Kernel available at {}", dist_archive.as_ref().display());
Ok(())
}

fn cargo_encoded_rustflags(&self) -> Result<String> {
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
.as_deref()
.map(|s| vec![s])
.unwrap_or_default();

// TODO: Re-enable mutable-noalias
// https://github.com/hermitcore/kernel/issues/200
rustflags.push("-Zmutable-noalias=no");

if self.instrument_mcount {
rustflags.push("-Zinstrument-mcount");
}

if self.randomize_layout {
rustflags.push("-Zrandomize-layout")
}

rustflags.extend(self.cargo_build.artifact.arch.rustflags());

Ok(rustflags.join("\x1f"))
}

fn export_syms(&self) -> Result<()> {
let archive = self.cargo_build.artifact.dist_archive();

let syscall_symbols = archive.syscall_symbols()?;
let explicit_exports = [
"_start",
"__bss_start",
"runtime_entry",
// lwIP functions (C runtime)
"init_lwip",
"lwip_read",
"lwip_write",
// lwIP rtl8139 driver
"init_rtl8139_netif",
"irq_install_handler",
"virt_to_phys",
"eoi",
]
.into_iter();

let symbols = explicit_exports.chain(syscall_symbols.iter().map(String::as_str));

archive.retain_symbols(symbols)?;

Ok(())
}
}
Loading