Refactor dependency handling for lib32 packages
- Updated the `CrossConfig` to change the generator name from `nyapm` to `depot`. - Introduced a new `RequestedOutputs` enum to manage different output types for dependencies. - Refactored dependency collection functions to support lib32-specific dependencies. - Enhanced the `check_build_deps`, `check_runtime_deps`, and `check_test_deps` functions to accept output types. - Modified the `print_dep_status` function to handle output-specific dependency statuses. - Added support for lib32 dependencies in the `PackageSpec` struct and related methods. - Updated the `source_deps_for_install` function to conditionally include lib32 dependencies. - Implemented tests to verify the correct handling of lib32 dependencies and their overrides. - Adjusted legacy hook collection to support lib32-prefixed scripts.
This commit is contained in:
Generated
+1
-1
@@ -418,7 +418,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "depot"
|
name = "depot"
|
||||||
version = "0.18.1"
|
version = "0.19.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ar",
|
"ar",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "depot"
|
name = "depot"
|
||||||
version = "0.18.1"
|
version = "0.19.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'depot',
|
'depot',
|
||||||
version: '0.18.1',
|
version: '0.19.0',
|
||||||
meson_version: '>=0.60.0',
|
meson_version: '>=0.60.0',
|
||||||
default_options: ['buildtype=release'],
|
default_options: ['buildtype=release'],
|
||||||
)
|
)
|
||||||
|
|||||||
+197
-46
@@ -91,6 +91,7 @@ struct ChildInstallCommandOptions<'a> {
|
|||||||
no_flags: bool,
|
no_flags: bool,
|
||||||
cross_prefix: Option<&'a str>,
|
cross_prefix: Option<&'a str>,
|
||||||
clean: bool,
|
clean: bool,
|
||||||
|
lib32_only: bool,
|
||||||
install_test_deps: bool,
|
install_test_deps: bool,
|
||||||
install_context: Option<&'a str>,
|
install_context: Option<&'a str>,
|
||||||
dep_chain: Option<&'a str>,
|
dep_chain: Option<&'a str>,
|
||||||
@@ -131,6 +132,9 @@ fn run_install_command_with_program(
|
|||||||
if options.clean {
|
if options.clean {
|
||||||
cmd.arg("--clean");
|
cmd.arg("--clean");
|
||||||
}
|
}
|
||||||
|
if options.lib32_only {
|
||||||
|
cmd.arg("--lib32-only");
|
||||||
|
}
|
||||||
if options.install_test_deps {
|
if options.install_test_deps {
|
||||||
cmd.arg("--test-deps");
|
cmd.arg("--test-deps");
|
||||||
}
|
}
|
||||||
@@ -214,6 +218,7 @@ fn run_child_install_command(
|
|||||||
no_flags: options.no_flags,
|
no_flags: options.no_flags,
|
||||||
cross_prefix: options.cross_prefix,
|
cross_prefix: options.cross_prefix,
|
||||||
clean: options.clean,
|
clean: options.clean,
|
||||||
|
lib32_only: false,
|
||||||
install_test_deps: options.install_test_deps,
|
install_test_deps: options.install_test_deps,
|
||||||
install_context: None,
|
install_context: None,
|
||||||
dep_chain: None,
|
dep_chain: None,
|
||||||
@@ -381,6 +386,7 @@ fn package_spec_from_archive_metadata(metadata: &toml::Value) -> package::Packag
|
|||||||
runtime: parse_dependency_list(metadata, "runtime"),
|
runtime: parse_dependency_list(metadata, "runtime"),
|
||||||
test: Vec::new(),
|
test: Vec::new(),
|
||||||
optional: parse_dependency_list(metadata, "optional"),
|
optional: parse_dependency_list(metadata, "optional"),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
@@ -454,6 +460,7 @@ fn package_spec_from_repo_record(
|
|||||||
runtime: record.runtime_dependencies.clone(),
|
runtime: record.runtime_dependencies.clone(),
|
||||||
test: Vec::new(),
|
test: Vec::new(),
|
||||||
optional: record.optional_dependencies.clone(),
|
optional: record.optional_dependencies.clone(),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
@@ -613,15 +620,16 @@ fn build_type_runs_automatic_tests(spec: &package::PackageSpec) -> bool {
|
|||||||
fn maybe_disable_tests_for_missing_deps(
|
fn maybe_disable_tests_for_missing_deps(
|
||||||
pkg_spec: &mut package::PackageSpec,
|
pkg_spec: &mut package::PackageSpec,
|
||||||
db_path: &Path,
|
db_path: &Path,
|
||||||
|
requested_outputs: deps::RequestedOutputs,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if pkg_spec.build.flags.skip_tests
|
if pkg_spec.build.flags.skip_tests
|
||||||
|| !build_type_runs_automatic_tests(pkg_spec)
|
|| !build_type_runs_automatic_tests(pkg_spec)
|
||||||
|| pkg_spec.dependencies.test.is_empty()
|
|| deps::declared_test_deps(pkg_spec, requested_outputs).is_empty()
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let missing_test = deps::check_test_deps(pkg_spec, db_path)?;
|
let missing_test = deps::check_test_deps_for_outputs(pkg_spec, db_path, requested_outputs)?;
|
||||||
if !missing_test.is_empty() {
|
if !missing_test.is_empty() {
|
||||||
ui::warn(format!(
|
ui::warn(format!(
|
||||||
"Missing test dependencies: {}. Tests will be skipped.",
|
"Missing test dependencies: {}. Tests will be skipped.",
|
||||||
@@ -655,8 +663,24 @@ fn maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_install_test_deps(pkg_spec: &package::PackageSpec, install_test_deps: bool) -> bool {
|
fn requested_outputs(pkg_spec: &package::PackageSpec, lib32_only: bool) -> deps::RequestedOutputs {
|
||||||
install_test_deps && !pkg_spec.build.flags.skip_tests && !pkg_spec.dependencies.test.is_empty()
|
if lib32_only {
|
||||||
|
deps::RequestedOutputs::Lib32Only
|
||||||
|
} else if pkg_spec.build.flags.build_32 {
|
||||||
|
deps::RequestedOutputs::PrimaryAndLib32
|
||||||
|
} else {
|
||||||
|
deps::RequestedOutputs::PrimaryOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_install_test_deps(
|
||||||
|
pkg_spec: &package::PackageSpec,
|
||||||
|
install_test_deps: bool,
|
||||||
|
requested_outputs: deps::RequestedOutputs,
|
||||||
|
) -> bool {
|
||||||
|
install_test_deps
|
||||||
|
&& !pkg_spec.build.flags.skip_tests
|
||||||
|
&& !deps::declared_test_deps(pkg_spec, requested_outputs).is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_build_workspace(config: &config::Config) -> Result<()> {
|
fn clean_build_workspace(config: &config::Config) -> Result<()> {
|
||||||
@@ -817,9 +841,12 @@ fn make_lib32_build_spec(base: &package::PackageSpec) -> package::PackageSpec {
|
|||||||
|
|
||||||
fn make_lib32_package_spec(base: &package::PackageSpec) -> package::PackageSpec {
|
fn make_lib32_package_spec(base: &package::PackageSpec) -> package::PackageSpec {
|
||||||
let mut spec = base.clone();
|
let mut spec = base.clone();
|
||||||
spec.package.name = lib32_package_name(&base.package.name);
|
let lib32_name = lib32_package_name(&base.package.name);
|
||||||
|
spec.package.name = lib32_name.clone();
|
||||||
// The lib32 pass currently emits a single companion package from /usr/lib32.
|
// The lib32 pass currently emits a single companion package from /usr/lib32.
|
||||||
spec.packages.clear();
|
spec.packages.clear();
|
||||||
|
spec.alternatives = base.alternatives_for_output(&lib32_name);
|
||||||
|
spec.dependencies = base.dependencies_for_output(&lib32_name);
|
||||||
spec
|
spec
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2312,6 +2339,7 @@ fn run_update_install_command(
|
|||||||
no_flags: options.no_flags,
|
no_flags: options.no_flags,
|
||||||
cross_prefix: options.cross_prefix,
|
cross_prefix: options.cross_prefix,
|
||||||
clean: options.clean,
|
clean: options.clean,
|
||||||
|
lib32_only: false,
|
||||||
install_test_deps: options.install_test_deps,
|
install_test_deps: options.install_test_deps,
|
||||||
install_context: Some(INSTALL_CONTEXT_UPDATE),
|
install_context: Some(INSTALL_CONTEXT_UPDATE),
|
||||||
dep_chain: None,
|
dep_chain: None,
|
||||||
@@ -2406,6 +2434,7 @@ fn run_update_command(
|
|||||||
prefer_binary: config.repo_settings.prefer_binary,
|
prefer_binary: config.repo_settings.prefer_binary,
|
||||||
local_sibling_root: None,
|
local_sibling_root: None,
|
||||||
include_test_deps: options.install_test_deps,
|
include_test_deps: options.install_test_deps,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
print_plan_summary(&dep_plan);
|
print_plan_summary(&dep_plan);
|
||||||
@@ -2425,6 +2454,7 @@ fn run_update_command(
|
|||||||
clean: options.clean,
|
clean: options.clean,
|
||||||
dry_run: false,
|
dry_run: false,
|
||||||
confirm_installation: false,
|
confirm_installation: false,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
install_test_deps: options.install_test_deps,
|
install_test_deps: options.install_test_deps,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
@@ -2901,9 +2931,21 @@ struct InstallPlanExecutionOptions<'a> {
|
|||||||
clean: bool,
|
clean: bool,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
confirm_installation: bool,
|
confirm_installation: bool,
|
||||||
|
lib32_only_requested_specs: bool,
|
||||||
install_test_deps: bool,
|
install_test_deps: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn step_requests_only_lib32(
|
||||||
|
step: &planner::PlannedStep,
|
||||||
|
options: &InstallPlanExecutionOptions<'_>,
|
||||||
|
) -> bool {
|
||||||
|
options.lib32_only_requested_specs
|
||||||
|
&& step
|
||||||
|
.requested_by
|
||||||
|
.iter()
|
||||||
|
.any(|reason| reason.starts_with("requested "))
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_install_plan_with_child_commands(
|
fn execute_install_plan_with_child_commands(
|
||||||
plan: &planner::ExecutionPlan,
|
plan: &planner::ExecutionPlan,
|
||||||
rootfs: &Path,
|
rootfs: &Path,
|
||||||
@@ -2946,10 +2988,11 @@ fn execute_install_plan_with_child_commands(
|
|||||||
let mut spec = package::PackageSpec::from_file(path)
|
let mut spec = package::PackageSpec::from_file(path)
|
||||||
.with_context(|| format!("Failed to parse spec {}", path.display()))?;
|
.with_context(|| format!("Failed to parse spec {}", path.display()))?;
|
||||||
spec.apply_config(config);
|
spec.apply_config(config);
|
||||||
|
let lib32_only = step_requests_only_lib32(step, &options);
|
||||||
conflict_subjects.extend(install_conflict_subjects_for_spec(
|
conflict_subjects.extend(install_conflict_subjects_for_spec(
|
||||||
&spec,
|
&spec,
|
||||||
true,
|
!lib32_only,
|
||||||
spec.build.flags.build_32,
|
spec.build.flags.build_32 || lib32_only,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
planner::PlanOrigin::Binary { record, .. } => {
|
planner::PlanOrigin::Binary { record, .. } => {
|
||||||
@@ -2966,29 +3009,40 @@ fn execute_install_plan_with_child_commands(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if should_delegate_live_rootfs_installs(rootfs) {
|
if should_delegate_live_rootfs_installs(rootfs) {
|
||||||
let mut install_requests = Vec::new();
|
|
||||||
for step in actionable_steps {
|
for step in actionable_steps {
|
||||||
match &step.origin {
|
let install_request = match &step.origin {
|
||||||
planner::PlanOrigin::Source { path, .. } => {
|
planner::PlanOrigin::Source { path, .. } => path.clone(),
|
||||||
install_requests.push(path.clone());
|
|
||||||
}
|
|
||||||
planner::PlanOrigin::Binary { repo_name, record } => {
|
planner::PlanOrigin::Binary { repo_name, record } => {
|
||||||
let repo_cfg = config.binary_repos.get(repo_name).with_context(|| {
|
let repo_cfg = config.binary_repos.get(repo_name).with_context(|| {
|
||||||
format!("Binary repo '{}' not found in config", repo_name)
|
format!("Binary repo '{}' not found in config", repo_name)
|
||||||
})?;
|
})?;
|
||||||
let archive_path = db::repo::fetch_binary_package_archive(
|
db::repo::fetch_binary_package_archive(
|
||||||
repo_name,
|
repo_name,
|
||||||
repo_cfg,
|
repo_cfg,
|
||||||
rootfs,
|
rootfs,
|
||||||
record,
|
record,
|
||||||
&config.package_cache_dir,
|
&config.package_cache_dir,
|
||||||
)?;
|
)?
|
||||||
install_requests.push(archive_path);
|
|
||||||
}
|
}
|
||||||
planner::PlanOrigin::Installed => {}
|
planner::PlanOrigin::Installed => continue,
|
||||||
}
|
};
|
||||||
|
run_install_command_with_program(
|
||||||
|
&std::env::current_exe().context("Failed to locate depot executable")?,
|
||||||
|
&[install_request],
|
||||||
|
rootfs,
|
||||||
|
ChildInstallCommandOptions {
|
||||||
|
no_deps: true,
|
||||||
|
assume_yes: true,
|
||||||
|
no_flags: options.no_flags,
|
||||||
|
cross_prefix: options.cross_prefix,
|
||||||
|
clean: options.clean,
|
||||||
|
lib32_only: step_requests_only_lib32(step, &options),
|
||||||
|
install_test_deps: options.install_test_deps,
|
||||||
|
install_context: None,
|
||||||
|
dep_chain: None,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
run_child_install_command(&install_requests, rootfs, options)?;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3197,6 +3251,9 @@ fn execute_install_plan_with_child_commands(
|
|||||||
if options.clean {
|
if options.clean {
|
||||||
cmd.arg("--clean");
|
cmd.arg("--clean");
|
||||||
}
|
}
|
||||||
|
if step_requests_only_lib32(step, &options) {
|
||||||
|
cmd.arg("--lib32-only");
|
||||||
|
}
|
||||||
cmd.arg("install").arg(path);
|
cmd.arg("install").arg(path);
|
||||||
|
|
||||||
let status = cmd
|
let status = cmd
|
||||||
@@ -3466,6 +3523,8 @@ fn run_direct_install_request(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requested_outputs = requested_outputs(&pkg_spec, options.lib32_only);
|
||||||
|
|
||||||
let mut conflict_subjects = install_conflict_subjects_for_spec(
|
let mut conflict_subjects = install_conflict_subjects_for_spec(
|
||||||
&pkg_spec,
|
&pkg_spec,
|
||||||
!options.lib32_only,
|
!options.lib32_only,
|
||||||
@@ -3506,8 +3565,11 @@ fn run_direct_install_request(
|
|||||||
let db_path = config.installed_db_path(options.rootfs);
|
let db_path = config.installed_db_path(options.rootfs);
|
||||||
|
|
||||||
if staging_dir.is_none() {
|
if staging_dir.is_none() {
|
||||||
if options.no_deps && should_install_test_deps(&pkg_spec, options.install_test_deps) {
|
if options.no_deps
|
||||||
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
|
&& should_install_test_deps(&pkg_spec, options.install_test_deps, requested_outputs)
|
||||||
|
{
|
||||||
|
let missing_test =
|
||||||
|
deps::check_test_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if !missing_test.is_empty()
|
if !missing_test.is_empty()
|
||||||
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
||||||
&mut pkg_spec,
|
&mut pkg_spec,
|
||||||
@@ -3517,19 +3579,20 @@ fn run_direct_install_request(
|
|||||||
{
|
{
|
||||||
anyhow::bail!("Missing test dependencies: {}", missing_test.join(", "));
|
anyhow::bail!("Missing test dependencies: {}", missing_test.join(", "));
|
||||||
}
|
}
|
||||||
} else if options.no_deps || !should_install_test_deps(&pkg_spec, options.install_test_deps)
|
} else if options.no_deps
|
||||||
|
|| !should_install_test_deps(&pkg_spec, options.install_test_deps, requested_outputs)
|
||||||
{
|
{
|
||||||
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path)?;
|
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path, requested_outputs)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check dependencies and prompt for auto-install if needed
|
// Check dependencies and prompt for auto-install if needed
|
||||||
if !options.no_deps {
|
if !options.no_deps {
|
||||||
deps::print_dep_status(&pkg_spec, &db_path)?;
|
deps::print_dep_status_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
|
|
||||||
let missing_required = merge_missing_dependencies(
|
let missing_required = merge_missing_dependencies(
|
||||||
deps::check_build_deps(&pkg_spec, &db_path)?,
|
deps::check_build_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?,
|
||||||
deps::check_runtime_deps(&pkg_spec, &db_path)?,
|
deps::check_runtime_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?,
|
||||||
);
|
);
|
||||||
if !missing_required.is_empty() {
|
if !missing_required.is_empty() {
|
||||||
// Check for dependency cycles via DEPOT_DEPCHAIN env var
|
// Check for dependency cycles via DEPOT_DEPCHAIN env var
|
||||||
@@ -3587,6 +3650,7 @@ fn run_direct_install_request(
|
|||||||
no_flags: options.no_flags,
|
no_flags: options.no_flags,
|
||||||
cross_prefix: options.cross_prefix,
|
cross_prefix: options.cross_prefix,
|
||||||
clean: options.clean,
|
clean: options.clean,
|
||||||
|
lib32_only: false,
|
||||||
install_test_deps: options.install_test_deps,
|
install_test_deps: options.install_test_deps,
|
||||||
install_context: None,
|
install_context: None,
|
||||||
dep_chain: Some(&new_chain),
|
dep_chain: Some(&new_chain),
|
||||||
@@ -3596,10 +3660,11 @@ fn run_direct_install_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enforce required dependencies before building/installing.
|
// Enforce required dependencies before building/installing.
|
||||||
deps::require_build_deps(&pkg_spec, &db_path)?;
|
deps::require_build_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
deps::require_runtime_deps(&pkg_spec, &db_path)?;
|
deps::require_runtime_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if should_install_test_deps(&pkg_spec, options.install_test_deps) {
|
if should_install_test_deps(&pkg_spec, options.install_test_deps, requested_outputs) {
|
||||||
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
|
let missing_test =
|
||||||
|
deps::check_test_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if !missing_test.is_empty() {
|
if !missing_test.is_empty() {
|
||||||
let pkg_index =
|
let pkg_index =
|
||||||
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
|
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
|
||||||
@@ -3645,6 +3710,7 @@ fn run_direct_install_request(
|
|||||||
no_flags: options.no_flags,
|
no_flags: options.no_flags,
|
||||||
cross_prefix: options.cross_prefix,
|
cross_prefix: options.cross_prefix,
|
||||||
clean: options.clean,
|
clean: options.clean,
|
||||||
|
lib32_only: false,
|
||||||
install_test_deps: options.install_test_deps,
|
install_test_deps: options.install_test_deps,
|
||||||
install_context: None,
|
install_context: None,
|
||||||
dep_chain: None,
|
dep_chain: None,
|
||||||
@@ -3661,8 +3727,9 @@ fn run_direct_install_request(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_install_test_deps(&pkg_spec, options.install_test_deps) {
|
if should_install_test_deps(&pkg_spec, options.install_test_deps, requested_outputs) {
|
||||||
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
|
let missing_test =
|
||||||
|
deps::check_test_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if !missing_test.is_empty()
|
if !missing_test.is_empty()
|
||||||
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
||||||
&mut pkg_spec,
|
&mut pkg_spec,
|
||||||
@@ -3670,7 +3737,7 @@ fn run_direct_install_request(
|
|||||||
"Requested test dependencies are still missing",
|
"Requested test dependencies are still missing",
|
||||||
)?
|
)?
|
||||||
{
|
{
|
||||||
deps::require_test_deps(&pkg_spec, &db_path)?;
|
deps::require_test_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3817,6 +3884,7 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
prefer_binary: config.repo_settings.prefer_binary,
|
prefer_binary: config.repo_settings.prefer_binary,
|
||||||
local_sibling_root: shared_local_sibling_root(&planned_spec_paths),
|
local_sibling_root: shared_local_sibling_root(&planned_spec_paths),
|
||||||
include_test_deps: install_test_deps,
|
include_test_deps: install_test_deps,
|
||||||
|
lib32_only_requested_specs: cli.lib32_only,
|
||||||
};
|
};
|
||||||
let plan = if planned_targets.len() == 1 {
|
let plan = if planned_targets.len() == 1 {
|
||||||
planner::build_install_plan(
|
planner::build_install_plan(
|
||||||
@@ -3844,6 +3912,7 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
clean: cli.clean,
|
clean: cli.clean,
|
||||||
dry_run: cli.dry_run,
|
dry_run: cli.dry_run,
|
||||||
confirm_installation: true,
|
confirm_installation: true,
|
||||||
|
lib32_only_requested_specs: cli.lib32_only,
|
||||||
install_test_deps,
|
install_test_deps,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
@@ -3913,6 +3982,7 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
|
|
||||||
// Apply system overrides
|
// Apply system overrides
|
||||||
pkg_spec.apply_config(&config);
|
pkg_spec.apply_config(&config);
|
||||||
|
let requested_outputs = requested_outputs(&pkg_spec, cli.lib32_only);
|
||||||
|
|
||||||
// Ensure database directory exists
|
// Ensure database directory exists
|
||||||
std::fs::create_dir_all(&config.db_dir).with_context(|| {
|
std::fs::create_dir_all(&config.db_dir).with_context(|| {
|
||||||
@@ -3923,8 +3993,11 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
})?;
|
})?;
|
||||||
let db_path = config.installed_db_path(&cli.rootfs);
|
let db_path = config.installed_db_path(&cli.rootfs);
|
||||||
|
|
||||||
if cli.no_deps && should_install_test_deps(&pkg_spec, install_test_deps) {
|
if cli.no_deps
|
||||||
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
|
&& should_install_test_deps(&pkg_spec, install_test_deps, requested_outputs)
|
||||||
|
{
|
||||||
|
let missing_test =
|
||||||
|
deps::check_test_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if !missing_test.is_empty()
|
if !missing_test.is_empty()
|
||||||
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
||||||
&mut pkg_spec,
|
&mut pkg_spec,
|
||||||
@@ -3934,16 +4007,18 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
{
|
{
|
||||||
anyhow::bail!("Missing test dependencies: {}", missing_test.join(", "));
|
anyhow::bail!("Missing test dependencies: {}", missing_test.join(", "));
|
||||||
}
|
}
|
||||||
} else if cli.no_deps || !should_install_test_deps(&pkg_spec, install_test_deps) {
|
} else if cli.no_deps
|
||||||
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path)?;
|
|| !should_install_test_deps(&pkg_spec, install_test_deps, requested_outputs)
|
||||||
|
{
|
||||||
|
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path, requested_outputs)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check build dependencies
|
// Check build dependencies
|
||||||
if !cli.no_deps {
|
if !cli.no_deps {
|
||||||
deps::print_dep_status(&pkg_spec, &db_path)?;
|
deps::print_dep_status_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
let missing_required = merge_missing_dependencies(
|
let missing_required = merge_missing_dependencies(
|
||||||
deps::check_build_deps(&pkg_spec, &db_path)?,
|
deps::check_build_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?,
|
||||||
deps::check_runtime_deps(&pkg_spec, &db_path)?,
|
deps::check_runtime_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?,
|
||||||
);
|
);
|
||||||
if !missing_required.is_empty() {
|
if !missing_required.is_empty() {
|
||||||
ui::warn(format!(
|
ui::warn(format!(
|
||||||
@@ -3963,6 +4038,7 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
prefer_binary: config.repo_settings.prefer_binary,
|
prefer_binary: config.repo_settings.prefer_binary,
|
||||||
local_sibling_root,
|
local_sibling_root,
|
||||||
include_test_deps: install_test_deps,
|
include_test_deps: install_test_deps,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
print_plan_summary(&dep_plan);
|
print_plan_summary(&dep_plan);
|
||||||
@@ -3987,14 +4063,16 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
clean: cli.clean,
|
clean: cli.clean,
|
||||||
dry_run: cli.dry_run,
|
dry_run: cli.dry_run,
|
||||||
confirm_installation: false,
|
confirm_installation: false,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
install_test_deps,
|
install_test_deps,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
deps::require_build_deps(&pkg_spec, &db_path)?;
|
deps::require_build_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
deps::require_runtime_deps(&pkg_spec, &db_path)?;
|
deps::require_runtime_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if should_install_test_deps(&pkg_spec, install_test_deps) {
|
if should_install_test_deps(&pkg_spec, install_test_deps, requested_outputs) {
|
||||||
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
|
let missing_test =
|
||||||
|
deps::check_test_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
|
||||||
if !missing_test.is_empty() {
|
if !missing_test.is_empty() {
|
||||||
let local_sibling_root = spec_path
|
let local_sibling_root = spec_path
|
||||||
.parent()
|
.parent()
|
||||||
@@ -4009,6 +4087,7 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
prefer_binary: config.repo_settings.prefer_binary,
|
prefer_binary: config.repo_settings.prefer_binary,
|
||||||
local_sibling_root,
|
local_sibling_root,
|
||||||
include_test_deps: install_test_deps,
|
include_test_deps: install_test_deps,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Ok(plan) => plan,
|
Ok(plan) => plan,
|
||||||
@@ -4053,14 +4132,20 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
clean: cli.clean,
|
clean: cli.clean,
|
||||||
dry_run: cli.dry_run,
|
dry_run: cli.dry_run,
|
||||||
confirm_installation: false,
|
confirm_installation: false,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
install_test_deps,
|
install_test_deps,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_install_test_deps(&pkg_spec, install_test_deps) {
|
if should_install_test_deps(&pkg_spec, install_test_deps, requested_outputs)
|
||||||
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
|
{
|
||||||
|
let missing_test = deps::check_test_deps_for_outputs(
|
||||||
|
&pkg_spec,
|
||||||
|
&db_path,
|
||||||
|
requested_outputs,
|
||||||
|
)?;
|
||||||
if !missing_test.is_empty()
|
if !missing_test.is_empty()
|
||||||
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
|
||||||
&mut pkg_spec,
|
&mut pkg_spec,
|
||||||
@@ -4068,7 +4153,11 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
"Requested test dependencies are still missing",
|
"Requested test dependencies are still missing",
|
||||||
)?
|
)?
|
||||||
{
|
{
|
||||||
deps::require_test_deps(&pkg_spec, &db_path)?;
|
deps::require_test_deps_for_outputs(
|
||||||
|
&pkg_spec,
|
||||||
|
&db_path,
|
||||||
|
requested_outputs,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4217,6 +4306,7 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
clean: cli.clean,
|
clean: cli.clean,
|
||||||
dry_run: cli.dry_run,
|
dry_run: cli.dry_run,
|
||||||
confirm_installation: false,
|
confirm_installation: false,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
install_test_deps,
|
install_test_deps,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
@@ -5798,6 +5888,7 @@ optional = []
|
|||||||
no_flags: true,
|
no_flags: true,
|
||||||
cross_prefix: Some("x86_64-linux-musl"),
|
cross_prefix: Some("x86_64-linux-musl"),
|
||||||
clean: true,
|
clean: true,
|
||||||
|
lib32_only: false,
|
||||||
install_test_deps: true,
|
install_test_deps: true,
|
||||||
install_context: None,
|
install_context: None,
|
||||||
dep_chain: Some("parent"),
|
dep_chain: Some("parent"),
|
||||||
@@ -5825,6 +5916,46 @@ optional = []
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn child_install_command_includes_lib32_only_flag_when_requested() -> Result<()> {
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let temp = tempfile::tempdir().context("Failed to create temp dir")?;
|
||||||
|
let script_path = temp.path().join("capture-lib32-child-install.sh");
|
||||||
|
let args_path = temp.path().join("args.txt");
|
||||||
|
let script = format!(
|
||||||
|
"#!/bin/sh\nprintf '%s\\n' \"$@\" > \"{}\"\n",
|
||||||
|
args_path.display()
|
||||||
|
);
|
||||||
|
fs::write(&script_path, script)
|
||||||
|
.with_context(|| format!("Failed to write {}", script_path.display()))?;
|
||||||
|
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755))
|
||||||
|
.with_context(|| format!("Failed to chmod {}", script_path.display()))?;
|
||||||
|
|
||||||
|
run_install_command_with_program(
|
||||||
|
&script_path,
|
||||||
|
&[PathBuf::from("/tmp/pkg.toml")],
|
||||||
|
Path::new("/"),
|
||||||
|
ChildInstallCommandOptions {
|
||||||
|
no_deps: true,
|
||||||
|
assume_yes: true,
|
||||||
|
no_flags: false,
|
||||||
|
cross_prefix: None,
|
||||||
|
clean: false,
|
||||||
|
lib32_only: true,
|
||||||
|
install_test_deps: false,
|
||||||
|
install_context: None,
|
||||||
|
dep_chain: None,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let captured_args = fs::read_to_string(&args_path)
|
||||||
|
.with_context(|| format!("Failed to read {}", args_path.display()))?;
|
||||||
|
assert!(captured_args.lines().any(|line| line == "--lib32-only"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn child_install_command_propagates_install_context_env() -> Result<()> {
|
fn child_install_command_propagates_install_context_env() -> Result<()> {
|
||||||
@@ -5853,6 +5984,7 @@ optional = []
|
|||||||
no_flags: false,
|
no_flags: false,
|
||||||
cross_prefix: None,
|
cross_prefix: None,
|
||||||
clean: false,
|
clean: false,
|
||||||
|
lib32_only: false,
|
||||||
install_test_deps: false,
|
install_test_deps: false,
|
||||||
install_context: Some(INSTALL_CONTEXT_UPDATE),
|
install_context: Some(INSTALL_CONTEXT_UPDATE),
|
||||||
dep_chain: None,
|
dep_chain: None,
|
||||||
@@ -5995,4 +6127,23 @@ optional = []
|
|||||||
vec!["-O2=>-O3", "-fno-rtti=>-fno-exceptions"]
|
vec!["-O2=>-O3", "-fno-rtti=>-fno-exceptions"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn make_lib32_package_spec_uses_lib32_dependency_override() {
|
||||||
|
let mut base = test_package_spec(package::BuildType::Custom, None, &[]);
|
||||||
|
base.dependencies.runtime = vec!["zlib".into()];
|
||||||
|
base.dependencies.lib32 = Some(package::DependencyGroup {
|
||||||
|
build: vec!["gcc-multilib".into()],
|
||||||
|
runtime: vec!["lib32-zlib".into()],
|
||||||
|
test: Vec::new(),
|
||||||
|
optional: vec!["lib32-gtk-doc".into()],
|
||||||
|
});
|
||||||
|
|
||||||
|
let lib32 = make_lib32_package_spec(&base);
|
||||||
|
|
||||||
|
assert_eq!(lib32.package.name, "lib32-pkg");
|
||||||
|
assert_eq!(lib32.dependencies.build, vec!["gcc-multilib"]);
|
||||||
|
assert_eq!(lib32.dependencies.runtime, vec!["lib32-zlib"]);
|
||||||
|
assert_eq!(lib32.dependencies.optional, vec!["lib32-gtk-doc"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -102,7 +102,7 @@ impl CrossConfig {
|
|||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
r#"# CMake toolchain file for cross-compilation
|
r#"# CMake toolchain file for cross-compilation
|
||||||
# Generated by nyapm for target: {}
|
# Generated by depot for target: {}
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_NAME Linux)
|
set(CMAKE_SYSTEM_NAME Linux)
|
||||||
set(CMAKE_SYSTEM_PROCESSOR {})
|
set(CMAKE_SYSTEM_PROCESSOR {})
|
||||||
@@ -140,7 +140,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
|||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
r#"# Meson cross file for cross-compilation
|
r#"# Meson cross file for cross-compilation
|
||||||
# Generated by nyapm for target: {}
|
# Generated by depot for target: {}
|
||||||
|
|
||||||
[binaries]
|
[binaries]
|
||||||
c = '{}'
|
c = '{}'
|
||||||
|
|||||||
+242
-63
@@ -12,6 +12,7 @@ use crate::db;
|
|||||||
use crate::package::{BuildType, PackageSpec};
|
use crate::package::{BuildType, PackageSpec};
|
||||||
use crate::ui;
|
use crate::ui;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Version comparison operator
|
/// Version comparison operator
|
||||||
@@ -32,6 +33,13 @@ struct ParsedDep<'a> {
|
|||||||
op: VersionOp,
|
op: VersionOp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub(crate) enum RequestedOutputs {
|
||||||
|
PrimaryOnly,
|
||||||
|
PrimaryAndLib32,
|
||||||
|
Lib32Only,
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a dependency string into name, version, and operator
|
/// Parse a dependency string into name, version, and operator
|
||||||
fn parse_dep(dep: &str) -> ParsedDep<'_> {
|
fn parse_dep(dep: &str) -> ParsedDep<'_> {
|
||||||
// Try operators in order of specificity (>= before >, etc.)
|
// Try operators in order of specificity (>= before >, etc.)
|
||||||
@@ -157,18 +165,85 @@ pub fn is_dep_satisfied_in_db(dep: &str, db_path: &Path) -> Result<bool> {
|
|||||||
is_dep_satisfied(dep, &installed, &provides, db_path)
|
is_dep_satisfied(dep, &installed, &provides, db_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if all build dependencies are satisfied
|
fn push_unique(v: &mut Vec<String>, item: String) {
|
||||||
pub fn check_build_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<String>> {
|
if !v.contains(&item) {
|
||||||
|
v.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requested_dependency_sets(
|
||||||
|
spec: &PackageSpec,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Vec<crate::package::Dependencies> {
|
||||||
|
match outputs {
|
||||||
|
RequestedOutputs::PrimaryOnly => vec![spec.dependencies.primary_dependencies()],
|
||||||
|
RequestedOutputs::PrimaryAndLib32 => {
|
||||||
|
vec![
|
||||||
|
spec.dependencies.primary_dependencies(),
|
||||||
|
spec.lib32_dependencies(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
RequestedOutputs::Lib32Only => vec![spec.lib32_dependencies()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requested_local_provides(spec: &PackageSpec, outputs: RequestedOutputs) -> HashSet<String> {
|
||||||
|
match outputs {
|
||||||
|
RequestedOutputs::PrimaryOnly => spec.local_dependency_provides_for_selection(true, false),
|
||||||
|
RequestedOutputs::PrimaryAndLib32 => {
|
||||||
|
spec.local_dependency_provides_for_selection(true, true)
|
||||||
|
}
|
||||||
|
RequestedOutputs::Lib32Only => spec.local_dependency_provides_for_selection(false, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_build_deps(spec: &PackageSpec, outputs: RequestedOutputs) -> Vec<String> {
|
||||||
|
let mut deps = Vec::new();
|
||||||
|
for dep_set in requested_dependency_sets(spec, outputs) {
|
||||||
|
for dep in dep_set.build {
|
||||||
|
push_unique(&mut deps, dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_runtime_deps(spec: &PackageSpec, outputs: RequestedOutputs) -> Vec<String> {
|
||||||
|
let mut deps = Vec::new();
|
||||||
|
for dep_set in requested_dependency_sets(spec, outputs) {
|
||||||
|
for dep in dep_set.runtime {
|
||||||
|
push_unique(&mut deps, dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn declared_test_deps(spec: &PackageSpec, outputs: RequestedOutputs) -> Vec<String> {
|
||||||
|
let mut deps = Vec::new();
|
||||||
|
for dep_set in requested_dependency_sets(spec, outputs) {
|
||||||
|
for dep in dep_set.test {
|
||||||
|
push_unique(&mut deps, dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deps
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if all build dependencies are satisfied for the selected outputs.
|
||||||
|
pub(crate) fn check_build_deps_for_outputs(
|
||||||
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<Vec<String>> {
|
||||||
let mut missing = Vec::new();
|
let mut missing = Vec::new();
|
||||||
|
let build_deps = collect_build_deps(spec, outputs);
|
||||||
|
|
||||||
if !db_path.exists() {
|
if !db_path.exists() {
|
||||||
return Ok(spec.dependencies.build.clone());
|
return Ok(build_deps);
|
||||||
}
|
}
|
||||||
|
|
||||||
let installed = db::get_installed_packages(db_path)?;
|
let installed = db::get_installed_packages(db_path)?;
|
||||||
let provides = db::get_all_provides(db_path)?;
|
let provides = db::get_all_provides(db_path)?;
|
||||||
|
|
||||||
for dep in &spec.dependencies.build {
|
for dep in &build_deps {
|
||||||
if !is_dep_satisfied(dep, &installed, &provides, db_path)? {
|
if !is_dep_satisfied(dep, &installed, &provides, db_path)? {
|
||||||
missing.push(dep.clone());
|
missing.push(dep.clone());
|
||||||
}
|
}
|
||||||
@@ -177,13 +252,18 @@ pub fn check_build_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<String
|
|||||||
Ok(missing)
|
Ok(missing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if all runtime dependencies are satisfied
|
/// Check if all runtime dependencies are satisfied for the selected outputs.
|
||||||
pub fn check_runtime_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<String>> {
|
pub(crate) fn check_runtime_deps_for_outputs(
|
||||||
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<Vec<String>> {
|
||||||
let mut missing = Vec::new();
|
let mut missing = Vec::new();
|
||||||
let local_provides = spec.local_dependency_provides();
|
let runtime_deps = collect_runtime_deps(spec, outputs);
|
||||||
|
let local_provides = requested_local_provides(spec, outputs);
|
||||||
|
|
||||||
if !db_path.exists() {
|
if !db_path.exists() {
|
||||||
for dep in &spec.dependencies.runtime {
|
for dep in &runtime_deps {
|
||||||
if !local_provides.contains(dep_name(dep)) {
|
if !local_provides.contains(dep_name(dep)) {
|
||||||
missing.push(dep.clone());
|
missing.push(dep.clone());
|
||||||
}
|
}
|
||||||
@@ -194,7 +274,7 @@ pub fn check_runtime_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<Stri
|
|||||||
let installed = db::get_installed_packages(db_path)?;
|
let installed = db::get_installed_packages(db_path)?;
|
||||||
let provides = db::get_all_provides(db_path)?;
|
let provides = db::get_all_provides(db_path)?;
|
||||||
|
|
||||||
for dep in &spec.dependencies.runtime {
|
for dep in &runtime_deps {
|
||||||
if local_provides.contains(dep_name(dep)) {
|
if local_provides.contains(dep_name(dep)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -206,18 +286,23 @@ pub fn check_runtime_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<Stri
|
|||||||
Ok(missing)
|
Ok(missing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if all test dependencies are satisfied
|
/// Check if all test dependencies are satisfied for the selected outputs.
|
||||||
pub fn check_test_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<String>> {
|
pub(crate) fn check_test_deps_for_outputs(
|
||||||
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<Vec<String>> {
|
||||||
let mut missing = Vec::new();
|
let mut missing = Vec::new();
|
||||||
|
let test_deps = declared_test_deps(spec, outputs);
|
||||||
|
|
||||||
if !db_path.exists() {
|
if !db_path.exists() {
|
||||||
return Ok(spec.dependencies.test.clone());
|
return Ok(test_deps);
|
||||||
}
|
}
|
||||||
|
|
||||||
let installed = db::get_installed_packages(db_path)?;
|
let installed = db::get_installed_packages(db_path)?;
|
||||||
let provides = db::get_all_provides(db_path)?;
|
let provides = db::get_all_provides(db_path)?;
|
||||||
|
|
||||||
for dep in &spec.dependencies.test {
|
for dep in &test_deps {
|
||||||
if !is_dep_satisfied(dep, &installed, &provides, db_path)? {
|
if !is_dep_satisfied(dep, &installed, &provides, db_path)? {
|
||||||
missing.push(dep.clone());
|
missing.push(dep.clone());
|
||||||
}
|
}
|
||||||
@@ -228,59 +313,109 @@ pub fn check_test_deps(spec: &PackageSpec, db_path: &Path) -> Result<Vec<String>
|
|||||||
|
|
||||||
/// Print dependency status
|
/// Print dependency status
|
||||||
pub fn print_dep_status(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
pub fn print_dep_status(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
||||||
let missing_build = check_build_deps(spec, db_path)?;
|
print_dep_status_for_outputs(spec, db_path, RequestedOutputs::PrimaryOnly)
|
||||||
let missing_runtime = check_runtime_deps(spec, db_path)?;
|
}
|
||||||
let missing_test = check_test_deps(spec, db_path)?;
|
|
||||||
|
|
||||||
if !spec.dependencies.build.is_empty() {
|
fn print_named_dep_status(label: &str, deps: &[String], missing: &[String], warn_on_missing: bool) {
|
||||||
ui::info(format!(
|
if deps.is_empty() {
|
||||||
"Build dependencies: {}",
|
return;
|
||||||
spec.dependencies.build.join(", ")
|
|
||||||
));
|
|
||||||
if !missing_build.is_empty() {
|
|
||||||
ui::warn(format!("Build deps missing: {}", missing_build.join(", ")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !spec.dependencies.runtime.is_empty() {
|
ui::info(format!("{label}: {}", deps.join(", ")));
|
||||||
ui::info(format!(
|
if warn_on_missing && !missing.is_empty() {
|
||||||
"Runtime dependencies: {}",
|
ui::warn(format!("{label} missing: {}", missing.join(", ")));
|
||||||
spec.dependencies.runtime.join(", ")
|
}
|
||||||
));
|
}
|
||||||
if !missing_runtime.is_empty() {
|
|
||||||
ui::warn(format!(
|
/// Print dependency status for the selected outputs.
|
||||||
"Runtime deps missing: {}",
|
pub(crate) fn print_dep_status_for_outputs(
|
||||||
missing_runtime.join(", ")
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<()> {
|
||||||
|
let primary = spec.dependencies.primary_dependencies();
|
||||||
|
let lib32 = spec.lib32_dependencies();
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
outputs,
|
||||||
|
RequestedOutputs::PrimaryOnly | RequestedOutputs::PrimaryAndLib32
|
||||||
|
) {
|
||||||
|
let missing_build =
|
||||||
|
check_build_deps_for_outputs(spec, db_path, RequestedOutputs::PrimaryOnly)?;
|
||||||
|
let missing_runtime =
|
||||||
|
check_runtime_deps_for_outputs(spec, db_path, RequestedOutputs::PrimaryOnly)?;
|
||||||
|
let missing_test =
|
||||||
|
check_test_deps_for_outputs(spec, db_path, RequestedOutputs::PrimaryOnly)?;
|
||||||
|
|
||||||
|
print_named_dep_status("Build dependencies", &primary.build, &missing_build, true);
|
||||||
|
print_named_dep_status(
|
||||||
|
"Runtime dependencies",
|
||||||
|
&primary.runtime,
|
||||||
|
&missing_runtime,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
print_named_dep_status(
|
||||||
|
"Test dependencies",
|
||||||
|
&primary.test,
|
||||||
|
&missing_test,
|
||||||
|
!spec.build.flags.skip_tests && build_type_runs_automatic_tests(spec),
|
||||||
|
);
|
||||||
|
if !primary.optional.is_empty() {
|
||||||
|
ui::info(format!(
|
||||||
|
"Optional dependencies: {}",
|
||||||
|
primary.optional.join(", ")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !spec.dependencies.test.is_empty() {
|
if matches!(
|
||||||
ui::info(format!(
|
outputs,
|
||||||
"Test dependencies: {}",
|
RequestedOutputs::PrimaryAndLib32 | RequestedOutputs::Lib32Only
|
||||||
spec.dependencies.test.join(", ")
|
) {
|
||||||
));
|
let missing_build =
|
||||||
if !spec.build.flags.skip_tests
|
check_build_deps_for_outputs(spec, db_path, RequestedOutputs::Lib32Only)?;
|
||||||
&& build_type_runs_automatic_tests(spec)
|
let missing_runtime =
|
||||||
&& !missing_test.is_empty()
|
check_runtime_deps_for_outputs(spec, db_path, RequestedOutputs::Lib32Only)?;
|
||||||
{
|
let missing_test = check_test_deps_for_outputs(spec, db_path, RequestedOutputs::Lib32Only)?;
|
||||||
ui::warn(format!("Test deps missing: {}", missing_test.join(", ")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !spec.dependencies.optional.is_empty() {
|
if outputs == RequestedOutputs::Lib32Only || lib32 != primary {
|
||||||
ui::info(format!(
|
print_named_dep_status(
|
||||||
"Optional dependencies: {}",
|
"Lib32 build dependencies",
|
||||||
spec.dependencies.optional.join(", ")
|
&lib32.build,
|
||||||
));
|
&missing_build,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
print_named_dep_status(
|
||||||
|
"Lib32 runtime dependencies",
|
||||||
|
&lib32.runtime,
|
||||||
|
&missing_runtime,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
print_named_dep_status(
|
||||||
|
"Lib32 test dependencies",
|
||||||
|
&lib32.test,
|
||||||
|
&missing_test,
|
||||||
|
!spec.build.flags.skip_tests && build_type_runs_automatic_tests(spec),
|
||||||
|
);
|
||||||
|
if !lib32.optional.is_empty() {
|
||||||
|
ui::info(format!(
|
||||||
|
"Lib32 optional dependencies: {}",
|
||||||
|
lib32.optional.join(", ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify all build dependencies are installed, error if not
|
/// Verify all build dependencies are installed for the selected outputs.
|
||||||
pub fn require_build_deps(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
pub(crate) fn require_build_deps_for_outputs(
|
||||||
let missing = check_build_deps(spec, db_path)?;
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<()> {
|
||||||
|
let missing = check_build_deps_for_outputs(spec, db_path, outputs)?;
|
||||||
|
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
@@ -292,9 +427,13 @@ pub fn require_build_deps(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify all runtime dependencies are installed, error if not.
|
/// Verify all runtime dependencies are installed for the selected outputs.
|
||||||
pub fn require_runtime_deps(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
pub(crate) fn require_runtime_deps_for_outputs(
|
||||||
let missing = check_runtime_deps(spec, db_path)?;
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<()> {
|
||||||
|
let missing = check_runtime_deps_for_outputs(spec, db_path, outputs)?;
|
||||||
|
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
@@ -306,9 +445,13 @@ pub fn require_runtime_deps(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify all test dependencies are installed, error if not.
|
/// Verify all test dependencies are installed for the selected outputs.
|
||||||
pub fn require_test_deps(spec: &PackageSpec, db_path: &Path) -> Result<()> {
|
pub(crate) fn require_test_deps_for_outputs(
|
||||||
let missing = check_test_deps(spec, db_path)?;
|
spec: &PackageSpec,
|
||||||
|
db_path: &Path,
|
||||||
|
outputs: RequestedOutputs,
|
||||||
|
) -> Result<()> {
|
||||||
|
let missing = check_test_deps_for_outputs(spec, db_path, outputs)?;
|
||||||
|
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
@@ -441,13 +584,19 @@ mod tests {
|
|||||||
runtime: Vec::new(),
|
runtime: Vec::new(),
|
||||||
test: vec!["bats".into(), "python".into()],
|
test: vec!["bats".into(), "python".into()],
|
||||||
optional: Vec::new(),
|
optional: Vec::new(),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
spec_dir: std::path::PathBuf::from("."),
|
spec_dir: std::path::PathBuf::from("."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let missing = check_test_deps(&spec, Path::new("/definitely/not/a/real/db")).unwrap();
|
let missing = check_test_deps_for_outputs(
|
||||||
|
&spec,
|
||||||
|
Path::new("/definitely/not/a/real/db"),
|
||||||
|
RequestedOutputs::PrimaryOnly,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(missing, vec!["bats".to_string(), "python".to_string()]);
|
assert_eq!(missing, vec!["bats".to_string(), "python".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,14 +631,19 @@ mod tests {
|
|||||||
runtime: vec!["python".into()],
|
runtime: vec!["python".into()],
|
||||||
test: Vec::new(),
|
test: Vec::new(),
|
||||||
optional: Vec::new(),
|
optional: Vec::new(),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
spec_dir: std::path::PathBuf::from("."),
|
spec_dir: std::path::PathBuf::from("."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let err = require_runtime_deps(&spec, Path::new("/definitely/not/a/real/db"))
|
let err = require_runtime_deps_for_outputs(
|
||||||
.expect_err("runtime deps should be required");
|
&spec,
|
||||||
|
Path::new("/definitely/not/a/real/db"),
|
||||||
|
RequestedOutputs::PrimaryOnly,
|
||||||
|
)
|
||||||
|
.expect_err("runtime deps should be required");
|
||||||
assert!(err.to_string().contains("Missing runtime dependencies"));
|
assert!(err.to_string().contains("Missing runtime dependencies"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,6 +685,7 @@ mod tests {
|
|||||||
runtime: vec!["foo-libs".into(), "libfoo".into(), "python".into()],
|
runtime: vec!["foo-libs".into(), "libfoo".into(), "python".into()],
|
||||||
test: Vec::new(),
|
test: Vec::new(),
|
||||||
optional: Vec::new(),
|
optional: Vec::new(),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: std::collections::BTreeMap::from([(
|
package_alternatives: std::collections::BTreeMap::from([(
|
||||||
"foo-libs".to_string(),
|
"foo-libs".to_string(),
|
||||||
@@ -544,10 +699,34 @@ mod tests {
|
|||||||
spec_dir: std::path::PathBuf::from("."),
|
spec_dir: std::path::PathBuf::from("."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let missing = check_runtime_deps(&spec, Path::new("/definitely/not/a/real/db")).unwrap();
|
let missing = check_runtime_deps_for_outputs(
|
||||||
|
&spec,
|
||||||
|
Path::new("/definitely/not/a/real/db"),
|
||||||
|
RequestedOutputs::PrimaryOnly,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(missing, vec!["python".to_string()]);
|
assert_eq!(missing, vec!["python".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_runtime_deps_for_lib32_only_does_not_treat_primary_output_as_local() {
|
||||||
|
let mut spec = test_spec_with_build(BuildType::Custom, None, &[]);
|
||||||
|
spec.dependencies.lib32 = Some(crate::package::DependencyGroup {
|
||||||
|
build: Vec::new(),
|
||||||
|
runtime: vec!["foo".into()],
|
||||||
|
test: Vec::new(),
|
||||||
|
optional: Vec::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let missing = check_runtime_deps_for_outputs(
|
||||||
|
&spec,
|
||||||
|
Path::new("/definitely/not/a/real/db"),
|
||||||
|
RequestedOutputs::Lib32Only,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(missing, vec!["foo".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_build_type_runs_automatic_tests_matches_builder_behavior() {
|
fn test_build_type_runs_automatic_tests_matches_builder_behavior() {
|
||||||
assert!(build_type_runs_automatic_tests(&test_spec_with_build(
|
assert!(build_type_runs_automatic_tests(&test_spec_with_build(
|
||||||
|
|||||||
+119
-37
@@ -115,6 +115,11 @@ impl Hook {
|
|||||||
let compact = self.canonical_name().replace('_', "");
|
let compact = self.canonical_name().replace('_', "");
|
||||||
[compact.clone(), format!("{}.sh", compact)]
|
[compact.clone(), format!("{}.sh", compact)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn legacy_root_lib32_candidate_names(self) -> [String; 2] {
|
||||||
|
let compact = self.canonical_name().replace('_', "");
|
||||||
|
[format!("lib32-{compact}"), format!("lib32-{compact}.sh")]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -154,7 +159,7 @@ pub fn stage_scripts_from_spec_dir(spec: &PackageSpec, destdir: &Path) -> Result
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let legacy_hooks = collect_legacy_root_hooks(&spec.spec_dir)?;
|
let legacy_hooks = collect_legacy_root_hooks(&spec.spec_dir, &spec.package.name)?;
|
||||||
if !has_scripts_dir && legacy_hooks.is_empty() {
|
if !has_scripts_dir && legacy_hooks.is_empty() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -749,48 +754,82 @@ fn is_lib32_package(pkg_name: &str) -> bool {
|
|||||||
pkg_name.starts_with("lib32-")
|
pkg_name.starts_with("lib32-")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_legacy_root_hooks(spec_dir: &Path) -> Result<Vec<(Hook, PathBuf)>> {
|
fn collect_legacy_root_hook_candidates<I>(
|
||||||
let mut found = Vec::new();
|
spec_dir: &Path,
|
||||||
|
hook_label: &str,
|
||||||
for hook in ALL_HOOKS {
|
candidates: I,
|
||||||
let mut hook_matches = Vec::new();
|
) -> Result<Option<PathBuf>>
|
||||||
for candidate in hook.legacy_root_candidate_names() {
|
where
|
||||||
let path = spec_dir.join(candidate);
|
I: IntoIterator<Item = String>,
|
||||||
let metadata = match path.symlink_metadata() {
|
{
|
||||||
Ok(meta) => meta,
|
let mut matches = Vec::new();
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
|
for candidate in candidates {
|
||||||
Err(e) => {
|
let path = spec_dir.join(&candidate);
|
||||||
return Err(e).with_context(|| {
|
let metadata = match path.symlink_metadata() {
|
||||||
format!("Failed to inspect legacy hook script: {}", path.display())
|
Ok(meta) => meta,
|
||||||
});
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
|
||||||
}
|
Err(e) => {
|
||||||
};
|
return Err(e).with_context(|| {
|
||||||
|
format!("Failed to inspect legacy hook script: {}", path.display())
|
||||||
let file_type = metadata.file_type();
|
});
|
||||||
if !file_type.is_file() && !file_type.is_symlink() {
|
|
||||||
bail!(
|
|
||||||
"Legacy lifecycle hook candidate exists but is not a file: {}",
|
|
||||||
path.display()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
hook_matches.push(path);
|
let file_type = metadata.file_type();
|
||||||
}
|
if !file_type.is_file() && !file_type.is_symlink() {
|
||||||
|
|
||||||
if hook_matches.len() > 1 {
|
|
||||||
let names = hook_matches
|
|
||||||
.iter()
|
|
||||||
.map(|p| p.display().to_string())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
bail!(
|
bail!(
|
||||||
"Ambiguous legacy lifecycle hook '{}': multiple script candidates found: {}",
|
"Legacy lifecycle hook candidate exists but is not a file: {}",
|
||||||
hook.canonical_name(),
|
path.display()
|
||||||
names
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = hook_matches.into_iter().next() {
|
matches.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches.len() > 1 {
|
||||||
|
let names = matches
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.display().to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
bail!(
|
||||||
|
"Ambiguous legacy lifecycle hook '{}': multiple script candidates found: {}",
|
||||||
|
hook_label,
|
||||||
|
names
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(matches.into_iter().next())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_legacy_root_hooks(spec_dir: &Path, pkg_name: &str) -> Result<Vec<(Hook, PathBuf)>> {
|
||||||
|
let mut found = Vec::new();
|
||||||
|
|
||||||
|
for hook in ALL_HOOKS {
|
||||||
|
let matched_path = if is_lib32_package(pkg_name) {
|
||||||
|
let lib32_label = format!("{} (lib32)", hook.canonical_name());
|
||||||
|
if let Some(path) = collect_legacy_root_hook_candidates(
|
||||||
|
spec_dir,
|
||||||
|
&lib32_label,
|
||||||
|
hook.legacy_root_lib32_candidate_names(),
|
||||||
|
)? {
|
||||||
|
Some(path)
|
||||||
|
} else {
|
||||||
|
collect_legacy_root_hook_candidates(
|
||||||
|
spec_dir,
|
||||||
|
hook.canonical_name(),
|
||||||
|
hook.legacy_root_candidate_names(),
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collect_legacy_root_hook_candidates(
|
||||||
|
spec_dir,
|
||||||
|
hook.canonical_name(),
|
||||||
|
hook.legacy_root_candidate_names(),
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(path) = matched_path {
|
||||||
found.push((hook, path));
|
found.push((hook, path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1282,4 +1321,47 @@ mod tests {
|
|||||||
assert_ne!(mode & 0o111, 0);
|
assert_ne!(mode & 0o111, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stage_scripts_from_spec_dir_stages_lib32_prefixed_legacy_root_hook() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let spec_dir = tmp.path().join("spec");
|
||||||
|
let destdir = tmp.path().join("dest");
|
||||||
|
std::fs::create_dir_all(&spec_dir).unwrap();
|
||||||
|
std::fs::create_dir_all(&destdir).unwrap();
|
||||||
|
|
||||||
|
std::fs::write(spec_dir.join("lib32-postinstall.sh"), "echo lib32-post").unwrap();
|
||||||
|
|
||||||
|
let mut spec = mk_spec(&spec_dir);
|
||||||
|
spec.package.name = "lib32-foo".into();
|
||||||
|
let staged = stage_scripts_from_spec_dir(&spec, &destdir).unwrap();
|
||||||
|
assert!(staged);
|
||||||
|
assert!(destdir.join("scripts/post_install").exists());
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let mode = std::fs::metadata(destdir.join("scripts/post_install"))
|
||||||
|
.unwrap()
|
||||||
|
.permissions()
|
||||||
|
.mode();
|
||||||
|
assert_ne!(mode & 0o111, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stage_scripts_from_spec_dir_lib32_falls_back_to_generic_legacy_root_hook() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let spec_dir = tmp.path().join("spec");
|
||||||
|
let destdir = tmp.path().join("dest");
|
||||||
|
std::fs::create_dir_all(&spec_dir).unwrap();
|
||||||
|
std::fs::create_dir_all(&destdir).unwrap();
|
||||||
|
|
||||||
|
// No lib32-prefixed hook; the generic one should be staged for lib32 packages.
|
||||||
|
std::fs::write(spec_dir.join("postinstall.sh"), "echo fallback").unwrap();
|
||||||
|
|
||||||
|
let mut spec = mk_spec(&spec_dir);
|
||||||
|
spec.package.name = "lib32-foo".into();
|
||||||
|
let staged = stage_scripts_from_spec_dir(&spec, &destdir).unwrap();
|
||||||
|
assert!(staged);
|
||||||
|
assert!(destdir.join("scripts/post_install").exists());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -690,6 +690,7 @@ pub fn create_interactive() -> Result<PackageSpec> {
|
|||||||
runtime: runtime_deps,
|
runtime: runtime_deps,
|
||||||
test: test_deps,
|
test: test_deps,
|
||||||
optional: optional_deps,
|
optional: optional_deps,
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
@@ -1776,6 +1777,7 @@ mod tests {
|
|||||||
runtime: vec![],
|
runtime: vec![],
|
||||||
test: vec!["python".into(), "bats".into()],
|
test: vec!["python".into(), "bats".into()],
|
||||||
optional: vec!["gtk-doc".into()],
|
optional: vec!["gtk-doc".into()],
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
@@ -1884,6 +1886,7 @@ mod tests {
|
|||||||
runtime: vec!["foo".into(), "bar".into()],
|
runtime: vec!["foo".into(), "bar".into()],
|
||||||
test: Vec::new(),
|
test: Vec::new(),
|
||||||
optional: Vec::new(),
|
optional: Vec::new(),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: Default::default(),
|
package_alternatives: Default::default(),
|
||||||
package_dependencies: Default::default(),
|
package_dependencies: Default::default(),
|
||||||
|
|||||||
+145
-10
@@ -225,6 +225,14 @@ impl PackageSpec {
|
|||||||
///
|
///
|
||||||
/// If no per-output override exists, returns the top-level dependencies.
|
/// If no per-output override exists, returns the top-level dependencies.
|
||||||
pub fn dependencies_for_output(&self, pkg_name: &str) -> Dependencies {
|
pub fn dependencies_for_output(&self, pkg_name: &str) -> Dependencies {
|
||||||
|
if pkg_name == self.lib32_package_name() {
|
||||||
|
return self
|
||||||
|
.package_dependencies
|
||||||
|
.get(pkg_name)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| self.lib32_dependencies());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(parent_output) = self.docs_parent_output_name(pkg_name) {
|
if let Some(parent_output) = self.docs_parent_output_name(pkg_name) {
|
||||||
return self
|
return self
|
||||||
.package_dependencies
|
.package_dependencies
|
||||||
@@ -240,16 +248,40 @@ impl PackageSpec {
|
|||||||
self.package_dependencies
|
self.package_dependencies
|
||||||
.get(pkg_name)
|
.get(pkg_name)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| self.dependencies.clone())
|
.unwrap_or_else(|| self.dependencies.primary_dependencies())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return all package names/provided features produced by this spec.
|
/// Return the generated lib32 companion package name for this spec.
|
||||||
///
|
pub fn lib32_package_name(&self) -> String {
|
||||||
/// This includes all output package names and per-output `provides` entries.
|
format!("lib32-{}", self.package.name)
|
||||||
pub fn local_dependency_provides(&self) -> HashSet<String> {
|
}
|
||||||
|
|
||||||
|
/// Return the effective dependency set used by the generated lib32 companion package.
|
||||||
|
pub fn lib32_dependencies(&self) -> Dependencies {
|
||||||
|
self.dependencies
|
||||||
|
.lib32_dependencies()
|
||||||
|
.unwrap_or_else(|| self.dependencies.primary_dependencies())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return local package names/provided features for the selected output set.
|
||||||
|
pub fn local_dependency_provides_for_selection(
|
||||||
|
&self,
|
||||||
|
include_primary_outputs: bool,
|
||||||
|
include_lib32_output: bool,
|
||||||
|
) -> HashSet<String> {
|
||||||
let mut names = HashSet::new();
|
let mut names = HashSet::new();
|
||||||
for output in self.outputs() {
|
if include_primary_outputs {
|
||||||
let output_name = output.name.clone();
|
for output in self.outputs() {
|
||||||
|
let output_name = output.name.clone();
|
||||||
|
names.insert(output_name.clone());
|
||||||
|
let alternatives = self.alternatives_for_output(&output_name);
|
||||||
|
for provided in alternatives.provides {
|
||||||
|
names.insert(provided);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if include_lib32_output {
|
||||||
|
let output_name = self.lib32_package_name();
|
||||||
names.insert(output_name.clone());
|
names.insert(output_name.clone());
|
||||||
let alternatives = self.alternatives_for_output(&output_name);
|
let alternatives = self.alternatives_for_output(&output_name);
|
||||||
for provided in alternatives.provides {
|
for provided in alternatives.provides {
|
||||||
@@ -1796,6 +1828,59 @@ runtime = ["llvm-libgcc", "zstd"]
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_lib32_dependencies_override() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let path = tmp.path().join("pkg.toml");
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
&path,
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "llvm"
|
||||||
|
version = "1.0"
|
||||||
|
description = "d"
|
||||||
|
homepage = "h"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[source]
|
||||||
|
url = "https://example.com/llvm.tar.gz"
|
||||||
|
sha256 = "skip"
|
||||||
|
extract_dir = "llvm"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
type = "custom"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
runtime = ["base"]
|
||||||
|
|
||||||
|
[dependencies.lib32]
|
||||||
|
build = ["gcc-multilib"]
|
||||||
|
runtime = ["lib32-zlib"]
|
||||||
|
test = ["bats"]
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let spec = PackageSpec::from_file(&path).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
spec.dependencies_for_output("llvm").runtime,
|
||||||
|
vec!["base".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
spec.dependencies_for_output("lib32-llvm").build,
|
||||||
|
vec!["gcc-multilib".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
spec.dependencies_for_output("lib32-llvm").runtime,
|
||||||
|
vec!["lib32-zlib".to_string()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
spec.dependencies_for_output("lib32-llvm").test,
|
||||||
|
vec!["bats".to_string()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_package_alternatives_overrides() {
|
fn parse_package_alternatives_overrides() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
@@ -4355,9 +4440,9 @@ fn default_cxx() -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Package dependencies
|
/// Nested dependency override group for a specific output variant.
|
||||||
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Dependencies {
|
pub struct DependencyGroup {
|
||||||
/// Dependencies required for building packages.
|
/// Dependencies required for building packages.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub build: Vec<String>,
|
pub build: Vec<String>,
|
||||||
@@ -4371,3 +4456,53 @@ pub struct Dependencies {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub optional: Vec<String>,
|
pub optional: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DependencyGroup {
|
||||||
|
fn to_dependencies(&self) -> Dependencies {
|
||||||
|
Dependencies {
|
||||||
|
build: self.build.clone(),
|
||||||
|
runtime: self.runtime.clone(),
|
||||||
|
test: self.test.clone(),
|
||||||
|
optional: self.optional.clone(),
|
||||||
|
lib32: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Package dependencies
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct Dependencies {
|
||||||
|
/// Dependencies required for building packages.
|
||||||
|
#[serde(default)]
|
||||||
|
pub build: Vec<String>,
|
||||||
|
/// Dependencies required at runtime.
|
||||||
|
#[serde(default)]
|
||||||
|
pub runtime: Vec<String>,
|
||||||
|
/// Dependencies required to run package test suites.
|
||||||
|
#[serde(default)]
|
||||||
|
pub test: Vec<String>,
|
||||||
|
/// Optional runtime integrations that enhance functionality when installed.
|
||||||
|
#[serde(default)]
|
||||||
|
pub optional: Vec<String>,
|
||||||
|
/// Optional dependency overrides used only for the generated `lib32-*` companion package.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub lib32: Option<DependencyGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dependencies {
|
||||||
|
/// Return the top-level dependency set without any nested output-specific overrides.
|
||||||
|
pub fn primary_dependencies(&self) -> Dependencies {
|
||||||
|
Dependencies {
|
||||||
|
build: self.build.clone(),
|
||||||
|
runtime: self.runtime.clone(),
|
||||||
|
test: self.test.clone(),
|
||||||
|
optional: self.optional.clone(),
|
||||||
|
lib32: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the optional lib32-specific dependency override set.
|
||||||
|
pub fn lib32_dependencies(&self) -> Option<Dependencies> {
|
||||||
|
self.lib32.as_ref().map(DependencyGroup::to_dependencies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+67
-16
@@ -105,6 +105,7 @@ pub(crate) struct PlannerOptions {
|
|||||||
pub prefer_binary: bool,
|
pub prefer_binary: bool,
|
||||||
pub local_sibling_root: Option<PathBuf>,
|
pub local_sibling_root: Option<PathBuf>,
|
||||||
pub include_test_deps: bool,
|
pub include_test_deps: bool,
|
||||||
|
pub lib32_only_requested_specs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -267,11 +268,13 @@ impl<'a> Resolver<'a> {
|
|||||||
requested_by: String,
|
requested_by: String,
|
||||||
) -> Result<NodeIndex> {
|
) -> Result<NodeIndex> {
|
||||||
let include_test_deps = self.opts.include_test_deps;
|
let include_test_deps = self.opts.include_test_deps;
|
||||||
|
let lib32_only =
|
||||||
|
self.opts.lib32_only_requested_specs && requested_by.starts_with("requested ");
|
||||||
let (package_name, deps_needed) = {
|
let (package_name, deps_needed) = {
|
||||||
let spec = self.load_spec(path)?;
|
let spec = self.load_spec(path)?;
|
||||||
(
|
(
|
||||||
spec.package.name.clone(),
|
spec.package.name.clone(),
|
||||||
source_deps_for_install(spec, include_test_deps),
|
source_deps_for_install(spec, include_test_deps, lib32_only),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if let Some(&idx) = self.by_package.get(&package_name) {
|
if let Some(&idx) = self.by_package.get(&package_name) {
|
||||||
@@ -613,31 +616,57 @@ impl<'a> Resolver<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_deps_for_install(spec: &PackageSpec, include_test_deps: bool) -> Vec<String> {
|
fn source_deps_for_install(
|
||||||
|
spec: &PackageSpec,
|
||||||
|
include_test_deps: bool,
|
||||||
|
lib32_only: bool,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut deps_all = Vec::new();
|
let mut deps_all = Vec::new();
|
||||||
let local_provides = spec.local_dependency_provides();
|
let include_lib32 = lib32_only || spec.build.flags.build_32;
|
||||||
if !spec.is_metapackage() {
|
let local_provides = spec.local_dependency_provides_for_selection(!lib32_only, include_lib32);
|
||||||
|
if !lib32_only && !spec.is_metapackage() {
|
||||||
for dep in &spec.dependencies.build {
|
for dep in &spec.dependencies.build {
|
||||||
push_unique(&mut deps_all, dep.clone());
|
push_unique(&mut deps_all, dep.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for dep in &spec.dependencies.runtime {
|
if include_lib32 && !spec.is_metapackage() {
|
||||||
if !local_provides.contains(deps::dep_name(dep)) {
|
for dep in &spec.lib32_dependencies().build {
|
||||||
push_unique(&mut deps_all, dep.clone());
|
push_unique(&mut deps_all, dep.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !lib32_only {
|
||||||
|
for dep in &spec.dependencies.runtime {
|
||||||
|
if !local_provides.contains(deps::dep_name(dep)) {
|
||||||
|
push_unique(&mut deps_all, dep.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for out in spec.outputs() {
|
for out in spec.outputs() {
|
||||||
let deps = spec.dependencies_for_output(&out.name);
|
let deps = spec.dependencies_for_output(&out.name);
|
||||||
for dep in deps.runtime {
|
for dep in deps.runtime {
|
||||||
if !local_provides.contains(deps::dep_name(&dep)) {
|
if !local_provides.contains(deps::dep_name(&dep)) {
|
||||||
push_unique(&mut deps_all, dep);
|
push_unique(&mut deps_all, dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if include_lib32 {
|
||||||
|
for dep in &spec.lib32_dependencies().runtime {
|
||||||
|
if !local_provides.contains(deps::dep_name(dep)) {
|
||||||
|
push_unique(&mut deps_all, dep.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if include_test_deps && !spec.build.flags.skip_tests {
|
if include_test_deps && !spec.build.flags.skip_tests {
|
||||||
for dep in &spec.dependencies.test {
|
if !lib32_only {
|
||||||
push_unique(&mut deps_all, dep.clone());
|
for dep in &spec.dependencies.test {
|
||||||
|
push_unique(&mut deps_all, dep.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if include_lib32 {
|
||||||
|
for dep in &spec.lib32_dependencies().test {
|
||||||
|
push_unique(&mut deps_all, dep.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deps_all
|
deps_all
|
||||||
@@ -809,6 +838,7 @@ mod tests {
|
|||||||
runtime: vec!["foo-libs".into(), "zlib".into()],
|
runtime: vec!["foo-libs".into(), "zlib".into()],
|
||||||
test: vec!["bats".into()],
|
test: vec!["bats".into()],
|
||||||
optional: vec!["docs-viewer".into()],
|
optional: vec!["docs-viewer".into()],
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
package_alternatives: BTreeMap::from([(
|
package_alternatives: BTreeMap::from([(
|
||||||
"foo-libs".into(),
|
"foo-libs".into(),
|
||||||
@@ -825,6 +855,7 @@ mod tests {
|
|||||||
runtime: vec!["foo-libs".into(), "libfoo".into(), "openssl".into()],
|
runtime: vec!["foo-libs".into(), "libfoo".into(), "openssl".into()],
|
||||||
test: Vec::new(),
|
test: Vec::new(),
|
||||||
optional: Vec::new(),
|
optional: Vec::new(),
|
||||||
|
lib32: None,
|
||||||
},
|
},
|
||||||
)]),
|
)]),
|
||||||
spec_dir: PathBuf::from("."),
|
spec_dir: PathBuf::from("."),
|
||||||
@@ -888,7 +919,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn source_deps_for_install_excludes_local_runtime_outputs_and_provides() {
|
fn source_deps_for_install_excludes_local_runtime_outputs_and_provides() {
|
||||||
let spec = mk_spec();
|
let spec = mk_spec();
|
||||||
let deps = source_deps_for_install(&spec, false);
|
let deps = source_deps_for_install(&spec, false, false);
|
||||||
assert!(deps.contains(&"make".to_string()));
|
assert!(deps.contains(&"make".to_string()));
|
||||||
assert!(deps.contains(&"zlib".to_string()));
|
assert!(deps.contains(&"zlib".to_string()));
|
||||||
assert!(deps.contains(&"openssl".to_string()));
|
assert!(deps.contains(&"openssl".to_string()));
|
||||||
@@ -899,17 +930,36 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn source_deps_for_install_does_not_include_test_deps() {
|
fn source_deps_for_install_does_not_include_test_deps() {
|
||||||
let spec = mk_spec();
|
let spec = mk_spec();
|
||||||
let deps = source_deps_for_install(&spec, false);
|
let deps = source_deps_for_install(&spec, false, false);
|
||||||
assert!(!deps.contains(&"bats".to_string()));
|
assert!(!deps.contains(&"bats".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn source_deps_for_install_includes_test_deps_when_enabled() {
|
fn source_deps_for_install_includes_test_deps_when_enabled() {
|
||||||
let spec = mk_spec();
|
let spec = mk_spec();
|
||||||
let deps = source_deps_for_install(&spec, true);
|
let deps = source_deps_for_install(&spec, true, false);
|
||||||
assert!(deps.contains(&"bats".to_string()));
|
assert!(deps.contains(&"bats".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn source_deps_for_install_uses_lib32_only_dependencies_when_requested() {
|
||||||
|
let mut spec = mk_spec();
|
||||||
|
spec.dependencies.lib32 = Some(crate::package::DependencyGroup {
|
||||||
|
build: vec!["gcc-multilib".into()],
|
||||||
|
runtime: vec!["lib32-zlib".into()],
|
||||||
|
test: vec!["lib32-bats".into()],
|
||||||
|
optional: Vec::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let deps = source_deps_for_install(&spec, true, true);
|
||||||
|
assert!(deps.contains(&"gcc-multilib".to_string()));
|
||||||
|
assert!(deps.contains(&"lib32-zlib".to_string()));
|
||||||
|
assert!(deps.contains(&"lib32-bats".to_string()));
|
||||||
|
assert!(!deps.contains(&"make".to_string()));
|
||||||
|
assert!(!deps.contains(&"zlib".to_string()));
|
||||||
|
assert!(!deps.contains(&"bats".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn candidate_dedup_keeps_highest_priority_origin_for_same_package() {
|
fn candidate_dedup_keeps_highest_priority_origin_for_same_package() {
|
||||||
let candidates = vec![
|
let candidates = vec![
|
||||||
@@ -957,6 +1007,7 @@ mod tests {
|
|||||||
prefer_binary: true,
|
prefer_binary: true,
|
||||||
local_sibling_root: None,
|
local_sibling_root: None,
|
||||||
include_test_deps: false,
|
include_test_deps: false,
|
||||||
|
lib32_only_requested_specs: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user