feat: add cleanup option for auto-installed dependencies in build command
- Introduced a new `cleanup_deps` flag in the build command to remove dependencies that were automatically installed during the build process. - Implemented an `InterruptWatcher` to handle Ctrl-C interruptions gracefully during the build process. - Enhanced the `AutoInstalledDependencyTracker` to track and manage auto-installed dependencies, allowing for cleanup after the build. - Updated the CLI to parse the new `cleanup_deps` flag and integrated it into the build workflow. - Added tests to ensure the correct parsing of the `cleanup_deps` flag and the functionality of the dependency tracking and cleanup process. - Improved logging for package removal operations to include a verbose mode controlled by the `DEPOT_VERBOSE_REMOVE` environment variable.
This commit is contained in:
+1
-14
@@ -13,17 +13,4 @@ sast:
|
|||||||
stage: test
|
stage: test
|
||||||
|
|
||||||
secret_detection:
|
secret_detection:
|
||||||
stage: secret-detection
|
stage: secret-detection
|
||||||
|
|
||||||
rust_test:
|
|
||||||
stage: test
|
|
||||||
image: rust:1.93.1
|
|
||||||
cache:
|
|
||||||
key: cargo
|
|
||||||
paths:
|
|
||||||
- .cargo/
|
|
||||||
- target/
|
|
||||||
variables:
|
|
||||||
CARGO_HOME: "$CI_PROJECT_DIR/.cargo"
|
|
||||||
script:
|
|
||||||
- cargo test --workspace --all-features --locked
|
|
||||||
Generated
+13
-2
@@ -380,7 +380,7 @@ dependencies = [
|
|||||||
"mio",
|
"mio",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"rustix",
|
"rustix",
|
||||||
"signal-hook",
|
"signal-hook 0.3.18",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
@@ -445,6 +445,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_ignored",
|
"serde_ignored",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"signal-hook 0.4.3",
|
||||||
"suppaftp",
|
"suppaftp",
|
||||||
"sys-mount",
|
"sys-mount",
|
||||||
"tar",
|
"tar",
|
||||||
@@ -1962,6 +1963,16 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-mio"
|
name = "signal-hook-mio"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -1970,7 +1981,7 @@ checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"signal-hook",
|
"signal-hook 0.3.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ b2sum-rust = "0.3.0"
|
|||||||
serde_ignored = "0.1.14"
|
serde_ignored = "0.1.14"
|
||||||
lz4_flex = "0.12.0"
|
lz4_flex = "0.12.0"
|
||||||
lzma-rust2 = "0.16.2"
|
lzma-rust2 = "0.16.2"
|
||||||
|
signal-hook = "0.4.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "=3.27.0"
|
tempfile = "=3.27.0"
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ depot install zlib-1.2.11-1-x86_64.depot.pkg.tar.zst
|
|||||||
- `remove <PACKAGE>`: Remove an installed package.
|
- `remove <PACKAGE>`: Remove an installed package.
|
||||||
- `build <SPEC>`: Build a package and create an archive without installing.
|
- `build <SPEC>`: Build a package and create an archive without installing.
|
||||||
- Use `--install-deps` to automatically install missing build/runtime/test dependencies before fetching/building.
|
- Use `--install-deps` to automatically install missing build/runtime/test dependencies before fetching/building.
|
||||||
|
- Use `--cleanup-deps` to remove dependencies auto-installed for the build after the command finishes; when combined with `--install`, runtime dependencies are kept.
|
||||||
- Missing test dependencies automatically disable test execution unless `--test-deps` or `[install].test_deps = true` is set.
|
- Missing test dependencies automatically disable test execution unless `--test-deps` or `[install].test_deps = true` is set.
|
||||||
- `update [PACKAGE ...]`: Update installed packages from configured repositories.
|
- `update [PACKAGE ...]`: Update installed packages from configured repositories.
|
||||||
- With no package names, updates every installed package that has a newer repo version available.
|
- With no package names, updates every installed package that has a newer repo version available.
|
||||||
|
|||||||
+60
-11
@@ -111,10 +111,33 @@ fn apply_replacement_rules(current: &mut [String], replacements: &[String], labe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sanitize_flag_list(values: Vec<String>, label: &str) -> Vec<String> {
|
||||||
|
let mut sanitized = Vec::with_capacity(values.len());
|
||||||
|
for value in values {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if trimmed == "-" {
|
||||||
|
crate::log_warn!(
|
||||||
|
"Dropping invalid {} entry '-'; compiler/linker flag lists cannot contain a bare dash",
|
||||||
|
label
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sanitized.push(trimmed.to_string());
|
||||||
|
}
|
||||||
|
sanitized
|
||||||
|
}
|
||||||
|
|
||||||
fn replaced_flags(values: &[String], replacements: &[String], label: &str) -> Vec<String> {
|
fn replaced_flags(values: &[String], replacements: &[String], label: &str) -> Vec<String> {
|
||||||
let mut current = values.to_vec();
|
let mut current = values.to_vec();
|
||||||
apply_replacement_rules(&mut current, replacements, label);
|
apply_replacement_rules(
|
||||||
current
|
&mut current,
|
||||||
|
replacements,
|
||||||
|
&format!("{label} replacement rules"),
|
||||||
|
);
|
||||||
|
sanitize_flag_list(current, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn install_dirs(flags: &crate::package::BuildFlags) -> InstallDirs {
|
pub(crate) fn install_dirs(flags: &crate::package::BuildFlags) -> InstallDirs {
|
||||||
@@ -145,25 +168,21 @@ pub(crate) fn install_dirs(flags: &crate::package::BuildFlags) -> InstallDirs {
|
|||||||
fn compiler_flag_sets(
|
fn compiler_flag_sets(
|
||||||
flags: &crate::package::BuildFlags,
|
flags: &crate::package::BuildFlags,
|
||||||
) -> (Vec<String>, Vec<String>, Vec<String>, Vec<String>) {
|
) -> (Vec<String>, Vec<String>, Vec<String>, Vec<String>) {
|
||||||
let mut cflags = replaced_flags(
|
let mut cflags = replaced_flags(&flags.cflags, &flags.replace_cflags, "build.flags.cflags");
|
||||||
&flags.cflags,
|
|
||||||
&flags.replace_cflags,
|
|
||||||
"build.flags.replace_cflags",
|
|
||||||
);
|
|
||||||
let mut cxxflags = replaced_flags(
|
let mut cxxflags = replaced_flags(
|
||||||
&flags.cxxflags,
|
&flags.cxxflags,
|
||||||
&flags.replace_cxxflags,
|
&flags.replace_cxxflags,
|
||||||
"build.flags.replace_cxxflags",
|
"build.flags.cxxflags",
|
||||||
);
|
);
|
||||||
let mut ldflags = replaced_flags(
|
let mut ldflags = replaced_flags(
|
||||||
&flags.ldflags,
|
&flags.ldflags,
|
||||||
&flags.replace_ldflags,
|
&flags.replace_ldflags,
|
||||||
"build.flags.replace_ldflags",
|
"build.flags.ldflags",
|
||||||
);
|
);
|
||||||
let ltoflags = replaced_flags(
|
let ltoflags = replaced_flags(
|
||||||
&flags.ltoflags,
|
&flags.ltoflags,
|
||||||
&flags.replace_ltoflags,
|
&flags.replace_ltoflags,
|
||||||
"build.flags.replace_ltoflags",
|
"build.flags.ltoflags",
|
||||||
);
|
);
|
||||||
|
|
||||||
if flags.use_lto && !ltoflags.is_empty() {
|
if flags.use_lto && !ltoflags.is_empty() {
|
||||||
@@ -179,7 +198,7 @@ pub(crate) fn effective_rustflags(flags: &crate::package::BuildFlags) -> Vec<Str
|
|||||||
replaced_flags(
|
replaced_flags(
|
||||||
&flags.rustflags,
|
&flags.rustflags,
|
||||||
&flags.replace_rustflags,
|
&flags.replace_rustflags,
|
||||||
"build.flags.replace_rustflags",
|
"build.flags.rustflags",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,6 +682,36 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_standard_build_env_drops_bare_dash_flags() {
|
||||||
|
let mut spec = mk_spec(vec!["-O2", "-", ""], vec!["-Wl,--as-needed", " "]);
|
||||||
|
spec.build.flags.cxxflags = vec!["-O2".into(), "-".into(), "-fno-exceptions".into()];
|
||||||
|
spec.build.flags.ltoflags = vec!["-".into(), "-flto=thin".into()];
|
||||||
|
spec.build.flags.use_lto = true;
|
||||||
|
|
||||||
|
let env = standard_build_env(&spec, None, true, true);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "CFLAGS" && v == "-O2 -flto=thin"),
|
||||||
|
"expected bare dash entries to be removed from CFLAGS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "CXXFLAGS" && v == "-O2 -fno-exceptions -flto=thin"),
|
||||||
|
"expected bare dash entries to be removed from CXXFLAGS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "LDFLAGS" && v == "-Wl,--as-needed -flto=thin"),
|
||||||
|
"expected blank and bare dash entries to be removed from LDFLAGS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=thin"),
|
||||||
|
"expected bare dash entries to be removed from LTOFLAGS"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_standard_build_env_skips_lto_injection_when_disabled() {
|
fn test_standard_build_env_skips_lto_injection_when_disabled() {
|
||||||
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
||||||
|
|||||||
+13
@@ -97,6 +97,10 @@ pub enum Commands {
|
|||||||
/// Automatically install missing dependencies before building
|
/// Automatically install missing dependencies before building
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
install_deps: bool,
|
install_deps: bool,
|
||||||
|
|
||||||
|
/// Remove dependencies auto-installed for this build after the command finishes
|
||||||
|
#[arg(long)]
|
||||||
|
cleanup_deps: bool,
|
||||||
},
|
},
|
||||||
/// Update installed packages from configured repositories
|
/// Update installed packages from configured repositories
|
||||||
Update {
|
Update {
|
||||||
@@ -360,4 +364,13 @@ mod tests {
|
|||||||
_ => panic!("expected build command"),
|
_ => panic!("expected build command"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_cleanup_deps_flag_is_parsed() {
|
||||||
|
let cli = Cli::try_parse_from(["depot", "build", "--cleanup-deps", "pkg.toml"]).unwrap();
|
||||||
|
match cli.command {
|
||||||
|
Commands::Build { cleanup_deps, .. } => assert!(cleanup_deps),
|
||||||
|
_ => panic!("expected build command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+753
-346
File diff suppressed because it is too large
Load Diff
+13
-4
@@ -14,6 +14,10 @@ fn format_licenses(licenses: &[String]) -> String {
|
|||||||
licenses.join(", ")
|
licenses.join(", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verbose_remove_output() -> bool {
|
||||||
|
std::env::var_os("DEPOT_VERBOSE_REMOVE").is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Installed package row from the local package database.
|
/// Installed package row from the local package database.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct InstalledPackageRecord {
|
pub struct InstalledPackageRecord {
|
||||||
@@ -268,7 +272,9 @@ pub fn remove_package(db_path: &Path, name: &str, rootfs: &Path) -> Result<()> {
|
|||||||
let path = rootfs.join(file);
|
let path = rootfs.join(file);
|
||||||
match fs::remove_file(&path) {
|
match fs::remove_file(&path) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
crate::log_info!(" Removed file: {}", file);
|
if verbose_remove_output() {
|
||||||
|
crate::log_info!(" Removed file: {}", file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
// Already gone, keep going.
|
// Already gone, keep going.
|
||||||
@@ -297,14 +303,15 @@ pub fn remove_package(db_path: &Path, name: &str, rootfs: &Path) -> Result<()> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
if other_owners > 0 {
|
if other_owners > 0 {
|
||||||
crate::log_info!(" Keeping directory (owned by other package): {}", dir);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to remove (will fail if not empty, which is fine)
|
// Try to remove (will fail if not empty, which is fine)
|
||||||
match fs::remove_dir(&path) {
|
match fs::remove_dir(&path) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
crate::log_info!(" Removed directory: {}", dir);
|
if verbose_remove_output() {
|
||||||
|
crate::log_info!(" Removed directory: {}", dir);
|
||||||
|
}
|
||||||
dirs_removed += 1;
|
dirs_removed += 1;
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
@@ -312,7 +319,9 @@ pub fn remove_package(db_path: &Path, name: &str, rootfs: &Path) -> Result<()> {
|
|||||||
}
|
}
|
||||||
Err(e) if e.raw_os_error() == Some(39) || e.raw_os_error() == Some(66) => {
|
Err(e) if e.raw_os_error() == Some(39) || e.raw_os_error() == Some(66) => {
|
||||||
// ENOTEMPTY (39 on Linux, 66 on macOS) - directory not empty
|
// ENOTEMPTY (39 on Linux, 66 on macOS) - directory not empty
|
||||||
crate::log_info!(" Keeping directory (not empty): {}", dir);
|
if verbose_remove_output() {
|
||||||
|
crate::log_info!(" Keeping directory (not empty): {}", dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Other errors (permission, etc.) - just skip silently
|
// Other errors (permission, etc.) - just skip silently
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ pub fn set_assume_yes(assume_yes: bool) {
|
|||||||
ASSUME_YES.store(assume_yes, Ordering::Relaxed);
|
ASSUME_YES.store(assume_yes, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn assume_yes_enabled() -> bool {
|
||||||
|
ASSUME_YES.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn prompt_yes_no(prompt: &str, default_yes: bool) -> Result<bool> {
|
pub fn prompt_yes_no(prompt: &str, default_yes: bool) -> Result<bool> {
|
||||||
if ASSUME_YES.load(Ordering::Relaxed) {
|
if ASSUME_YES.load(Ordering::Relaxed) {
|
||||||
info(format!("{} [auto-yes]", prompt));
|
info(format!("{} [auto-yes]", prompt));
|
||||||
|
|||||||
Reference in New Issue
Block a user