#[cfg(feature = "private")] mod private { use serde::Deserialize; use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::process::Command; #[derive(Debug, Deserialize)] #[serde(untagged)] enum DependencyType { Git { git: String, commit: String }, } fn default_features() -> bool { true } #[derive(Debug, Deserialize)] struct Dependency { #[serde(flatten)] dep: DependencyType, #[serde(default)] package: Option, #[serde(default)] features: Vec, #[serde(rename = "default-features", default = "default_features")] default_features: bool, } #[derive(Debug, Deserialize)] struct Manifest { dependencies: HashMap, features: HashMap>, } fn is_feature_enabled(name: &str) -> bool { let key = format!("CARGO_FEATURE_{}", name.to_uppercase().replace('-', "_")); std::env::var(key).is_ok() } fn clone_and_checkout(dep_dir: &Path, git: &str, commit: &str) { if dep_dir.exists() { if dep_dir.join(".git").exists() { let output = Command::new("git") .args(["rev-parse", "HEAD"]) .current_dir(dep_dir) .output() .expect("failed to run git rev-parse"); if output.status.success() { let current = String::from_utf8_lossy(&output.stdout).trim().to_string(); if current == commit { return; } } } std::fs::remove_dir_all(dep_dir).expect("failed to remove old dependency directory"); } let parent = dep_dir.parent().unwrap(); std::fs::create_dir_all(parent).expect("failed to create parent directory"); let status = Command::new("git") .args(["clone", git]) .arg(dep_dir.to_str().unwrap()) .status() .expect("failed to run git clone"); assert!(status.success(), "git clone failed"); let status = Command::new("git") .args(["checkout", commit]) .current_dir(dep_dir) .status() .expect("failed to run git checkout"); assert!(status.success(), "git checkout failed"); } fn dep_git_url<'a>(dep: &'a Dependency) -> &'a str { match &dep.dep { DependencyType::Git { git, .. } => git, } } fn dep_commit<'a>(dep: &'a Dependency) -> &'a str { match &dep.dep { DependencyType::Git { commit, .. } => commit, } } pub fn compile_private() { println!("cargo:rerun-if-changed=private.toml"); let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let manifest_path = manifest_dir.join("private.toml"); let content = std::fs::read_to_string(&manifest_path).expect("failed to read private.toml"); let manifest: Manifest = toml::from_str(&content).expect("failed to parse private.toml"); // Collect dependencies whose feature is enabled let mut deps_to_build: Vec = Vec::new(); for (feat, dep_names) in &manifest.features { if is_feature_enabled(feat) { for dep_name in dep_names { if !deps_to_build.contains(dep_name) { deps_to_build.push(dep_name.clone()); } } } } if deps_to_build.is_empty() { return; } let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); let target = std::env::var("TARGET").unwrap(); let is_debug = match std::env::var("PROFILE").as_deref() { Ok("debug") => true, Ok("release") => false, _ => std::env::var("DEBUG").unwrap_or_default() == "true", }; for dep_name in &deps_to_build { let dep = manifest .dependencies .get(dep_name) .unwrap_or_else(|| panic!("dependency '{}' not found in private.toml", dep_name)); let package_name = dep.package.as_ref().unwrap_or(dep_name); let dep_dir = out_dir.join(package_name); clone_and_checkout(&dep_dir, dep_git_url(dep), dep_commit(dep)); let target_dir = dep_dir.join("target"); let mut cmd = Command::new("cargo"); cmd.args(["build", "-p", package_name]) .arg("--target-dir") .arg(&target_dir) .arg("--target") .arg(&target); if !dep.features.is_empty() { cmd.arg("--features"); cmd.arg(dep.features.join(",")); } if !dep.default_features { cmd.arg("--no-default-features"); } if !is_debug { cmd.arg("--release"); } let status = cmd .current_dir(&dep_dir) .status() .expect("failed to run cargo build"); assert!(status.success(), "cargo build failed for '{}'", dep_name); let profile = if is_debug { "debug" } else { "release" }; let build_dir = target_dir.join(&target).join(profile); println!("cargo:rustc-link-search={}", build_dir.display()); let lib_name = package_name.replace('-', "_"); println!("cargo:rustc-link-lib=static={}", lib_name); } } } fn main() { #[cfg(windows)] let default_stack_size = "4194304"; // 4 MiB #[cfg(not(windows))] let default_stack_size = "8388608"; // 8 MiB let stack_size = std::env::var("MSG_TOOL_STACK_SIZE").unwrap_or(default_stack_size.to_string()); let stack_size = parse_size::parse_size(stack_size).unwrap(); println!("cargo:rerun-if-env-changed=MSG_TOOL_STACK_SIZE"); #[cfg(target_env = "msvc")] println!("cargo:rustc-link-arg=/STACK:{}", stack_size); #[cfg(target_env = "gnu")] println!("cargo:rustc-link-arg=-Wl,-z,stack-size={}", stack_size); #[cfg(feature = "private")] private::compile_private(); }