feat: improve depot UX and packaging workflow

- add shared terminal UI helpers for colored info/warn/success logs and yes/no prompts
- add Python build backend (PEP 517/setup.py wheel build + install) and expose python in interactive spec creation
- expand spec/build flags (post_configure, make phase targets/dirs, configure_file, no_strip, no_compress_man)
- support split-output staging with internal .depot output dirs, shell helpers (haul/subdestdir), and per-output deps/provides overrides
- improve staging with ELF auto-strip and zstd manpage compression (with opt-out flags)
- enforce runtime deps before install and support legacy lifecycle hook script names
- improve manual source handling with files/urls lists and stricter validation
This commit is contained in:
2026-02-22 14:52:45 -06:00
parent 0c676f6743
commit d63ad03e98
28 changed files with 4639 additions and 412 deletions
+184 -3
View File
@@ -17,6 +17,7 @@ impl fmt::Display for BuildType {
BuildType::CMake => write!(f, "CMake"),
BuildType::Meson => write!(f, "Meson"),
BuildType::Custom => write!(f, "Custom"),
BuildType::Python => write!(f, "Python"),
BuildType::Rust => write!(f, "Rust"),
BuildType::Makefile => write!(f, "Makefile"),
BuildType::Bin => write!(f, "Binary installer"),
@@ -177,8 +178,8 @@ fn expand_known_package_vars(input: &str, name: &str, version: &str) -> String {
}
pub fn create_interactive() -> Result<PackageSpec> {
println!("Interactive Package Specification Creator");
println!("-----------------------------------------");
crate::log_info!("Interactive Package Specification Creator");
crate::log_info!("-----------------------------------------");
// Ask early whether this is a GNU project so we can pre-fill homepage and
// avoid repeating the same question later for autotools sources.
@@ -237,6 +238,7 @@ pub fn create_interactive() -> Result<PackageSpec> {
BuildType::CMake,
BuildType::Meson,
BuildType::Makefile,
BuildType::Python,
BuildType::Rust,
BuildType::Custom,
BuildType::Bin,
@@ -409,6 +411,22 @@ pub fn create_interactive() -> Result<PackageSpec> {
}
if show_advanced && matches!(build_type, BuildType::Autotools) {
flags.configure_file = prompt_optional_text(
"Configure script path (optional):",
"Relative to source root, e.g. build-aux/configure",
)?;
flags.make_dirs = prompt_repeating_list(
"Make dir (build phase)",
"Relative to build dir, e.g. lib, libelf (empty = build root)",
)?;
flags.make_test_dirs = prompt_repeating_list(
"Make dir (test phase)",
"Relative to build dir, e.g. tests (empty = build root)",
)?;
flags.make_install_dirs = prompt_repeating_list(
"Make dir (install phase)",
"Relative to build dir, e.g. lib, apps (empty = build root)",
)?;
flags.skip_tests = Confirm::new("Skip automatic tests (make check/test)?")
.with_help_message(
"If enabled, Depot will not auto-run detected Autotools test targets",
@@ -418,7 +436,7 @@ pub fn create_interactive() -> Result<PackageSpec> {
}
if matches!(build_type, BuildType::Makefile) {
println!("Define Makefile build and install commands.");
crate::log_info!("Define Makefile build and install commands.");
flags.makefile_commands = prompt_repeating_list(
"makefile build command",
"Examples: make -j$(nproc), make all",
@@ -467,6 +485,10 @@ pub fn create_interactive() -> Result<PackageSpec> {
}
if show_advanced {
flags.post_configure = prompt_repeating_list(
"post_configure command",
"Runs after configure/setup, before build",
)?;
flags.post_compile =
prompt_repeating_list("post_compile command", "Runs after build, before install")?;
flags.post_install =
@@ -501,6 +523,8 @@ pub fn create_interactive() -> Result<PackageSpec> {
runtime: runtime_deps,
test: test_deps,
},
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
})
}
@@ -596,6 +620,7 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
BuildType::CMake => "cmake",
BuildType::Meson => "meson",
BuildType::Custom => "custom",
BuildType::Python => "python",
BuildType::Rust => "rust",
BuildType::Makefile => "makefile",
BuildType::Bin => "bin",
@@ -655,6 +680,15 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
if spec.build.flags.no_flags != defaults.no_flags {
flags_tbl.insert("no_flags".into(), Value::Boolean(spec.build.flags.no_flags));
}
if spec.build.flags.no_strip != defaults.no_strip {
flags_tbl.insert("no_strip".into(), Value::Boolean(spec.build.flags.no_strip));
}
if spec.build.flags.no_compress_man != defaults.no_compress_man {
flags_tbl.insert(
"no_compress_man".into(),
Value::Boolean(spec.build.flags.no_compress_man),
);
}
if spec.build.flags.skip_tests != defaults.skip_tests {
flags_tbl.insert(
"skip_tests".into(),
@@ -674,6 +708,12 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
),
);
}
if !spec.build.flags.configure_file.is_empty() {
flags_tbl.insert(
"configure_file".into(),
Value::String(spec.build.flags.configure_file.clone()),
);
}
if spec.build.flags.prefix != defaults.prefix {
flags_tbl.insert(
"prefix".into(),
@@ -723,6 +763,44 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
),
);
}
if !spec.build.flags.make_exec.is_empty() {
flags_tbl.insert(
"make_exec".into(),
Value::String(spec.build.flags.make_exec.clone()),
);
}
if !spec.build.flags.make_target.is_empty() {
flags_tbl.insert(
"make_target".into(),
Value::String(spec.build.flags.make_target.clone()),
);
}
if !spec.build.flags.make_targets.is_empty() {
flags_tbl.insert(
"make_targets".into(),
Value::Array(
spec.build
.flags
.make_targets
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.make_dirs.is_empty() {
flags_tbl.insert(
"make_dirs".into(),
Value::Array(
spec.build
.flags
.make_dirs
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.make_test_vars.is_empty() {
flags_tbl.insert(
"make_test_vars".into(),
@@ -736,6 +814,38 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
),
);
}
if !spec.build.flags.make_test_target.is_empty() {
flags_tbl.insert(
"make_test_target".into(),
Value::String(spec.build.flags.make_test_target.clone()),
);
}
if !spec.build.flags.make_test_targets.is_empty() {
flags_tbl.insert(
"make_test_targets".into(),
Value::Array(
spec.build
.flags
.make_test_targets
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.make_test_dirs.is_empty() {
flags_tbl.insert(
"make_test_dirs".into(),
Value::Array(
spec.build
.flags
.make_test_dirs
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.make_install_vars.is_empty() {
flags_tbl.insert(
"make_install_vars".into(),
@@ -749,6 +859,38 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
),
);
}
if !spec.build.flags.make_install_target.is_empty() {
flags_tbl.insert(
"make_install_target".into(),
Value::String(spec.build.flags.make_install_target.clone()),
);
}
if !spec.build.flags.make_install_targets.is_empty() {
flags_tbl.insert(
"make_install_targets".into(),
Value::Array(
spec.build
.flags
.make_install_targets
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.make_install_dirs.is_empty() {
flags_tbl.insert(
"make_install_dirs".into(),
Value::Array(
spec.build
.flags
.make_install_dirs
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.passthrough_env.is_empty() {
flags_tbl.insert(
"passthrough_env".into(),
@@ -775,6 +917,19 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
),
);
}
if !spec.build.flags.post_configure.is_empty() {
flags_tbl.insert(
"post_configure".into(),
Value::Array(
spec.build
.flags
.post_configure
.iter()
.map(|s| Value::String(s.clone()))
.collect(),
),
);
}
if !spec.build.flags.post_install.is_empty() {
flags_tbl.insert(
"post_install".into(),
@@ -949,6 +1104,8 @@ mod tests {
flags: BuildFlags::default(),
},
dependencies: Dependencies::default(),
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
};
@@ -996,6 +1153,8 @@ mod tests {
flags: BuildFlags::default(),
},
dependencies: Dependencies::default(),
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
};
@@ -1030,6 +1189,8 @@ mod tests {
flags: BuildFlags::default(),
},
dependencies: Dependencies::default(),
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
};
@@ -1044,6 +1205,8 @@ mod tests {
let mut flags = BuildFlags::default();
flags.source_subdir = "project/subdir".into();
flags.configure = vec!["--disable-static".into(), "--enable-foo".into()];
flags.configure_file = "build-aux/configure".into();
flags.post_configure = vec!["./configure-helper.sh".into()];
flags.post_compile = vec!["make check".into()];
flags.post_install = vec!["strip $DESTDIR/usr/bin/foo".into()];
flags.makefile_commands = vec!["make".into()];
@@ -1053,10 +1216,15 @@ mod tests {
flags.target = "x86_64-unknown-linux-gnu".into();
flags.keep = vec!["etc/locale.gen".into()];
flags.no_flags = true;
flags.no_strip = true;
flags.no_compress_man = true;
flags.skip_tests = true;
flags.make_vars = vec!["V=1".into()];
flags.make_dirs = vec!["lib".into(), "libelf".into()];
flags.make_test_vars = vec!["TESTS=unit".into()];
flags.make_test_dirs = vec!["tests".into()];
flags.make_install_vars = vec!["STRIPPROG=true".into()];
flags.make_install_dirs = vec!["lib".into(), "apps".into()];
let spec = PackageSpec {
package: PackageInfo {
@@ -1082,12 +1250,16 @@ mod tests {
flags,
},
dependencies: Dependencies::default(),
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
};
let toml = spec_to_minimal_toml(&spec).unwrap();
assert!(toml.contains("source_subdir = \"project/subdir\""));
assert!(toml.contains("configure = ["));
assert!(toml.contains("configure_file = \"build-aux/configure\""));
assert!(toml.contains("post_configure = ["));
assert!(toml.contains("post_compile = ["));
assert!(toml.contains("post_install = ["));
assert!(toml.contains("makefile_commands = ["));
@@ -1098,10 +1270,15 @@ mod tests {
assert!(toml.contains("keep = ["));
assert!(toml.contains("\"etc/locale.gen\""));
assert!(toml.contains("no_flags = true"));
assert!(toml.contains("no_strip = true"));
assert!(toml.contains("no_compress_man = true"));
assert!(toml.contains("skip_tests = true"));
assert!(toml.contains("make_vars = ["));
assert!(toml.contains("make_dirs = ["));
assert!(toml.contains("make_test_vars = ["));
assert!(toml.contains("make_test_dirs = ["));
assert!(toml.contains("make_install_vars = ["));
assert!(toml.contains("make_install_dirs = ["));
assert!(toml.contains("patches = ["));
assert!(toml.contains("post_extract = ["));
}
@@ -1132,6 +1309,8 @@ mod tests {
flags: BuildFlags::default(),
},
dependencies: Dependencies::default(),
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
};
@@ -1169,6 +1348,8 @@ mod tests {
runtime: vec![],
test: vec!["python".into(), "bats".into()],
},
package_alternatives: Default::default(),
package_dependencies: Default::default(),
spec_dir: PathBuf::from("."),
};