feat: update depot version to 0.17.0 and enhance Python build/install commands

This commit is contained in:
2026-03-11 01:02:36 -05:00
parent aff2c68ce5
commit f72b6c46de
9 changed files with 236 additions and 29 deletions
Generated
+1 -1
View File
@@ -418,7 +418,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
[[package]]
name = "depot"
version = "0.16.1"
version = "0.17.0"
dependencies = [
"anyhow",
"ar",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "depot"
version = "0.16.1"
version = "0.17.0"
edition = "2024"
[lints.rust]
+1 -1
View File
@@ -1,6 +1,6 @@
project(
'depot',
version: '0.16.1',
version: '0.17.0',
meson_version: '>=0.60.0',
default_options: ['buildtype=release'],
)
+1 -1
View File
@@ -7,7 +7,7 @@ mod custom;
mod makefile;
mod meson;
mod perl;
mod python;
pub(crate) mod python;
mod rust;
pub mod state;
+38 -19
View File
@@ -62,19 +62,7 @@ pub fn build(
fs::create_dir_all(&dist_dir)
.with_context(|| format!("Failed to create dist dir: {}", dist_dir.display()))?;
match detect_frontend(&actual_src)? {
BuildFrontend::Pep517(cfg) => {
build_wheel_pep517(&actual_src, &dist_dir, &env_vars, &cfg, &config_settings)?
}
BuildFrontend::LegacySetupPy => {
if !config_settings.is_empty() {
bail!(
"build.flags.config_setting is only supported for PEP 517 builds (pyproject.toml)"
);
}
build_wheel_setup_py(&actual_src, &dist_dir, &env_vars)?;
}
}
build_wheels(&actual_src, &dist_dir, &env_vars, &config_settings)?;
hooks::run_post_compile_commands(spec, &actual_src, destdir)?;
state.mark_done(BuildStep::PostCompileDone)?;
@@ -84,11 +72,7 @@ pub fn build(
if !state.is_done(BuildStep::PostInstallDone) {
let wheels = collect_wheels(&dist_dir)?;
let py_version = detect_python_major_minor(&env_vars)?;
let prefix_rel = normalized_prefix(&flags.prefix)?;
for wheel in wheels {
install_wheel(&wheel, destdir, &prefix_rel, &py_version)?;
}
install_wheels(&wheels, destdir, &flags.prefix, &env_vars)?;
hooks::run_post_install_commands(spec, &actual_src, destdir)?;
state.mark_done(BuildStep::PostInstallDone)?;
@@ -356,7 +340,28 @@ fn build_wheel_pep517(
Ok(())
}
fn collect_wheels(dist_dir: &Path) -> Result<Vec<PathBuf>> {
pub(crate) fn build_wheels(
src_dir: &Path,
dist_dir: &Path,
env_vars: &[(String, String)],
config_settings: &[String],
) -> Result<()> {
match detect_frontend(src_dir)? {
BuildFrontend::Pep517(cfg) => {
build_wheel_pep517(src_dir, dist_dir, env_vars, &cfg, config_settings)
}
BuildFrontend::LegacySetupPy => {
if !config_settings.is_empty() {
bail!(
"build.flags.config_setting is only supported for PEP 517 builds (pyproject.toml)"
);
}
build_wheel_setup_py(src_dir, dist_dir, env_vars)
}
}
}
pub(crate) fn collect_wheels(dist_dir: &Path) -> Result<Vec<PathBuf>> {
let mut wheels = Vec::new();
for entry in fs::read_dir(dist_dir).with_context(|| {
format!(
@@ -513,6 +518,20 @@ fn install_wheel(
Ok(())
}
pub(crate) fn install_wheels(
wheel_paths: &[PathBuf],
destdir: &Path,
prefix: &str,
env_vars: &[(String, String)],
) -> Result<()> {
let py_version = detect_python_major_minor(env_vars)?;
let prefix_rel = normalized_prefix(prefix)?;
for wheel in wheel_paths {
install_wheel(wheel, destdir, &prefix_rel, &py_version)?;
}
Ok(())
}
fn is_top_level_dist_info_entry_points(rel: &Path) -> bool {
let parts = match path_parts(rel, "wheel entry") {
Ok(p) => p,
+27
View File
@@ -152,6 +152,33 @@ pub enum Commands {
#[arg(short, long)]
output: Option<PathBuf>,
},
#[command(hide = true)]
Internal {
#[command(subcommand)]
command: InternalCommands,
},
}
#[derive(Subcommand)]
pub enum InternalCommands {
#[command(hide = true)]
PythonBuild {
#[arg(long, default_value = ".")]
src_dir: PathBuf,
#[arg(long, default_value = "dist")]
dist_dir: PathBuf,
#[arg(long = "config-setting")]
config_settings: Vec<String>,
},
#[command(hide = true)]
PythonInstall {
#[arg(long, default_value = "dist")]
dist_dir: PathBuf,
#[arg(long = "wheel", value_name = "FILE")]
wheels: Vec<PathBuf>,
#[arg(long, default_value = "/usr")]
prefix: String,
},
}
#[derive(Subcommand)]
+41 -1
View File
@@ -1,4 +1,4 @@
use crate::cli::{Cli, Commands, RepoCommands, RepoKindArg};
use crate::cli::{Cli, Commands, InternalCommands, RepoCommands, RepoKindArg};
use crate::{
builder, cli_assets, config, cross, db, deps, index, install, locking, package, planner,
signing, source, staging, ui,
@@ -260,6 +260,43 @@ fn parse_keep_list(metadata: &toml::Value) -> Vec<String> {
Vec::new()
}
fn current_process_env_vars() -> Vec<(String, String)> {
std::env::vars().collect()
}
fn run_internal_command(command: InternalCommands) -> Result<()> {
match command {
InternalCommands::PythonBuild {
src_dir,
dist_dir,
config_settings,
} => {
let env_vars = current_process_env_vars();
crate::builder::python::build_wheels(&src_dir, &dist_dir, &env_vars, &config_settings)
}
InternalCommands::PythonInstall {
dist_dir,
wheels,
prefix,
} => {
let env_vars = current_process_env_vars();
let wheel_paths = if wheels.is_empty() {
crate::builder::python::collect_wheels(&dist_dir)?
} else {
wheels
};
let destdir = std::env::var("DESTDIR")
.context("DESTDIR must be set for internal python-install")?;
crate::builder::python::install_wheels(
&wheel_paths,
Path::new(&destdir),
&prefix,
&env_vars,
)
}
}
}
fn package_spec_from_archive_metadata(metadata: &toml::Value) -> package::PackageSpec {
let mut spec = package::PackageSpec {
package: package::PackageInfo {
@@ -4465,6 +4502,9 @@ pub fn run(cli: Cli) -> Result<()> {
output_path.display()
));
}
Commands::Internal { command } => {
run_internal_command(command)?;
}
}
Ok(())
+2 -2
View File
@@ -15,12 +15,12 @@ pub fn is_root() -> bool {
pub fn wrap_install_command(program: &str, destdir: &Path) -> Command {
let script_path = shell_script_path(program);
if is_root() {
build_command(program, script_path.as_deref())
build_command(program, script_path)
} else {
// Use system fakeroot command which handles LD_PRELOAD internally
let mut cmd = Command::new("fakeroot");
cmd.arg("--");
if let Some(script_path) = script_path.as_deref() {
if let Some(script_path) = script_path {
cmd.arg("sh");
cmd.arg(script_path);
} else {
+124 -3
View File
@@ -12,14 +12,20 @@ pub const INTERNAL_DEPOT_DIR: &str = ".depot";
pub const INTERNAL_OUTPUTS_DIR: &str = ".depot/outputs";
const DEPOT_HAUL_HELPER_ENV: &str = "DEPOT_HAUL_HELPER";
const DEPOT_SUBDESTDIR_HELPER_ENV: &str = "DEPOT_SUBDESTDIR_HELPER";
const DEPOT_PYTHON_BUILD_HELPER_ENV: &str = "DEPOT_PYTHON_BUILD_HELPER";
const DEPOT_PYTHON_INSTALL_HELPER_ENV: &str = "DEPOT_PYTHON_INSTALL_HELPER";
const DEPOT_EXECUTABLE_ENV: &str = "DEPOT_EXECUTABLE";
/// Ephemeral helper command directory to prepend to PATH while running scripts.
pub struct ShellHelpers {
_tempdir: TempDir,
path_value: String,
outputs_dir: PathBuf,
depot_executable: PathBuf,
haul_path: PathBuf,
subdestdir_path: PathBuf,
python_build_path: PathBuf,
python_install_path: PathBuf,
}
impl ShellHelpers {
@@ -37,6 +43,8 @@ impl ShellHelpers {
let bin_dir = tempdir.path().join("bin");
fs::create_dir_all(&bin_dir)
.with_context(|| format!("Failed to create helper bin dir: {}", bin_dir.display()))?;
let depot_executable = std::env::current_exe()
.context("Failed to locate depot executable for shell helpers")?;
let haul_path = bin_dir.join("haul");
fs::write(&haul_path, HAUL_SCRIPT).with_context(|| {
@@ -75,14 +83,57 @@ impl ShellHelpers {
})?;
}
let python_build_path = bin_dir.join("python_build");
fs::write(&python_build_path, PYTHON_BUILD_SCRIPT).with_context(|| {
format!(
"Failed to write shell helper command: {}",
python_build_path.display()
)
})?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&python_build_path)
.with_context(|| format!("Failed to stat helper: {}", python_build_path.display()))?
.permissions();
perms.set_mode(0o755);
fs::set_permissions(&python_build_path, perms).with_context(|| {
format!("Failed to chmod helper: {}", python_build_path.display())
})?;
}
let python_install_path = bin_dir.join("python_install");
fs::write(&python_install_path, PYTHON_INSTALL_SCRIPT).with_context(|| {
format!(
"Failed to write shell helper command: {}",
python_install_path.display()
)
})?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&python_install_path)
.with_context(|| {
format!("Failed to stat helper: {}", python_install_path.display())
})?
.permissions();
perms.set_mode(0o755);
fs::set_permissions(&python_install_path, perms).with_context(|| {
format!("Failed to chmod helper: {}", python_install_path.display())
})?;
}
let path_value = crate::runtime_env::prepend_helper_to_safe_path(&bin_dir);
Ok(Self {
_tempdir: tempdir,
path_value,
outputs_dir: destdir.join(INTERNAL_OUTPUTS_DIR),
depot_executable,
haul_path,
subdestdir_path,
python_build_path,
python_install_path,
})
}
@@ -105,6 +156,21 @@ impl ShellHelpers {
DEPOT_SUBDESTDIR_HELPER_ENV,
self.subdestdir_path.to_string_lossy().into_owned(),
);
set_env_var(
env_vars,
DEPOT_PYTHON_BUILD_HELPER_ENV,
self.python_build_path.to_string_lossy().into_owned(),
);
set_env_var(
env_vars,
DEPOT_PYTHON_INSTALL_HELPER_ENV,
self.python_install_path.to_string_lossy().into_owned(),
);
set_env_var(
env_vars,
DEPOT_EXECUTABLE_ENV,
self.depot_executable.to_string_lossy().into_owned(),
);
}
/// Apply helper-related variables directly to a `std::process::Command`.
@@ -113,7 +179,10 @@ impl ShellHelpers {
.env("DEPOT_OUTPUTS_DIR", &self.outputs_dir)
.env("DEPOT_INTERNAL_DIR", INTERNAL_DEPOT_DIR)
.env(DEPOT_HAUL_HELPER_ENV, &self.haul_path)
.env(DEPOT_SUBDESTDIR_HELPER_ENV, &self.subdestdir_path);
.env(DEPOT_SUBDESTDIR_HELPER_ENV, &self.subdestdir_path)
.env(DEPOT_PYTHON_BUILD_HELPER_ENV, &self.python_build_path)
.env(DEPOT_PYTHON_INSTALL_HELPER_ENV, &self.python_install_path)
.env(DEPOT_EXECUTABLE_ENV, &self.depot_executable);
}
}
@@ -121,7 +190,7 @@ impl ShellHelpers {
/// through `/bin/sh`, avoiding direct execution from mounts that may be `noexec`.
pub fn wrap_shell_command(command: &str) -> String {
format!(
"haul() {{ /bin/sh \"${{{DEPOT_HAUL_HELPER_ENV}:?}}\" \"$@\"; }}\nsubdestdir() {{ /bin/sh \"${{{DEPOT_SUBDESTDIR_HELPER_ENV}:?}}\" \"$@\"; }}\n{command}"
"haul() {{ /bin/sh \"${{{DEPOT_HAUL_HELPER_ENV}:?}}\" \"$@\"; }}\nsubdestdir() {{ /bin/sh \"${{{DEPOT_SUBDESTDIR_HELPER_ENV}:?}}\" \"$@\"; }}\npython_build() {{ /bin/sh \"${{{DEPOT_PYTHON_BUILD_HELPER_ENV}:?}}\" \"$@\"; }}\npython_install() {{ /bin/sh \"${{{DEPOT_PYTHON_INSTALL_HELPER_ENV}:?}}\" \"$@\"; }}\n{command}"
)
}
@@ -273,9 +342,36 @@ mkdir -p "$path"
printf '%s\n' "$path"
"#;
const PYTHON_BUILD_SCRIPT: &str = r#"#!/bin/sh
set -eu
fail() {
echo "python_build: $*" >&2
exit 1
}
[ "${DEPOT_EXECUTABLE:-}" != "" ] || fail "DEPOT_EXECUTABLE is not set"
exec "$DEPOT_EXECUTABLE" internal python-build "$@"
"#;
const PYTHON_INSTALL_SCRIPT: &str = r#"#!/bin/sh
set -eu
fail() {
echo "python_install: $*" >&2
exit 1
}
[ "${DEPOT_EXECUTABLE:-}" != "" ] || fail "DEPOT_EXECUTABLE is not set"
[ "${DESTDIR:-}" != "" ] || fail "DESTDIR is not set"
exec "$DEPOT_EXECUTABLE" internal python-install "$@"
"#;
#[cfg(test)]
mod tests {
use super::{INTERNAL_DEPOT_DIR, ShellHelpers, shell_ident_suffix};
use super::{INTERNAL_DEPOT_DIR, ShellHelpers, shell_ident_suffix, wrap_shell_command};
use tempfile::tempdir;
#[test]
@@ -307,4 +403,29 @@ mod tests {
.into_owned();
assert!(path.starts_with(&helper_prefix));
}
#[test]
fn shell_helpers_export_python_helper_paths() {
let destdir = tempdir().unwrap();
let helpers = ShellHelpers::new(destdir.path()).unwrap();
let mut envs = Vec::new();
helpers.apply_to_env_vars(&mut envs);
assert!(
envs.iter()
.any(|(key, _)| key == "DEPOT_PYTHON_BUILD_HELPER")
);
assert!(
envs.iter()
.any(|(key, _)| key == "DEPOT_PYTHON_INSTALL_HELPER")
);
assert!(envs.iter().any(|(key, _)| key == "DEPOT_EXECUTABLE"));
}
#[test]
fn wrap_shell_command_exposes_python_helpers() {
let wrapped = wrap_shell_command("python_build\npython_install");
assert!(wrapped.contains("python_build()"));
assert!(wrapped.contains("python_install()"));
}
}