Implement update check functionality with version comparison and archive listing

- Added `check.rs` to handle package update checks, including logic for determining available updates from remote git repositories and archive listings.
- Introduced `versions.rs` to manage version patterns, comparison logic, and extraction of candidate versions from git refs and archive listings.
- Implemented `hex.rs` for encoding byte arrays to lowercase hexadecimal strings.
This commit is contained in:
2026-03-29 12:09:44 -05:00
parent 192cdc1af2
commit 57a364aa97
41 changed files with 8304 additions and 7267 deletions
+6
View File
@@ -134,6 +134,9 @@ pub fn build(
let expanded = expand_configure_arg(spec, arg, &env_vars);
configure_cmd.arg(expanded);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Autotools)? {
configure_cmd.arg(arg);
}
let status = crate::interrupts::command_status(&mut configure_cmd)
.with_context(|| format!("Failed to run configure in {}", build_dir.display()))?;
@@ -441,6 +444,9 @@ pub(crate) fn ensure_host_build(
let expanded = expand_configure_arg(&host_spec, arg, &env_vars);
configure_cmd.arg(expanded);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Autotools)? {
configure_cmd.arg(arg);
}
let status = crate::interrupts::command_status(&mut configure_cmd)
.with_context(|| format!("Failed to run host configure in {}", build_dir.display()))?;
+151 -45
View File
@@ -44,8 +44,7 @@ pub fn build(
}
// Extract prefix from configure flags (cmake-style -DCMAKE_INSTALL_PREFIX=)
let prefix = cmake_cache_entry_value(&flags.configure, "CMAKE_INSTALL_PREFIX")
.unwrap_or(flags.prefix.as_str());
let prefix = effective_cmake_install_prefix(flags);
// Generate toolchain file if cross-compiling
let toolchain_file = if let Some(cc_cfg) = cross {
@@ -68,7 +67,7 @@ pub fn build(
cmake_cmd.arg("-B").arg(&build_dir);
cmake_cmd.arg(format!("-DCMAKE_INSTALL_PREFIX={}", prefix));
cmake_cmd.arg("-DCMAKE_BUILD_TYPE=Release");
for arg in cmake_install_dir_args(flags) {
for arg in cmake_install_dir_args(flags, prefix) {
cmake_cmd.arg(arg);
}
for arg in cmake_lib32_target_args(flags, cross) {
@@ -98,6 +97,9 @@ pub fn build(
let expanded = expand_with_envs(flag, &env_vars);
cmake_cmd.arg(&expanded);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake)? {
cmake_cmd.arg(arg);
}
crate::builder::prepare_tool_command(&mut cmake_cmd, &env_vars);
@@ -280,8 +282,7 @@ pub(crate) fn ensure_host_build(
let env_vars =
crate::builder::standard_build_env(&host_spec, None, true, export_compiler_flags);
let prefix = cmake_cache_entry_value(&flags.configure, "CMAKE_INSTALL_PREFIX")
.unwrap_or(flags.prefix.as_str());
let prefix = effective_cmake_install_prefix(flags);
let mut state = StateTracker::new_with_namespace(&actual_src, Some("host"))?;
@@ -296,7 +297,7 @@ pub(crate) fn ensure_host_build(
cmake_cmd.arg("-B").arg(&build_dir);
cmake_cmd.arg(format!("-DCMAKE_INSTALL_PREFIX={}", prefix));
cmake_cmd.arg("-DCMAKE_BUILD_TYPE=Release");
for arg in cmake_install_dir_args(flags) {
for arg in cmake_install_dir_args(flags, prefix) {
cmake_cmd.arg(arg);
}
@@ -316,6 +317,9 @@ pub(crate) fn ensure_host_build(
let expanded = expand_with_envs(flag, &env_vars);
cmake_cmd.arg(&expanded);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake)? {
cmake_cmd.arg(arg);
}
crate::builder::prepare_tool_command(&mut cmake_cmd, &env_vars);
@@ -446,21 +450,72 @@ fn cmake_cache_entry_value<'a>(flags: &'a [String], variable: &str) -> Option<&'
})
}
fn cmake_install_dir_args(flags: &crate::package::BuildFlags) -> Vec<String> {
fn effective_cmake_install_prefix(flags: &crate::package::BuildFlags) -> &str {
cmake_cache_entry_value(&flags.configure, "CMAKE_INSTALL_PREFIX")
.unwrap_or(flags.prefix.as_str())
}
fn cmake_dir_value_for_prefix(prefix: &str, value: String) -> String {
let prefix_path = Path::new(prefix);
let value_path = Path::new(&value);
if prefix_path.is_absolute()
&& value_path.is_absolute()
&& let Ok(relative) = value_path.strip_prefix(prefix_path)
{
let relative = relative.to_string_lossy().replace('\\', "/");
return if relative.is_empty() {
".".to_string()
} else {
relative
};
}
value
}
fn cmake_install_dir_args(flags: &crate::package::BuildFlags, prefix: &str) -> Vec<String> {
let dirs = crate::builder::install_dirs(flags);
let defaults = [
("CMAKE_INSTALL_BINDIR", dirs.bindir),
("CMAKE_INSTALL_SBINDIR", dirs.sbindir),
("CMAKE_INSTALL_LIBDIR", dirs.libdir),
("CMAKE_INSTALL_LIBEXECDIR", dirs.libexecdir),
(
"CMAKE_INSTALL_BINDIR",
cmake_dir_value_for_prefix(prefix, dirs.bindir),
),
(
"CMAKE_INSTALL_SBINDIR",
cmake_dir_value_for_prefix(prefix, dirs.sbindir),
),
(
"CMAKE_INSTALL_LIBDIR",
cmake_dir_value_for_prefix(prefix, dirs.libdir),
),
(
"CMAKE_INSTALL_LIBEXECDIR",
cmake_dir_value_for_prefix(prefix, dirs.libexecdir),
),
("CMAKE_INSTALL_SYSCONFDIR", dirs.sysconfdir),
("CMAKE_INSTALL_LOCALSTATEDIR", dirs.localstatedir),
("CMAKE_INSTALL_SHAREDSTATEDIR", dirs.sharedstatedir),
("CMAKE_INSTALL_INCLUDEDIR", dirs.includedir),
("CMAKE_INSTALL_DATAROOTDIR", dirs.datarootdir),
("CMAKE_INSTALL_DATADIR", dirs.datadir),
("CMAKE_INSTALL_MANDIR", dirs.mandir),
("CMAKE_INSTALL_INFODIR", dirs.infodir),
(
"CMAKE_INSTALL_INCLUDEDIR",
cmake_dir_value_for_prefix(prefix, dirs.includedir),
),
(
"CMAKE_INSTALL_DATAROOTDIR",
cmake_dir_value_for_prefix(prefix, dirs.datarootdir),
),
(
"CMAKE_INSTALL_DATADIR",
cmake_dir_value_for_prefix(prefix, dirs.datadir),
),
(
"CMAKE_INSTALL_MANDIR",
cmake_dir_value_for_prefix(prefix, dirs.mandir),
),
(
"CMAKE_INSTALL_INFODIR",
cmake_dir_value_for_prefix(prefix, dirs.infodir),
),
];
defaults
@@ -710,15 +765,41 @@ mod tests {
}
#[test]
fn test_cmake_install_dir_args_include_defaults() {
let args = cmake_install_dir_args(&BuildFlags::default());
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=/usr/bin"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_SBINDIR=/usr/bin"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBDIR=/usr/lib"));
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_LIBEXECDIR=/usr/lib")
fn test_effective_cmake_install_prefix_prefers_explicit_configure_flag() {
let flags = BuildFlags {
prefix: "/usr".into(),
configure: vec!["-DCMAKE_INSTALL_PREFIX=/opt/soundtouch".into()],
..BuildFlags::default()
};
assert_eq!(effective_cmake_install_prefix(&flags), "/opt/soundtouch");
}
#[test]
fn test_cmake_dir_value_for_prefix_converts_prefix_owned_absolute_paths() {
assert_eq!(cmake_dir_value_for_prefix("/usr", "/usr/lib".into()), "lib");
assert_eq!(
cmake_dir_value_for_prefix("/usr", "/usr/share/man".into()),
"share/man"
);
}
#[test]
fn test_cmake_dir_value_for_prefix_keeps_non_prefix_absolute_paths() {
assert_eq!(cmake_dir_value_for_prefix("/usr", "/etc".into()), "/etc");
assert_eq!(
cmake_dir_value_for_prefix("/opt/pkg", "/usr/bin".into()),
"/usr/bin"
);
}
#[test]
fn test_cmake_install_dir_args_include_prefix_relative_defaults() {
let args = cmake_install_dir_args(&BuildFlags::default(), "/usr");
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=bin"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_SBINDIR=bin"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBDIR=lib"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBEXECDIR=lib"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_SYSCONFDIR=/etc"));
assert!(
args.iter()
@@ -730,23 +811,17 @@ mod tests {
);
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_INCLUDEDIR=/usr/include")
.any(|a| a == "-DCMAKE_INSTALL_INCLUDEDIR=include")
);
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_DATAROOTDIR=/usr/share")
.any(|a| a == "-DCMAKE_INSTALL_DATAROOTDIR=share")
);
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_DATADIR=share"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_MANDIR=share/man"));
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_DATADIR=/usr/share")
);
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_MANDIR=/usr/share/man")
);
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_INFODIR=/usr/share/info")
.any(|a| a == "-DCMAKE_INSTALL_INFODIR=share/info")
);
}
@@ -757,15 +832,9 @@ mod tests {
..BuildFlags::default()
};
let args = cmake_install_dir_args(&flags);
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_LIBDIR=/usr/lib32")
);
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_LIBEXECDIR=/usr/lib32")
);
let args = cmake_install_dir_args(&flags, "/usr");
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBDIR=lib32"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBEXECDIR=lib32"));
}
#[test]
@@ -832,7 +901,7 @@ mod tests {
..BuildFlags::default()
};
let args = cmake_install_dir_args(&flags);
let args = cmake_install_dir_args(&flags, "/usr");
assert!(
!args
.iter()
@@ -848,7 +917,44 @@ mod tests {
.iter()
.any(|a| a.starts_with("-DCMAKE_INSTALL_DATADIR="))
);
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=bin"));
}
#[test]
fn test_cmake_install_dir_args_convert_custom_prefix_owned_dirs() {
let flags = BuildFlags {
prefix: "/opt/soundtouch".into(),
bindir: "/opt/soundtouch/bin".into(),
libdir: "/opt/soundtouch/lib64".into(),
includedir: "/opt/soundtouch/include/soundtouch".into(),
datadir: "/opt/soundtouch/share".into(),
mandir: "/opt/soundtouch/share/man".into(),
..BuildFlags::default()
};
let args = cmake_install_dir_args(&flags, effective_cmake_install_prefix(&flags));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=bin"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBDIR=lib64"));
assert!(
args.iter()
.any(|a| a == "-DCMAKE_INSTALL_INCLUDEDIR=include/soundtouch")
);
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_DATADIR=share"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_MANDIR=share/man"));
}
#[test]
fn test_cmake_install_dir_args_keep_absolute_dirs_outside_prefix() {
let flags = BuildFlags {
prefix: "/opt/soundtouch".into(),
bindir: "/usr/bin".into(),
sysconfdir: "/etc".into(),
..BuildFlags::default()
};
let args = cmake_install_dir_args(&flags, effective_cmake_install_prefix(&flags));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=/usr/bin"));
assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_SYSCONFDIR=/etc"));
}
#[test]
+6
View File
@@ -65,6 +65,9 @@ pub fn build(
for arg in meson_setup_args(flags, cross_file.as_deref(), &env_vars) {
meson_cmd.arg(arg);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson)? {
meson_cmd.arg(arg);
}
crate::builder::prepare_tool_command(&mut meson_cmd, &env_vars);
@@ -229,6 +232,9 @@ pub(crate) fn ensure_host_build(
for arg in meson_setup_args(flags, None, &env_vars) {
meson_cmd.arg(arg);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson)? {
meson_cmd.arg(arg);
}
crate::builder::prepare_tool_command(&mut meson_cmd, &env_vars);
+54
View File
@@ -184,6 +184,47 @@ pub(crate) fn host_build_spec(spec: &PackageSpec) -> PackageSpec {
host_spec
}
pub(crate) fn requested_static_build() -> Result<Option<bool>> {
crate::build_options::requested_static_build()
}
pub(crate) fn static_build_args_for(build_type: BuildType) -> Result<Vec<String>> {
let Some(enabled) = requested_static_build()? else {
return Ok(Vec::new());
};
let args = match build_type {
BuildType::Autotools => vec![if enabled {
"--enable-static".to_string()
} else {
"--disable-static".to_string()
}],
BuildType::CMake => vec![format!(
"-DBUILD_SHARED_LIBS={}",
if enabled { "OFF" } else { "ON" }
)],
BuildType::Meson => vec![format!(
"-Ddefault_library={}",
if enabled { "static" } else { "shared" }
)],
BuildType::Perl => vec![format!(
"LINKTYPE={}",
if enabled { "static" } else { "dynamic" }
)],
_ => Vec::new(),
};
Ok(args)
}
pub(crate) fn build_tool_package_option(build_type: BuildType) -> Option<&'static str> {
crate::build_options::build_tool_package_option(build_type)
}
pub(crate) fn requested_build_tool_package(build_type: BuildType) -> Option<String> {
crate::build_options::requested_build_tool_package(build_type)
}
fn configured_install_dir(value: &str, default: &str) -> String {
let trimmed = value.trim();
if trimmed.is_empty() {
@@ -889,6 +930,19 @@ mod tests {
);
}
#[test]
fn test_build_tool_package_option_maps_supported_builders() {
assert_eq!(
build_tool_package_option(BuildType::Meson),
Some("DEPOT_MESON_PACKAGE")
);
assert_eq!(
build_tool_package_option(BuildType::CMake),
Some("DEPOT_CMAKE_PACKAGE")
);
assert_eq!(build_tool_package_option(BuildType::Bin), None);
}
#[test]
fn test_standard_build_env_exports_native_linker_and_cpp() {
let mut spec = mk_spec(Vec::new(), Vec::new());
+3
View File
@@ -59,6 +59,9 @@ pub fn build(
for arg in &flags.configure {
configure_cmd.arg(spec.expand_vars(arg));
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Perl)? {
configure_cmd.arg(arg);
}
crate::builder::prepare_tool_command(&mut configure_cmd, &env_vars);
let status = command_status_with_sh_fallback(&mut configure_cmd).with_context(|| {