// use std::{collections::HashMap, path::Path}; use std::{ collections::HashMap, fs::OpenOptions, os::unix::fs::OpenOptionsExt, path::Path, process::Command, }; use crate::{ env::BuildEnv, error::Error, util::{self, git_clone}, }; const TOOLCHAIN_URL: &str = "https://git.alnyan.me/yggdrasil/yggdrasil-rust"; #[derive(Debug, serde::Serialize)] struct RustBuildConfig { host: Vec, target: Vec, } #[derive(Debug, serde::Serialize)] struct RustTargetConfig { cc: Option, cxx: Option, } #[derive(Debug, serde::Serialize)] struct RustRustConfig { incremental: bool, } #[derive(Debug, serde::Serialize)] #[serde(rename_all = "kebab-case")] struct RustToolchainConfig { profile: String, build: RustBuildConfig, target: HashMap, rust: RustRustConfig, } fn configure>(env: &BuildEnv, config_toml_path: P) -> Result<(), Error> { let config = RustToolchainConfig { profile: "user".into(), build: RustBuildConfig { host: vec![env.host_triple.clone()], target: vec![ env.host_triple.clone(), "x86_64-unknown-yggdrasil".into(), "aarch64-unknown-yggdrasil".into(), ], }, target: HashMap::from_iter([( "aarch64-unknown-yggdrasil".into(), RustTargetConfig { cc: Some("aarch64-linux-gnu-gcc".into()), cxx: Some("aarch64-linux-gnu-g++".into()), }, )]), rust: RustRustConfig { incremental: true }, }; let toml = toml::to_string_pretty(&config)?; std::fs::write(config_toml_path, toml.as_bytes())?; Ok(()) } pub fn is_toolchain_linked() -> bool { util::run_external_command("cargo", ["+ygg-stage1", "--version"], true).is_ok() } fn link_rustup_toolchain>(env: &BuildEnv, toolchain_root: P) -> Result<(), Error> { util::run_external_command( "rustup", [ "toolchain", "link", "ygg-stage1", &format!( "{}", toolchain_root .as_ref() .join("build") .join(&env.host_triple) .join("stage1") .display() ), ], false, ) } fn do_build>(toolchain_root: P) -> Result<(), Error> { let status = Command::new("./x") .current_dir(toolchain_root) .args(["build", "--stage=1"]) .status()?; if status.success() { Ok(()) } else { Err(Error::ToolchainBuildFailed) } } fn install_shims>(env: &BuildEnv, toolchain_root: P) -> Result<(), Error> { use std::io::Write; let toolchain_root = toolchain_root.as_ref(); let build_dir = toolchain_root.join("build").join(&env.host_triple); let stage1_rustlib = build_dir .join("stage1/lib/rustlib/") .join(&env.host_triple) .join("lib"); let stage1_tools_bin = build_dir.join("stage1-tools-bin"); let stage1_bin = build_dir.join("stage1/bin"); let rust_analyzer = stage1_bin.join("rust-analyzer"); let mut file = OpenOptions::new() .write(true) .truncate(true) .create(true) .mode(0o755) .open(rust_analyzer)?; writeln!(file, "#!/bin/sh")?; writeln!( file, "LD_LIBRARY_PATH={} {}/rust-analyzer", stage1_rustlib.display(), stage1_tools_bin.display() )?; Ok(()) } pub fn fetch(env: &BuildEnv, branch: &str) -> Result<(), Error> { let path = env.workspace_root.join("toolchain"); git_clone(path, TOOLCHAIN_URL, branch, false) } pub fn build(env: &BuildEnv) -> Result<(), Error> { let toolchain_path = env.workspace_root.join("toolchain"); let config_toml_path = toolchain_path.join("config.toml"); assert!(toolchain_path.exists()); // Write config.toml if !config_toml_path.exists() { configure(env, config_toml_path)?; } log::info!("Building toolchain"); do_build(&toolchain_path)?; log::info!("Installing rust tool shims"); install_shims(env, &toolchain_path)?; if !is_toolchain_linked() { log::info!("Linking the newly built toolchain to +ygg-stage1"); link_rustup_toolchain(env, &toolchain_path)?; } else { log::debug!("Toolchain already linked"); } Ok(()) }