xtask: add llvm/compiler-rt/libcxx/libcxxabi build

This commit is contained in:
Mark Poliakov 2024-11-18 20:51:16 +02:00
parent ac7727b8b1
commit bcaa09cc5d
10 changed files with 355 additions and 72 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target
/toolchain
/xtask.user.toml

View File

@ -0,0 +1,22 @@
if (NOT DEFINED ENV{CROSS_TOOLCHAIN_PREFIX})
message(FATAL_ERROR "CROSS_TOOLCHAIN_PREFIX=... must be specified.")
endif()
set(CROSS_TOOLCHAIN_PREFIX "$ENV{CROSS_TOOLCHAIN_PREFIX}")
set(CMAKE_SYSTEM_NAME yggdrasil)
set(CMAKE_C_COMPILER "${CROSS_TOOLCHAIN_PREFIX}/bin/clang")
set(CMAKE_CXX_COMPILER "${CROSS_TOOLCHAIN_PREFIX}/bin/clang++")
# Specify the target triple for your custom OS.
set(CMAKE_C_COMPILER_TARGET "aarch64-unknown-yggdrasil")
set(CMAKE_ASM_COMPILER_TARGET "aarch64-unknown-yggdrasil")
# Disable default system libraries
# set(CMAKE_EXE_LINKER_FLAGS "-nostdlib")
# set(CMAKE_SHARED_LINKER_FLAGS "-v")
set(CMAKE_C_FLAGS "--target=aarch64-unknown-yggdrasil -fPIC")
set(CMAKE_CXX_FLAGS "--target=aarch64-unknown-yggdrasil -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK=1 -D_LIBCPP_HAS_NO_TREE_BARRIER")
# Specify any additional include paths or linker flags as needed

View File

@ -0,0 +1,22 @@
if (NOT DEFINED ENV{CROSS_TOOLCHAIN_PREFIX})
message(FATAL_ERROR "CROSS_TOOLCHAIN_PREFIX=... must be specified.")
endif()
set(CROSS_TOOLCHAIN_PREFIX "$ENV{CROSS_TOOLCHAIN_PREFIX}")
set(CMAKE_SYSTEM_NAME yggdrasil)
set(CMAKE_C_COMPILER "${CROSS_TOOLCHAIN_PREFIX}/bin/clang")
set(CMAKE_CXX_COMPILER "${CROSS_TOOLCHAIN_PREFIX}/bin/clang++")
# Specify the target triple for your custom OS.
set(CMAKE_C_COMPILER_TARGET "i686-unknown-yggdrasil")
set(CMAKE_ASM_COMPILER_TARGET "i686-unknown-yggdrasil")
# Disable default system libraries
# set(CMAKE_EXE_LINKER_FLAGS "-nostdlib")
# set(CMAKE_SHARED_LINKER_FLAGS "-v")
set(CMAKE_C_FLAGS "--target=i686-unknown-yggdrasil -fPIC -m32")
set(CMAKE_CXX_FLAGS "--target=i686-unknown-yggdrasil -m32 -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK=1 -D_LIBCPP_HAS_NO_TREE_BARRIER")
# Specify any additional include paths or linker flags as needed

View File

@ -0,0 +1,20 @@
if (NOT DEFINED ENV{CROSS_TOOLCHAIN_PREFIX})
message(FATAL_ERROR "CROSS_TOOLCHAIN_PREFIX=... must be specified.")
endif()
set(CROSS_TOOLCHAIN_PREFIX "$ENV{CROSS_TOOLCHAIN_PREFIX}")
set(CMAKE_SYSTEM_NAME yggdrasil)
set(CMAKE_C_COMPILER "${CROSS_TOOLCHAIN_PREFIX}/bin/clang")
set(CMAKE_CXX_COMPILER "${CROSS_TOOLCHAIN_PREFIX}/bin/clang++")
# Specify the target triple for your custom OS.
set(CMAKE_C_COMPILER_TARGET "x86_64-unknown-yggdrasil")
set(CMAKE_ASM_COMPILER_TARGET "x86_64-unknown-yggdrasil")
# Disable default system libraries
# set(CMAKE_EXE_LINKER_FLAGS "-nostdlib")
# set(CMAKE_SHARED_LINKER_FLAGS "-v")
set(CMAKE_C_FLAGS "--target=x86_64-unknown-yggdrasil -fPIC")
set(CMAKE_CXX_FLAGS "--target=x86_64-unknown-yggdrasil -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK=1 -D_LIBCPP_HAS_NO_TREE_BARRIER")

View File

@ -1,2 +0,0 @@
[toolchain]
branch = "alnyan/yggdrasil-1.84.0"

View File

@ -10,14 +10,13 @@ use crate::{
util::{self, git_clone},
};
use super::cargo::CargoBuilder;
use super::{
cargo::CargoBuilder,
toolchain_c::{self, Llvm},
};
pub const OPENLIBM_URL: &str = "https://git.alnyan.me/yggdrasil/openlibm.git";
pub struct Llvm {
root: PathBuf,
}
pub struct Ygglibc {
static_lib_file: PathBuf,
shared_lib_file: PathBuf,
@ -31,29 +30,6 @@ pub struct Openlibm {
include_path: PathBuf,
}
impl Llvm {
pub fn c_clang(&self, env: &BuildEnv) -> Command {
let mut command = Command::new(self.root.join("bin/clang"));
command.arg(format!("--target={}-unknown-yggdrasil", env.arch.name()));
command.arg(format!("--sysroot={}", env.llvm_sysroot.display()));
command
}
pub fn c_clangpp(&self, env: &BuildEnv) -> Command {
let mut command = Command::new(self.root.join("bin/clang++"));
command.arg(format!("--target={}-unknown-yggdrasil", env.arch.name()));
command.arg(format!("--sysroot={}", env.llvm_sysroot.display()));
// TODO: Not yet implemented features:
command.arg("-fno-exceptions");
command.arg("-fno-rtti");
command
}
pub fn clang(&self) -> PathBuf {
self.root.join("bin/clang")
}
}
fn build_test_cpp_program(
env: &BuildEnv,
llvm: &Llvm,
@ -226,43 +202,18 @@ fn install_openlibm(env: &BuildEnv, libm: &Openlibm) -> Result<(), Error> {
Ok(())
}
fn build_llvm(env: &BuildEnv) -> Result<Llvm, Error> {
// Configuration:
/*
cmake
-DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt"
-DLLVM_TARGETS_TO_BUILD=X86
-DCMAKE_SYSTEM_NAME=yggdrasil
-DLLVM_USE_LINKER=lld
-DBUILD_SHARED_LIBS=false
-DLLVM_BUILD_TOOLS=false
-DLLVM_CCACHE_BUILD=true
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_HOST_TRIPLE=x86_64-unknown-linux-gnu
-DUNIX=1
-GNinja
-DCMAKE_INSTALL_PREFIX=/home/alnyan/build/ygg/toolchain-c/prefix/host ../llvm
Build:
cmake --build . -j 16
Install:
cmake --build . -t install
*/
// TODO actually build and install LLVM
Ok(Llvm {
root: env.workspace_root.join("toolchain-c/prefix/host"),
})
}
pub fn build_c(env: &BuildEnv, install: &mut Vec<(PathBuf, PathBuf)>) -> Result<(), Error> {
let llvm = build_llvm(env)?;
let llvm = toolchain_c::install_llvm_compiler(env)?;
let ygglibc = build_ygglibc(env)?;
install_ygglibc(env, &ygglibc)?;
toolchain_c::install_compiler_rt(env, &llvm)?;
let libm = build_openlibm(env, &llvm)?;
install_openlibm(env, &libm)?;
toolchain_c::install_llvm_cxx_runtime(env, &llvm)?;
build_test_c_program(env, &llvm, install)?;
build_test_cpp_program(env, &llvm, install)?;

View File

@ -17,6 +17,7 @@ pub mod c;
mod cargo;
mod module;
pub mod toolchain;
pub mod toolchain_c;
mod userspace;
#[derive(Debug)]

View File

@ -0,0 +1,225 @@
use std::{fs, path::PathBuf, process::Command};
use crate::{env::BuildEnv, error::Error};
pub struct Llvm {
root: PathBuf,
}
impl Llvm {
pub fn c_clang(&self, env: &BuildEnv) -> Command {
let mut command = Command::new(self.root.join("bin/clang"));
command.arg(format!("--target={}-unknown-yggdrasil", env.arch.name()));
command.arg(format!("--sysroot={}", env.llvm_sysroot.display()));
command
}
pub fn c_clangpp(&self, env: &BuildEnv) -> Command {
let mut command = Command::new(self.root.join("bin/clang++"));
command.arg(format!("--target={}-unknown-yggdrasil", env.arch.name()));
command.arg(format!("--sysroot={}", env.llvm_sysroot.display()));
// TODO: Not yet implemented features:
command.arg("-fno-exceptions");
command.arg("-fno-rtti");
command
}
pub fn clang(&self) -> PathBuf {
self.root.join("bin/clang")
}
}
pub fn install_compiler_rt(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error> {
let build_dir = env.workspace_root.join(format!(
"toolchain-c/llvm-project/build-{}/compiler-rt",
env.arch.name()
));
let makefile = build_dir.join("build.ninja");
let toolchain_cmake = env.workspace_root.join(format!(
"toolchain-c/cmake/toolchain-{}.cmake",
env.arch.name()
));
fs::create_dir_all(&build_dir)?;
if !makefile.exists() {
log::info!("Configure compiler-rt for {}", env.arch.name());
let mut command = Command::new("cmake");
command.current_dir(&build_dir);
command.env("CROSS_TOOLCHAIN_PREFIX", &llvm.root);
command
.arg(format!("--toolchain={}", toolchain_cmake.display()))
.arg("-DCOMPILER_RT_BUILD_BUILTINS=ON")
.arg("-DCOMPILER_RT_BUILD_LIBFUZZER=OFF")
.arg("-DCOMPILER_RT_BUILD_MEMPROF=OFF")
.arg("-DCOMPILER_RT_BUILD_PROFILE=OFF")
.arg("-DCOMPILER_RT_BUILD_CTX_PROFILE=OFF")
.arg("-DCOMPILER_RT_BUILD_SANITIZERS=OFF")
.arg("-DCOMPILER_RT_BUILD_XRAY=OFF")
.arg("-DCOMPILER_RT_BUILD_STANDALONE_LIBATOMIC=ON")
.arg("-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON")
.arg("-DCOMPILER_RT_BAREMETAL_BUILD=ON")
.arg("-DCOMPILER_RT_BUILTINS_ENABLE_PIC=ON")
.arg("-DCOMPILER_RT_BUILTINS_HIDE_SYMBOLS=OFF")
.arg("-DDEFAULT_COMPILER_RT_USE_BUILTINS_LIBRARY=ON")
.arg("-DCMAKE_EXE_LINKER_FLAGS=-nostdlib")
.arg(format!(
"-DLLVM_CMAKE_DIR={}/lib/cmake/llvm",
llvm.root.display()
))
.arg(format!("-DCMAKE_SYSROOT={}", env.llvm_sysroot.display()))
.arg(format!(
"-DCMAKE_INSTALL_PREFIX={}",
env.llvm_sysroot.display()
))
.arg("-GNinja")
.arg("../../compiler-rt");
if !command.status()?.success() {
todo!()
}
}
// Build and install compiler-rt
log::info!("Build compiler-rt for {}", env.arch.name());
let mut command = Command::new("cmake");
command.current_dir(&build_dir);
command
.args(["--build", "."])
.args(["-t install"])
.arg("-j");
if !command.status()?.success() {
todo!()
}
Ok(())
}
pub fn install_llvm_cxx_runtime(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error> {
let build_dir = env.workspace_root.join(format!(
"toolchain-c/llvm-project/build-{}/runtimes",
env.arch.name()
));
let makefile = build_dir.join("build.ninja");
let toolchain_cmake = env.workspace_root.join(format!(
"toolchain-c/cmake/toolchain-{}.cmake",
env.arch.name()
));
fs::create_dir_all(&build_dir)?;
if !makefile.exists() {
log::info!("Configure libc++/libc++abi for {}", env.arch.name());
let mut command = Command::new("cmake");
command.current_dir(&build_dir);
command.env("CROSS_TOOLCHAIN_PREFIX", &llvm.root);
command
.arg(format!("--toolchain={}", toolchain_cmake.display()))
.arg("-GNinja")
.arg("-DCMAKE_BUILD_TYPE=RelWithDebInfo")
.arg("-DLLVM_ENABLE_RUNTIMES=libcxxabi;libcxx")
.arg("-DLIBCXXABI_ENABLE_STATIC=ON")
.arg("-DLIBCXXABI_ENABLE_SHARED=OFF")
.arg("-DLIBCXXABI_USE_COMPILER_RT=ON")
.arg("-DLIBCXXABI_ENABLE_THREADS=OFF")
.arg("-DLIBCXXABI_ENABLE_EXCEPTIONS=OFF")
.arg("-DLIBCXXABI_USE_LLVM_UNWINDER=OFF")
.arg("-DLIBCXX_CXX_ABI=libcxxabi")
.arg("-DLIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=ON")
.arg("-DLIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY=ON")
.arg("-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON")
.arg("-DLIBCXX_ENABLE_FILESYSTEM=ON")
.arg("-DLIBCXX_ENABLE_THREADS=OFF")
.arg("-DLIBCXX_ENABLE_EXCEPTIONS=OFF")
.arg("-DLIBCXX_ENABLE_SHARED=ON")
.arg("-DLIBCXX_ENABLE_STATIC=ON")
.arg("-DLIBCXX_INCLUDE_BENCHMARKS=OFF")
.arg("-DCMAKE_EXE_LINKER_FLAGS=-nostdlib")
.arg(format!(
"-DLLVM_DEFAULT_TARGET_TRIPLE={}-unknown-yggdrasil",
env.arch.name()
))
.arg(format!("-DCMAKE_SYSROOT={}", env.llvm_sysroot.display()))
.arg(format!(
"-DCMAKE_INSTALL_PREFIX={}",
env.llvm_sysroot.display()
))
.arg("../../runtimes");
if !command.status()?.success() {
todo!()
}
}
// Build and install libc++/abi
// TODO each rebuild of ygglibc reinstalls headers which triggers libc++ rebuild,
// only copy those if the mtime on ygglibc's headers is newer than whatever's
// present in the sysroot
log::info!("Build libc++/libc++abi for {}", env.arch.name());
let mut command = Command::new("cmake");
command.current_dir(&build_dir);
command
.args(["--build", "."])
.args(["-t install"])
.arg("-j");
if !command.status()?.success() {
todo!()
}
Ok(())
}
pub fn install_llvm_compiler(env: &BuildEnv) -> Result<Llvm, Error> {
let root = env.workspace_root.join("toolchain-c/prefix/host");
let build_dir = env.workspace_root.join("toolchain-c/llvm-project/build");
let makefile = build_dir.join("build.ninja");
fs::create_dir_all(&build_dir)?;
// TODO clone LLVM
// Configure LLVM
// TODO option to force reconfig
if !makefile.exists() {
log::info!("Configure LLVM");
let mut command = Command::new("cmake");
command.current_dir(&build_dir);
command
.arg("-DCMAKE_BUILD_TYPE=Release")
.arg("-DCMAKE_C_COMPILER=/usr/bin/clang")
.arg("-DCMAKE_CXX_COMPILER=/usr/bin/clang++")
.arg("-DBUILD_SHARED_LIBS=false")
.arg("-DLLVM_ENABLE_PROJECTS=clang;lld;compiler-rt")
.arg("-DLLVM_USE_LINKER=lld")
.arg("-DLLVM_BUILD_TOOLS=false")
.arg("-DLLVM_CCACHE_BUILD=true")
.arg("-DLLVM_TARGETS_TO_BUILD=X86;AArch64")
.arg(&format!("-DCMAKE_INSTALL_PREFIX={}", root.display()))
.arg("-GNinja")
.arg("../llvm");
if !command.status()?.success() {
todo!()
}
}
// Build and install LLVM
log::info!("Build LLVM");
let mut command = Command::new("cmake");
command.current_dir(&build_dir);
command
.args(["--build", "."])
.args(["-t install"])
.arg("-j");
if !command.status()?.success() {
todo!()
}
Ok(Llvm { root })
}

View File

@ -8,10 +8,45 @@ pub struct ToolchainConfig {
pub branch: String,
}
#[derive(Debug, serde::Deserialize)]
#[serde(default)]
pub struct BuildComponents {
pub libc: bool,
// Depends on .libc
pub libcxx: bool,
}
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
pub struct AArch64TargetConfig {
pub components: BuildComponents,
}
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
pub struct X86_64TargetConfig {
pub components: BuildComponents,
}
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
pub struct I686TargetConfig {
pub components: BuildComponents,
}
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
pub struct TargetConfig {
pub aarch64: AArch64TargetConfig,
pub x86_64: X86_64TargetConfig,
pub i686: I686TargetConfig,
}
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
pub struct XTaskConfig {
pub toolchain: ToolchainConfig,
pub target: TargetConfig,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, ValueEnum)]
@ -52,7 +87,16 @@ pub struct BuildEnv {
impl Default for ToolchainConfig {
fn default() -> Self {
Self {
branch: "alnyan/yggdrasil-master".into(),
branch: "alnyan/yggdrasil-1.84.0".into(),
}
}
}
impl Default for BuildComponents {
fn default() -> Self {
Self {
libc: true,
libcxx: true,
}
}
}

View File

@ -4,7 +4,7 @@ use std::{fs, path::PathBuf, process::ExitCode};
use build::CheckAction;
use clap::{Parser, Subcommand};
use env::{Arch, Profile};
use env::{Arch, Profile, XTaskConfig};
use error::Error;
#[macro_use]
@ -38,13 +38,8 @@ struct Args {
#[clap(short, long, help = "Be verbose where possible")]
verbose: bool,
#[clap(
short = 'C',
long,
default_value = "xtask.toml",
help = "Configuration path"
)]
config_path: PathBuf,
#[clap(short = 'C', long, help = "Configuration path")]
config_path: Option<PathBuf>,
#[clap(subcommand)]
action: Option<SubArgs>,
@ -97,8 +92,12 @@ fn run(args: Args) -> Result<(), Error> {
} else {
Profile::Debug
};
let config = toml::from_str(&fs::read_to_string(&args.config_path)?)
.map_err(|e| Error::TomlParseError(args.config_path.clone(), e))?;
let config = if let Some(path) = &args.config_path {
toml::from_str(&fs::read_to_string(path)?)
.map_err(|e| Error::TomlParseError(path.to_owned(), e))?
} else {
XTaskConfig::default()
};
let env = env::BuildEnv::new(config, args.verbose, profile, args.arch, workspace_root);
if args.clean_userspace && !matches!(&action, SubArgs::Clean { .. }) {