Files
depot/src/builder/rust.rs
T
SFG545 9953d4b2ee feat: add repo planning/binary repo support and Meson build integration
- add repos.toml source/binary repo configuration with arch overrides and priorities
- add dependency planner/execution plan flow with interactive provider selection and --yes support
- expand binary repo DB metadata (sha512, dependencies, file lists) and repo owns/search helpers
- add rootfs-scoped fd locking and minisign detached signing/verification helpers
- add Meson build/test support for the project with builddir-local CARGO_HOME/CARGO_TARGET_DIR
- install contrib example configs into sysconfdir (/etc/depot.d layout) via Meson
- improve builder env handling (sanitized PATH, tool env passthrough, CXXFLAGS support)
- support += appends in package specs and add build.flags.cxxflags parsing
- prompt for git credentials when private repo auth is required
- update docs/examples and dependency setup (system libs, zstdmt, tempfile pin)
2026-02-24 21:38:12 -06:00

184 lines
5.9 KiB
Rust
Executable File

//! Rust/Cargo build system
use crate::cross::CrossConfig;
use crate::package::PackageSpec;
use crate::source::hooks;
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
use std::process::Command;
pub fn build(
spec: &PackageSpec,
src_dir: &Path,
destdir: &Path,
cross: Option<&CrossConfig>,
export_compiler_flags: bool,
) -> Result<()> {
let flags = &spec.build.flags;
// Create destdir
fs::create_dir_all(destdir)?;
// Isolate from parent workspace by adding empty [workspace] if not present
// This prevents "believes it's in a workspace when it's not" errors
let cargo_toml = src_dir.join("Cargo.toml");
if cargo_toml.exists() {
let contents = fs::read_to_string(&cargo_toml)
.with_context(|| format!("Failed to read {}", cargo_toml.display()))?;
if !contents.contains("[workspace]") {
let isolated = format!("{}\n\n[workspace]\n", contents);
fs::write(&cargo_toml, isolated).with_context(|| {
format!("Failed to isolate Cargo.toml: {}", cargo_toml.display())
})?;
}
}
// Determine profile
let is_release = flags.profile == "release";
let profile_dir = if is_release { "release" } else { "debug" };
// Determine target triple
let target = if !flags.target.is_empty() {
Some(flags.target.clone())
} else {
cross.map(|cc_cfg| cc_cfg.prefix.clone())
};
// Build environment
let mut env_vars =
crate::builder::standard_build_env(spec, cross, false, export_compiler_flags);
// RUSTFLAGS
if !flags.rustflags.is_empty() {
crate::builder::set_env_var(&mut env_vars, "RUSTFLAGS", flags.rustflags.join(" "));
}
// If cross-compiling, set linker via CARGO_TARGET_*_LINKER
if let Some(cc_cfg) = cross {
// Convert target triple to uppercase with underscores for env var
let target_env = target
.as_ref()
.unwrap_or(&cc_cfg.prefix)
.to_uppercase()
.replace('-', "_");
let linker_var = format!("CARGO_TARGET_{}_LINKER", target_env);
crate::builder::set_env_var(&mut env_vars, &linker_var, cc_cfg.cc.clone());
crate::builder::set_env_var(&mut env_vars, "CC", cc_cfg.cc.clone());
crate::builder::set_env_var(&mut env_vars, "AR", cc_cfg.ar.clone());
}
// Set default rustup toolchain if not already set
if std::env::var("RUSTUP_TOOLCHAIN").is_err() {
crate::builder::set_env_var(&mut env_vars, "RUSTUP_TOOLCHAIN", "stable");
}
hooks::run_post_configure_commands(spec, src_dir, destdir)?;
// Run cargo build
crate::log_info!(
"Running cargo build ({})...",
if is_release { "release" } else { "debug" }
);
let mut cargo_cmd = Command::new("cargo");
cargo_cmd.current_dir(src_dir);
cargo_cmd.arg("build");
if is_release {
cargo_cmd.arg("--release");
}
// Add target if specified
if let Some(ref t) = target {
cargo_cmd.arg("--target").arg(t);
}
// Add additional cargo args
for arg in &flags.cargs {
cargo_cmd.arg(arg);
}
// Set environment
crate::builder::prepare_tool_command(&mut cargo_cmd, &env_vars);
let status = cargo_cmd
.status()
.with_context(|| format!("Failed to run cargo build for {}", spec.package.name))?;
if !status.success() {
anyhow::bail!("cargo build failed");
}
// Run post-compile hooks
hooks::run_post_compile_commands(spec, src_dir, destdir)?;
// Install binaries to destdir
crate::log_info!("Installing binaries to DESTDIR...");
// Determine target directory
let target_dir = if let Some(ref t) = target {
src_dir.join("target").join(t).join(profile_dir)
} else {
src_dir.join("target").join(profile_dir)
};
// Use bindir from flags (default: /usr/bin)
let bin_dir = destdir.join(flags.bindir.trim_start_matches('/'));
fs::create_dir_all(&bin_dir)?;
// Find and copy executable files
if target_dir.exists() {
for entry in fs::read_dir(&target_dir)
.with_context(|| format!("Failed to read target directory: {}", target_dir.display()))?
{
let entry = entry?;
let path = entry.path();
// Skip directories and non-executable files
if path.is_dir() {
continue;
}
// Check if it's an executable (no extension on Linux, or special check)
let file_name = path.file_name().unwrap().to_string_lossy();
// Skip common non-binary files
if file_name.ends_with(".d")
|| file_name.ends_with(".rlib")
|| file_name.ends_with(".rmeta")
|| file_name.contains(".so")
|| file_name.starts_with("lib")
{
continue;
}
// Check if file is executable
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if path
.metadata()
.ok()
.filter(|m| m.permissions().mode() & 0o111 != 0 && m.is_file())
.is_some()
{
let dest = bin_dir.join(&*file_name);
crate::log_info!(" Installing: {}", file_name);
fs::copy(&path, &dest).with_context(|| {
format!("Failed to copy {} to {}", path.display(), dest.display())
})?;
// Preserve executable permission
let mut perms = fs::metadata(&dest)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&dest, perms)?;
}
}
}
}
// Run post-install hooks
hooks::run_post_install_commands(spec, src_dir, destdir)?;
Ok(())
}