use std::{ fs, path::{Path, PathBuf}, process::Command, }; use crate::{ env::BuildEnv, error::Error, util::{self, git_clone}, }; use super::{ cargo::CargoBuilder, toolchain_c::{self, Llvm}, }; pub const OPENLIBM_URL: &str = "https://git.alnyan.me/yggdrasil/openlibm.git"; pub struct Ygglibc { static_lib_file: PathBuf, shared_lib_file: PathBuf, crt0_file: PathBuf, include_paths: Vec, } pub struct Openlibm { shared_lib_file: PathBuf, static_lib_file: PathBuf, include_path: PathBuf, } fn build_test_cpp_program( env: &BuildEnv, llvm: &Llvm, install: &mut Vec<(PathBuf, PathBuf)>, ) -> Result<(), Error> { log::info!("Building a test C++ program [PIE]"); let target_dir = &env.userspace_output_dir; let mut command = llvm.c_clangpp(env); command .args([ "-fpie", "-Bdynamic", "-O0", "-ggdb", "-fstack-protector-strong", ]) .arg("-o") .arg(target_dir.join("cpp-test")) .arg(env.workspace_root.join("test.cpp")); if !command.status()?.success() { return Err(Error::ExternalCommandFailed); } install.push((target_dir.join("cpp-test"), "cpp-test".into())); log::info!("Building a test C++ program [static]"); let target_dir = &env.userspace_output_dir; let mut command = llvm.c_clangpp(env); command .args(["-static", "-O0", "-ggdb", "-fstack-protector-strong"]) .arg("-o") .arg(target_dir.join("cpp-test-static")) .arg(env.workspace_root.join("test.cpp")); if !command.status()?.success() { return Err(Error::ExternalCommandFailed); } install.push((target_dir.join("cpp-test-static"), "cpp-test-static".into())); Ok(()) } fn build_test_c_program( env: &BuildEnv, llvm: &Llvm, install: &mut Vec<(PathBuf, PathBuf)>, ) -> Result<(), Error> { log::info!("Building a test C program [PIE]"); let target_dir = &env.userspace_output_dir; let mut command = llvm.c_clang(env); command .args([ "-fpie", "-Bdynamic", "-O0", "-ggdb", "-lm", "-fstack-protector-strong", ]) .arg("-o") .arg(target_dir.join("c-test")) .arg(env.workspace_root.join("test.c")); if !command.status()?.success() { return Err(Error::ExternalCommandFailed); } install.push((target_dir.join("c-test"), "c-test".into())); // if env.arch == Arch::x86_64 { log::info!("Building a test C program [static]"); let mut command = llvm.c_clang(env); command .args(["-static", "-O0", "-ggdb", "-fstack-protector-strong", "-lm"]) .arg("-o") .arg(target_dir.join("c-test-static")) .arg(env.workspace_root.join("test.c")); if !command.status()?.success() { return Err(Error::ExternalCommandFailed); } install.push((target_dir.join("c-test-static"), "c-test-static".into())); // } Ok(()) } fn install_ygglibc(env: &BuildEnv, ygglibc: &Ygglibc) -> Result<(), Error> { log::info!("Installing ygglibc into LLVM sysroot"); let dst_lib_dir = env.llvm_sysroot.join("lib"); let dst_include_dir = env.llvm_sysroot.join("usr/include"); fs::create_dir_all(&dst_lib_dir)?; fs::create_dir_all(&dst_include_dir)?; fs::copy(&ygglibc.static_lib_file, dst_lib_dir.join("libygglibc.a"))?; fs::copy(&ygglibc.shared_lib_file, dst_lib_dir.join("libygglibc.so"))?; fs::copy(&ygglibc.crt0_file, dst_lib_dir.join("crt0.o"))?; for path in ygglibc.include_paths.iter() { util::copy_dir_recursive(path, &dst_include_dir)?; } Ok(()) } fn build_ygglibc(env: &BuildEnv) -> Result { let ygglibc_dir = env.workspace_root.join("userspace/lib/ygglibc"); let target_dir = ygglibc_dir.join(format!( "target/{}-unknown-none/{}", env.arch.name(), env.profile.dir() )); CargoBuilder::Ygglibc(env).build(&ygglibc_dir)?; let static_lib_file = target_dir.join("libygglibc.a"); let shared_lib_file = target_dir.join("libygglibc.so"); let crt0_file = target_dir.join("crt0.o"); let generated_includes = target_dir.join("include"); let static_includes = ygglibc_dir.join("include"); Ok(Ygglibc { static_lib_file, shared_lib_file, crt0_file, include_paths: vec![generated_includes, static_includes], }) } // TODO clone openlibm fn build_openlibm(env: &BuildEnv, llvm: &Llvm) -> Result { fn make(env: &BuildEnv, llvm: &Llvm, libm_path: &Path) -> Command { let mut command = Command::new("make"); command.env("SYSROOT", &env.llvm_sysroot); command.env("ARCH", env.arch.name()); command.env("OS", "yggdrasil"); command.env("TRIPLE", format!("{}-unknown-yggdrasil", env.arch.name())); command.env("USEGCC", "0"); command.env("USECLANG", "1"); command.env("CC", llvm.clang()); command.current_dir(&libm_path); command } let libm_path = env .workspace_root .join(format!("toolchain-c/libs/openlibm-{}", env.arch.name())); git_clone(&libm_path, OPENLIBM_URL, "alnyan/yggdrasil", false)?; log::info!("Building openlibm"); let mut command = make(env, llvm, &libm_path); if !command.status()?.success() { return Err(Error::ExternalCommandFailed); } Ok(Openlibm { shared_lib_file: libm_path.join("libopenlibm.so.4.0"), static_lib_file: libm_path.join("libopenlibm.a"), include_path: libm_path.join("include"), }) } fn install_openlibm(env: &BuildEnv, libm: &Openlibm) -> Result<(), Error> { fs::copy(&libm.static_lib_file, env.llvm_sysroot.join("lib/libm.a"))?; fs::copy(&libm.shared_lib_file, env.llvm_sysroot.join("lib/libm.so"))?; util::copy_dir_recursive(&libm.include_path, env.llvm_sysroot.join("usr/include"))?; Ok(()) } pub fn build_c(env: &BuildEnv, install: &mut Vec<(PathBuf, PathBuf)>) -> Result<(), Error> { 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)?; if env.config.components(env).libcxx { toolchain_c::install_llvm_cxx_runtime(env, &llvm)?; } build_test_c_program(env, &llvm, install)?; if env.config.components(env).libcxx { build_test_cpp_program(env, &llvm, install)?; install.push(( env.llvm_sysroot.join("lib/libc++.so"), "lib/libc++.so".into(), )); } install.push((ygglibc.shared_lib_file, "lib/libygglibc.so".into())); install.push((libm.shared_lib_file, "lib/libopenlibm.so.4".into())); Ok(()) }