feat: run transaction hooks at boundaries and add configurable LTO flags
- execute transaction pre hooks at transaction start and post hooks at transaction end - add batched hook execution with ordered "(n/total)" logging - refactor install flows to plan package actions first, then run hooks around commits - add `build.flags.ltoflags` and `build.flags.use_lto` (default: true) - export `LTOFLAGS` and append LTO flags to `CFLAGS`, `CXXFLAGS`, and `LDFLAGS` unless `use_lto = false` - make autotools CFLAGS expansion use the final computed CFLAGS value - update spec parsing/appends, interactive TOML output, docs, and contrib build config - bump project version to 0.8.0 and update `zip` to 8.2.0
This commit is contained in:
Generated
+3
-3
@@ -382,7 +382,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "depot"
|
name = "depot"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ar",
|
"ar",
|
||||||
@@ -2995,9 +2995,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "8.1.0"
|
version = "8.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e499faf5c6b97a0d086f4a8733de6d47aee2252b8127962439d8d4311a73f72"
|
checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "depot"
|
name = "depot"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
@@ -27,7 +27,7 @@ toml = "0.9.8"
|
|||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
xz2 = "0.1.7"
|
xz2 = "0.1.7"
|
||||||
zip = "8.1.0"
|
zip = "8.2.0"
|
||||||
zstd = { version = "0.13.3", features = ["zstdmt"] }
|
zstd = { version = "0.13.3", features = ["zstdmt"] }
|
||||||
inquire = "0.9.4"
|
inquire = "0.9.4"
|
||||||
md5 = "0.8.0"
|
md5 = "0.8.0"
|
||||||
|
|||||||
@@ -85,6 +85,10 @@ runtime = ["libc"]
|
|||||||
optional = ["bash-completion"]
|
optional = ["bash-completion"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
LTO controls are available via `build.flags`:
|
||||||
|
- `ltoflags`: exported as `LTOFLAGS`
|
||||||
|
- `use_lto`: defaults to `true`; when enabled, `ltoflags` are appended to `CFLAGS`, `CXXFLAGS`, and `LDFLAGS`
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Depot can be configured via `/etc/depot.d/` (or relative to the rootfs).
|
Depot can be configured via `/etc/depot.d/` (or relative to the rootfs).
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ cflags += ["-pipe"]
|
|||||||
# Default LDFLAGS
|
# Default LDFLAGS
|
||||||
ldflags = ["-Wl,-O1"]
|
ldflags = ["-Wl,-O1"]
|
||||||
|
|
||||||
|
# Default LTOFLAGS and automatic injection into CFLAGS/CXXFLAGS/LDFLAGS.
|
||||||
|
ltoflags = ["-flto=auto"]
|
||||||
|
use_lto = true
|
||||||
|
|
||||||
# CARCH short name (can be overridden per-package)
|
# CARCH short name (can be overridden per-package)
|
||||||
carch = "x86_64"
|
carch = "x86_64"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
project(
|
project(
|
||||||
'depot',
|
'depot',
|
||||||
version: '0.7.0',
|
version: '0.8.0',
|
||||||
meson_version: '>=0.60.0',
|
meson_version: '>=0.60.0',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,14 @@ pub fn build(
|
|||||||
flags.cc.clone()
|
flags.cc.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
if export_compiler_flags && !flags.cflags.is_empty() {
|
if export_compiler_flags
|
||||||
// Expand shell command substitutions like $($CC -print-resource-dir)
|
&& let Some(cflags_str) = env_vars
|
||||||
let cflags_str = flags.cflags.join(" ");
|
.iter()
|
||||||
|
.find(|(key, _)| key == "CFLAGS")
|
||||||
|
.map(|(_, value)| value.clone())
|
||||||
|
&& !cflags_str.trim().is_empty()
|
||||||
|
{
|
||||||
|
// Expand shell command substitutions like $($CC -print-resource-dir).
|
||||||
let expanded = expand_shell_commands(&cflags_str, &cc)?;
|
let expanded = expand_shell_commands(&cflags_str, &cc)?;
|
||||||
crate::builder::set_env_var(&mut env_vars, "CFLAGS", expanded);
|
crate::builder::set_env_var(&mut env_vars, "CFLAGS", expanded);
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-12
@@ -28,6 +28,23 @@ pub fn set_env_var(env_vars: &mut EnvVars, key: &str, value: impl Into<String>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compiler_flag_sets(
|
||||||
|
flags: &crate::package::BuildFlags,
|
||||||
|
) -> (Vec<String>, Vec<String>, Vec<String>, Vec<String>) {
|
||||||
|
let mut cflags = flags.cflags.clone();
|
||||||
|
let mut cxxflags = flags.cxxflags.clone();
|
||||||
|
let mut ldflags = flags.ldflags.clone();
|
||||||
|
let ltoflags = flags.ltoflags.clone();
|
||||||
|
|
||||||
|
if flags.use_lto && !ltoflags.is_empty() {
|
||||||
|
cflags.extend(ltoflags.iter().cloned());
|
||||||
|
cxxflags.extend(ltoflags.iter().cloned());
|
||||||
|
ldflags.extend(ltoflags.iter().cloned());
|
||||||
|
}
|
||||||
|
|
||||||
|
(cflags, cxxflags, ldflags, ltoflags)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn standard_build_env(
|
pub fn standard_build_env(
|
||||||
spec: &PackageSpec,
|
spec: &PackageSpec,
|
||||||
cross: Option<&CrossConfig>,
|
cross: Option<&CrossConfig>,
|
||||||
@@ -39,24 +56,25 @@ pub fn standard_build_env(
|
|||||||
let export_compiler_flags = export_compiler_flags && !flags.no_flags;
|
let export_compiler_flags = export_compiler_flags && !flags.no_flags;
|
||||||
|
|
||||||
if include_compiler_env && export_compiler_flags {
|
if include_compiler_env && export_compiler_flags {
|
||||||
if !flags.cflags.is_empty() {
|
let (cflags, cxxflags, ldflags, ltoflags) = compiler_flag_sets(flags);
|
||||||
set_env_var(&mut env_vars, "CFLAGS", flags.cflags.join(" "));
|
|
||||||
|
if !cflags.is_empty() {
|
||||||
|
set_env_var(&mut env_vars, "CFLAGS", cflags.join(" "));
|
||||||
}
|
}
|
||||||
if !flags.cxxflags.is_empty() {
|
if !cxxflags.is_empty() {
|
||||||
set_env_var(&mut env_vars, "CXXFLAGS", flags.cxxflags.join(" "));
|
set_env_var(&mut env_vars, "CXXFLAGS", cxxflags.join(" "));
|
||||||
|
}
|
||||||
|
if !ltoflags.is_empty() {
|
||||||
|
set_env_var(&mut env_vars, "LTOFLAGS", ltoflags.join(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ldflags = if !flags.ldflags.is_empty() || !flags.libc.is_empty() {
|
let ldflags = if !ldflags.is_empty() || !flags.libc.is_empty() {
|
||||||
if flags.libc.is_empty() {
|
if flags.libc.is_empty() {
|
||||||
flags.ldflags.join(" ")
|
ldflags.join(" ")
|
||||||
} else if flags.ldflags.is_empty() {
|
} else if ldflags.is_empty() {
|
||||||
format!("-Wl,--dynamic-linker={}", flags.libc)
|
format!("-Wl,--dynamic-linker={}", flags.libc)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!("{} -Wl,--dynamic-linker={}", ldflags.join(" "), flags.libc)
|
||||||
"{} -Wl,--dynamic-linker={}",
|
|
||||||
flags.ldflags.join(" "),
|
|
||||||
flags.libc
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
@@ -424,6 +442,65 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_standard_build_env_injects_ltoflags_into_compiler_and_linker_flags() {
|
||||||
|
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
||||||
|
spec.build.flags.cxxflags = vec!["-O2".into()];
|
||||||
|
spec.build.flags.ltoflags = vec!["-flto=auto".into(), "-fuse-linker-plugin".into()];
|
||||||
|
spec.build.flags.use_lto = true;
|
||||||
|
|
||||||
|
let env = standard_build_env(&spec, None, true, true);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| { k == "CFLAGS" && v == "-O2 -flto=auto -fuse-linker-plugin" }),
|
||||||
|
"expected LTOFLAGS to be appended to CFLAGS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "CXXFLAGS" && v == "-O2 -flto=auto -fuse-linker-plugin"),
|
||||||
|
"expected LTOFLAGS to be appended to CXXFLAGS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter().any(|(k, v)| {
|
||||||
|
k == "LDFLAGS" && v == "-Wl,--as-needed -flto=auto -fuse-linker-plugin"
|
||||||
|
}),
|
||||||
|
"expected LTOFLAGS to be appended to LDFLAGS"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=auto -fuse-linker-plugin"),
|
||||||
|
"expected LTOFLAGS variable to be exported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_standard_build_env_skips_lto_injection_when_disabled() {
|
||||||
|
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
||||||
|
spec.build.flags.cxxflags = vec!["-O2".into()];
|
||||||
|
spec.build.flags.ltoflags = vec!["-flto=auto".into()];
|
||||||
|
spec.build.flags.use_lto = false;
|
||||||
|
|
||||||
|
let env = standard_build_env(&spec, None, true, true);
|
||||||
|
assert!(
|
||||||
|
env.iter().any(|(k, v)| k == "CFLAGS" && v == "-O2"),
|
||||||
|
"expected CFLAGS to remain unchanged when use_lto is false"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter().any(|(k, v)| k == "CXXFLAGS" && v == "-O2"),
|
||||||
|
"expected CXXFLAGS to remain unchanged when use_lto is false"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "LDFLAGS" && v == "-Wl,--as-needed"),
|
||||||
|
"expected LDFLAGS to remain unchanged when use_lto is false"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
env.iter()
|
||||||
|
.any(|(k, v)| k == "LTOFLAGS" && v == "-flto=auto"),
|
||||||
|
"expected LTOFLAGS variable to be exported even when use_lto is false"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_standard_build_env_exports_makeflags() {
|
fn test_standard_build_env_exports_makeflags() {
|
||||||
let mut spec = mk_spec(Vec::new(), Vec::new());
|
let mut spec = mk_spec(Vec::new(), Vec::new());
|
||||||
|
|||||||
+178
-73
@@ -491,12 +491,25 @@ fn build_lib32_companion_package(
|
|||||||
Ok(Some((lib32_pkg_spec, lib32_destdir)))
|
Ok(Some((lib32_pkg_spec, lib32_destdir)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_staged_to_rootfs(
|
#[derive(Debug, Clone)]
|
||||||
|
struct PlannedStagedInstall {
|
||||||
|
is_update: bool,
|
||||||
|
remove_paths: Vec<String>,
|
||||||
|
hook_context: install::hooks::HookExecutionContextOwned,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PlannedPackageInstall {
|
||||||
|
spec: package::PackageSpec,
|
||||||
|
destdir: PathBuf,
|
||||||
|
staged: PlannedStagedInstall,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plan_staged_install(
|
||||||
pkg_spec: &package::PackageSpec,
|
pkg_spec: &package::PackageSpec,
|
||||||
destdir: &Path,
|
destdir: &Path,
|
||||||
rootfs: &Path,
|
|
||||||
config: &config::Config,
|
config: &config::Config,
|
||||||
) -> Result<()> {
|
) -> Result<PlannedStagedInstall> {
|
||||||
std::fs::create_dir_all(&config.db_dir).with_context(|| {
|
std::fs::create_dir_all(&config.db_dir).with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"Failed to create database directory: {}",
|
"Failed to create database directory: {}",
|
||||||
@@ -506,10 +519,6 @@ fn install_staged_to_rootfs(
|
|||||||
let db_path = config.db_dir.join("packages.db");
|
let db_path = config.db_dir.join("packages.db");
|
||||||
|
|
||||||
let is_update = db::get_package_version(&db_path, &pkg_spec.package.name)?.is_some();
|
let is_update = db::get_package_version(&db_path, &pkg_spec.package.name)?.is_some();
|
||||||
let staged_scripts_dir = install::scripts::staged_scripts_dir(destdir);
|
|
||||||
let installed_scripts_dir =
|
|
||||||
install::scripts::installed_scripts_dir(rootfs, &pkg_spec.package.name);
|
|
||||||
|
|
||||||
let new_files = staging::generate_manifest_with_dirs(destdir)?;
|
let new_files = staging::generate_manifest_with_dirs(destdir)?;
|
||||||
let remove_paths =
|
let remove_paths =
|
||||||
db::calculate_upgrade_paths(&db_path, &pkg_spec.package.name, &new_files.files)?;
|
db::calculate_upgrade_paths(&db_path, &pkg_spec.package.name, &new_files.files)?;
|
||||||
@@ -523,17 +532,64 @@ fn install_staged_to_rootfs(
|
|||||||
affected_paths.sort();
|
affected_paths.sort();
|
||||||
affected_paths.dedup();
|
affected_paths.dedup();
|
||||||
|
|
||||||
install::hooks::run_transaction_hooks(
|
Ok(PlannedStagedInstall {
|
||||||
rootfs,
|
is_update,
|
||||||
&install::hooks::HookExecutionContext {
|
remove_paths,
|
||||||
phase: install::hooks::HookPhase::Pre,
|
hook_context: install::hooks::HookExecutionContextOwned {
|
||||||
operation,
|
operation,
|
||||||
package: &pkg_spec.package.name,
|
package: pkg_spec.package.name.clone(),
|
||||||
affected_paths: &affected_paths,
|
affected_paths,
|
||||||
},
|
},
|
||||||
)?;
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if is_update {
|
fn plan_package_outputs_for_install(
|
||||||
|
pkg_spec: &package::PackageSpec,
|
||||||
|
destdir: &Path,
|
||||||
|
config: &config::Config,
|
||||||
|
) -> Result<Vec<PlannedPackageInstall>> {
|
||||||
|
let mut plans = Vec::new();
|
||||||
|
for out in pkg_spec.outputs() {
|
||||||
|
let mut spec_for_out = pkg_spec.clone();
|
||||||
|
let output_name = out.name.clone();
|
||||||
|
spec_for_out.package = out;
|
||||||
|
spec_for_out.alternatives = pkg_spec.alternatives_for_output(&output_name);
|
||||||
|
spec_for_out.dependencies = pkg_spec.dependencies_for_output(&output_name);
|
||||||
|
let out_destdir = output_destdir_for(destdir, &pkg_spec.package.name, &output_name);
|
||||||
|
let staged = plan_staged_install(&spec_for_out, &out_destdir, config)?;
|
||||||
|
plans.push(PlannedPackageInstall {
|
||||||
|
spec: spec_for_out,
|
||||||
|
destdir: out_destdir,
|
||||||
|
staged,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(plans)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_transaction_hooks_for_plans(
|
||||||
|
rootfs: &Path,
|
||||||
|
phase: install::hooks::HookPhase,
|
||||||
|
plans: &[PlannedPackageInstall],
|
||||||
|
) -> Result<usize> {
|
||||||
|
let contexts: Vec<_> = plans
|
||||||
|
.iter()
|
||||||
|
.map(|plan| plan.staged.hook_context.clone())
|
||||||
|
.collect();
|
||||||
|
install::hooks::run_transaction_hooks_batch(rootfs, phase, &contexts)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_staged_to_rootfs(
|
||||||
|
pkg_spec: &package::PackageSpec,
|
||||||
|
destdir: &Path,
|
||||||
|
rootfs: &Path,
|
||||||
|
config: &config::Config,
|
||||||
|
plan: &PlannedStagedInstall,
|
||||||
|
) -> Result<()> {
|
||||||
|
let staged_scripts_dir = install::scripts::staged_scripts_dir(destdir);
|
||||||
|
let installed_scripts_dir =
|
||||||
|
install::scripts::installed_scripts_dir(rootfs, &pkg_spec.package.name);
|
||||||
|
|
||||||
|
if plan.is_update {
|
||||||
let has_staged_pre = install::scripts::run_hook_if_present(
|
let has_staged_pre = install::scripts::run_hook_if_present(
|
||||||
&staged_scripts_dir,
|
&staged_scripts_dir,
|
||||||
install::scripts::Hook::PreUpdate,
|
install::scripts::Hook::PreUpdate,
|
||||||
@@ -562,10 +618,11 @@ fn install_staged_to_rootfs(
|
|||||||
destdir,
|
destdir,
|
||||||
rootfs,
|
rootfs,
|
||||||
&tx_base,
|
&tx_base,
|
||||||
&remove_paths,
|
&plan.remove_paths,
|
||||||
&pkg_spec.build.flags.keep,
|
&pkg_spec.build.flags.keep,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let db_path = config.db_dir.join("packages.db");
|
||||||
if let Err(e) = db::register_package(&db_path, pkg_spec, destdir) {
|
if let Err(e) = db::register_package(&db_path, pkg_spec, destdir) {
|
||||||
let _ = tx.rollback();
|
let _ = tx.rollback();
|
||||||
return Err(e);
|
return Err(e);
|
||||||
@@ -578,7 +635,7 @@ fn install_staged_to_rootfs(
|
|||||||
&pkg_spec.package.name,
|
&pkg_spec.package.name,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if is_update {
|
if plan.is_update {
|
||||||
let _ = install::scripts::run_hook_if_present(
|
let _ = install::scripts::run_hook_if_present(
|
||||||
&installed_scripts_dir,
|
&installed_scripts_dir,
|
||||||
install::scripts::Hook::PostUpdate,
|
install::scripts::Hook::PostUpdate,
|
||||||
@@ -594,36 +651,33 @@ fn install_staged_to_rootfs(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
install::hooks::run_transaction_hooks(
|
|
||||||
rootfs,
|
|
||||||
&install::hooks::HookExecutionContext {
|
|
||||||
phase: install::hooks::HookPhase::Post,
|
|
||||||
operation,
|
|
||||||
package: &pkg_spec.package.name,
|
|
||||||
affected_paths: &affected_paths,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn install_planned_packages_to_rootfs(
|
||||||
|
plans: &[PlannedPackageInstall],
|
||||||
|
rootfs: &Path,
|
||||||
|
config: &config::Config,
|
||||||
|
) -> Result<Vec<package::PackageInfo>> {
|
||||||
|
let mut installed = Vec::with_capacity(plans.len());
|
||||||
|
for plan in plans {
|
||||||
|
install_staged_to_rootfs(&plan.spec, &plan.destdir, rootfs, config, &plan.staged)?;
|
||||||
|
installed.push(plan.spec.package.clone());
|
||||||
|
}
|
||||||
|
Ok(installed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
fn install_package_outputs_to_rootfs(
|
fn install_package_outputs_to_rootfs(
|
||||||
pkg_spec: &package::PackageSpec,
|
pkg_spec: &package::PackageSpec,
|
||||||
destdir: &Path,
|
destdir: &Path,
|
||||||
rootfs: &Path,
|
rootfs: &Path,
|
||||||
config: &config::Config,
|
config: &config::Config,
|
||||||
) -> Result<Vec<package::PackageInfo>> {
|
) -> Result<Vec<package::PackageInfo>> {
|
||||||
let mut installed = Vec::new();
|
let plans = plan_package_outputs_for_install(pkg_spec, destdir, config)?;
|
||||||
for out in pkg_spec.outputs() {
|
run_transaction_hooks_for_plans(rootfs, install::hooks::HookPhase::Pre, &plans)?;
|
||||||
let mut spec_for_out = pkg_spec.clone();
|
let installed = install_planned_packages_to_rootfs(&plans, rootfs, config)?;
|
||||||
let output_name = out.name.clone();
|
run_transaction_hooks_for_plans(rootfs, install::hooks::HookPhase::Post, &plans)?;
|
||||||
spec_for_out.package = out;
|
|
||||||
spec_for_out.alternatives = pkg_spec.alternatives_for_output(&output_name);
|
|
||||||
spec_for_out.dependencies = pkg_spec.dependencies_for_output(&output_name);
|
|
||||||
let out_destdir = output_destdir_for(destdir, &pkg_spec.package.name, &output_name);
|
|
||||||
install_staged_to_rootfs(&spec_for_out, &out_destdir, rootfs, config)?;
|
|
||||||
installed.push(spec_for_out.package);
|
|
||||||
}
|
|
||||||
Ok(installed)
|
Ok(installed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1196,8 +1250,32 @@ fn execute_install_plan_with_child_commands(
|
|||||||
signature_pb.finish_and_clear();
|
signature_pb.finish_and_clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut binary_pre_hook_plans = Vec::new();
|
||||||
|
for step in &actionable_steps {
|
||||||
|
if let planner::PlanOrigin::Binary { repo_name, record } = &step.origin {
|
||||||
|
let cached = binary_archives
|
||||||
|
.get(&(repo_name.clone(), record.filename.clone()))
|
||||||
|
.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Cached archive missing for planned binary step '{}' from repo '{}'",
|
||||||
|
record.filename, repo_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let staged = extract_package_archive_to_staging(&cached.package_path)?;
|
||||||
|
let spec = package_spec_from_repo_record(record);
|
||||||
|
let plans = plan_package_outputs_for_install(&spec, staged.path(), config)?;
|
||||||
|
binary_pre_hook_plans.extend(plans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run_transaction_hooks_for_plans(
|
||||||
|
rootfs,
|
||||||
|
install::hooks::HookPhase::Pre,
|
||||||
|
&binary_pre_hook_plans,
|
||||||
|
)?;
|
||||||
|
|
||||||
let exe = std::env::current_exe().context("Failed to locate depot executable")?;
|
let exe = std::env::current_exe().context("Failed to locate depot executable")?;
|
||||||
let total_steps = actionable_steps.len();
|
let total_steps = actionable_steps.len();
|
||||||
|
let mut binary_post_hook_plans = Vec::new();
|
||||||
for (idx, step) in actionable_steps.into_iter().enumerate() {
|
for (idx, step) in actionable_steps.into_iter().enumerate() {
|
||||||
match &step.origin {
|
match &step.origin {
|
||||||
planner::PlanOrigin::Source { path, .. } => {
|
planner::PlanOrigin::Source { path, .. } => {
|
||||||
@@ -1241,8 +1319,9 @@ fn execute_install_plan_with_child_commands(
|
|||||||
})?;
|
})?;
|
||||||
let staged = extract_package_archive_to_staging(&cached.package_path)?;
|
let staged = extract_package_archive_to_staging(&cached.package_path)?;
|
||||||
let spec = package_spec_from_repo_record(record);
|
let spec = package_spec_from_repo_record(record);
|
||||||
let installed =
|
let plans = plan_package_outputs_for_install(&spec, staged.path(), config)?;
|
||||||
install_package_outputs_to_rootfs(&spec, staged.path(), rootfs, config)?;
|
let installed = install_planned_packages_to_rootfs(&plans, rootfs, config)?;
|
||||||
|
binary_post_hook_plans.extend(plans);
|
||||||
for pkg in installed {
|
for pkg in installed {
|
||||||
ui::success(format!("Installed {} v{}", pkg.name, pkg.version));
|
ui::success(format!("Installed {} v{}", pkg.name, pkg.version));
|
||||||
}
|
}
|
||||||
@@ -1251,6 +1330,11 @@ fn execute_install_plan_with_child_commands(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_transaction_hooks_for_plans(
|
||||||
|
rootfs,
|
||||||
|
install::hooks::HookPhase::Post,
|
||||||
|
&binary_post_hook_plans,
|
||||||
|
)?;
|
||||||
install::scripts::run_deferred_hooks_if_possible(rootfs)?;
|
install::scripts::run_deferred_hooks_if_possible(rootfs)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1528,6 +1612,8 @@ fn run_direct_install_request(
|
|||||||
destdir
|
destdir
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut transaction_plans = Vec::new();
|
||||||
|
|
||||||
if !options.lib32_only {
|
if !options.lib32_only {
|
||||||
if staging_dir.is_none() {
|
if staging_dir.is_none() {
|
||||||
// Source-build path: apply staging transforms (strip/compress/static cleanup).
|
// Source-build path: apply staging transforms (strip/compress/static cleanup).
|
||||||
@@ -1537,15 +1623,8 @@ fn run_direct_install_request(
|
|||||||
ui::info("Installing binary archive payload without staging transforms");
|
ui::info("Installing binary archive payload without staging transforms");
|
||||||
}
|
}
|
||||||
|
|
||||||
let installed =
|
let output_plans = plan_package_outputs_for_install(&pkg_spec, &destdir, config)?;
|
||||||
install_package_outputs_to_rootfs(&pkg_spec, &destdir, options.rootfs, config)?;
|
transaction_plans.extend(output_plans);
|
||||||
for pkg in installed {
|
|
||||||
ui::success(format!(
|
|
||||||
"Successfully installed {} v{}",
|
|
||||||
pkg.name, pkg.version
|
|
||||||
));
|
|
||||||
// TODO(snapper): create post-install snapshot after install commit succeeds.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(src_dir) = built_src_dir.as_deref()
|
if let Some(src_dir) = built_src_dir.as_deref()
|
||||||
@@ -1558,12 +1637,32 @@ fn run_direct_install_request(
|
|||||||
options.lib32_only,
|
options.lib32_only,
|
||||||
)?
|
)?
|
||||||
{
|
{
|
||||||
install_staged_to_rootfs(&lib32_spec, &lib32_destdir, options.rootfs, config)?;
|
let staged = plan_staged_install(&lib32_spec, &lib32_destdir, config)?;
|
||||||
|
transaction_plans.push(PlannedPackageInstall {
|
||||||
|
spec: lib32_spec,
|
||||||
|
destdir: lib32_destdir,
|
||||||
|
staged,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run_transaction_hooks_for_plans(
|
||||||
|
options.rootfs,
|
||||||
|
install::hooks::HookPhase::Pre,
|
||||||
|
&transaction_plans,
|
||||||
|
)?;
|
||||||
|
let installed = install_planned_packages_to_rootfs(&transaction_plans, options.rootfs, config)?;
|
||||||
|
for pkg in installed {
|
||||||
ui::success(format!(
|
ui::success(format!(
|
||||||
"Successfully installed {} v{}",
|
"Successfully installed {} v{}",
|
||||||
lib32_spec.package.name, lib32_spec.package.version
|
pkg.name, pkg.version
|
||||||
));
|
));
|
||||||
|
// TODO(snapper): create post-install snapshot after install commit succeeds.
|
||||||
}
|
}
|
||||||
|
run_transaction_hooks_for_plans(
|
||||||
|
options.rootfs,
|
||||||
|
install::hooks::HookPhase::Post,
|
||||||
|
&transaction_plans,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@@ -1925,37 +2024,43 @@ pub fn run(cli: Cli) -> Result<()> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if ui::prompt_package_action("installation", &install_targets, false)? {
|
if ui::prompt_package_action("installation", &install_targets, false)? {
|
||||||
|
let mut transaction_plans = Vec::new();
|
||||||
if !cli.lib32_only {
|
if !cli.lib32_only {
|
||||||
for out in pkg_spec.outputs() {
|
let output_plans =
|
||||||
let mut spec_for_out = pkg_spec.clone();
|
plan_package_outputs_for_install(&pkg_spec, &destdir, &config)?;
|
||||||
let output_name = out.name.clone();
|
transaction_plans.extend(output_plans);
|
||||||
spec_for_out.package = out;
|
}
|
||||||
spec_for_out.alternatives =
|
if let Some((lib32_spec, lib32_destdir)) = &lib32_install_bundle {
|
||||||
pkg_spec.alternatives_for_output(&output_name);
|
let staged = plan_staged_install(lib32_spec, lib32_destdir, &config)?;
|
||||||
spec_for_out.dependencies =
|
transaction_plans.push(PlannedPackageInstall {
|
||||||
pkg_spec.dependencies_for_output(&output_name);
|
spec: lib32_spec.clone(),
|
||||||
let out_destdir =
|
destdir: lib32_destdir.clone(),
|
||||||
output_destdir_for(&destdir, &pkg_spec.package.name, &output_name);
|
staged,
|
||||||
install_staged_to_rootfs(
|
});
|
||||||
&spec_for_out,
|
}
|
||||||
&out_destdir,
|
|
||||||
|
run_transaction_hooks_for_plans(
|
||||||
|
&cli.rootfs,
|
||||||
|
install::hooks::HookPhase::Pre,
|
||||||
|
&transaction_plans,
|
||||||
|
)?;
|
||||||
|
let installed = install_planned_packages_to_rootfs(
|
||||||
|
&transaction_plans,
|
||||||
&cli.rootfs,
|
&cli.rootfs,
|
||||||
&config,
|
&config,
|
||||||
)?;
|
)?;
|
||||||
|
for pkg in installed {
|
||||||
ui::success(format!(
|
ui::success(format!(
|
||||||
"Successfully installed {} v{}",
|
"Successfully installed {} v{}",
|
||||||
spec_for_out.package.name, spec_for_out.package.version
|
pkg.name, pkg.version
|
||||||
));
|
));
|
||||||
// TODO(snapper): create post-install snapshot after --install commit succeeds.
|
// TODO(snapper): create post-install snapshot after --install commit succeeds.
|
||||||
}
|
}
|
||||||
}
|
run_transaction_hooks_for_plans(
|
||||||
if let Some((lib32_spec, lib32_destdir)) = &lib32_install_bundle {
|
&cli.rootfs,
|
||||||
install_staged_to_rootfs(lib32_spec, lib32_destdir, &cli.rootfs, &config)?;
|
install::hooks::HookPhase::Post,
|
||||||
ui::success(format!(
|
&transaction_plans,
|
||||||
"Successfully installed {} v{}",
|
)?;
|
||||||
lib32_spec.package.name, lib32_spec.package.version
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
install::scripts::run_deferred_hooks_if_possible(&cli.rootfs)?;
|
install::scripts::run_deferred_hooks_if_possible(&cli.rootfs)?;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+139
-8
@@ -104,6 +104,17 @@ pub struct HookExecutionContext<'a> {
|
|||||||
pub affected_paths: &'a [String],
|
pub affected_paths: &'a [String],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Owned transaction hook context used for batched execution.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct HookExecutionContextOwned {
|
||||||
|
/// Current operation (`install`, `update`, `remove`).
|
||||||
|
pub operation: HookOperation,
|
||||||
|
/// Package being processed.
|
||||||
|
pub package: String,
|
||||||
|
/// Filesystem paths affected by this package action.
|
||||||
|
pub affected_paths: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TransactionHook {
|
struct TransactionHook {
|
||||||
file_path: PathBuf,
|
file_path: PathBuf,
|
||||||
@@ -195,11 +206,7 @@ pub fn run_transaction_hooks(rootfs: &Path, ctx: &HookExecutionContext<'_>) -> R
|
|||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let normalized_paths: Vec<String> = ctx
|
let normalized_paths = normalize_affected_paths(ctx.affected_paths);
|
||||||
.affected_paths
|
|
||||||
.iter()
|
|
||||||
.map(|p| normalize_match_target(p))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut executed = 0usize;
|
let mut executed = 0usize;
|
||||||
for hook_file in hook_files {
|
for hook_file in hook_files {
|
||||||
@@ -208,13 +215,83 @@ pub fn run_transaction_hooks(rootfs: &Path, ctx: &HookExecutionContext<'_>) -> R
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
run_hook_command(rootfs, &hook, ctx, &normalized_paths)?;
|
run_hook_command(rootfs, &hook, ctx, &normalized_paths, None)?;
|
||||||
executed += 1;
|
executed += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(executed)
|
Ok(executed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load and execute hooks for a batch of transaction contexts in stable order.
|
||||||
|
///
|
||||||
|
/// Hooks are discovered and parsed once, then matched against each context in
|
||||||
|
/// input order. Matching hooks run in hook-file order for each context.
|
||||||
|
///
|
||||||
|
/// Returns the number of hook commands executed.
|
||||||
|
pub fn run_transaction_hooks_batch(
|
||||||
|
rootfs: &Path,
|
||||||
|
phase: HookPhase,
|
||||||
|
contexts: &[HookExecutionContextOwned],
|
||||||
|
) -> Result<usize> {
|
||||||
|
if contexts.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hook_dir = transaction_hooks_dir(rootfs);
|
||||||
|
let hook_files = discover_hook_files(&hook_dir)?;
|
||||||
|
if hook_files.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hooks = hook_files
|
||||||
|
.iter()
|
||||||
|
.map(|hook_file| parse_hook_file(hook_file))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct ScheduledHookRun {
|
||||||
|
hook_idx: usize,
|
||||||
|
ctx_idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut scheduled = Vec::new();
|
||||||
|
for (ctx_idx, ctx_owned) in contexts.iter().enumerate() {
|
||||||
|
let normalized_paths = normalize_affected_paths(&ctx_owned.affected_paths);
|
||||||
|
let ctx = HookExecutionContext {
|
||||||
|
phase,
|
||||||
|
operation: ctx_owned.operation,
|
||||||
|
package: &ctx_owned.package,
|
||||||
|
affected_paths: &ctx_owned.affected_paths,
|
||||||
|
};
|
||||||
|
for (hook_idx, hook) in hooks.iter().enumerate() {
|
||||||
|
if hook.matches(&ctx, &normalized_paths) {
|
||||||
|
scheduled.push(ScheduledHookRun { hook_idx, ctx_idx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = scheduled.len();
|
||||||
|
for (run_idx, run) in scheduled.into_iter().enumerate() {
|
||||||
|
let ctx_owned = &contexts[run.ctx_idx];
|
||||||
|
let normalized_paths = normalize_affected_paths(&ctx_owned.affected_paths);
|
||||||
|
let ctx = HookExecutionContext {
|
||||||
|
phase,
|
||||||
|
operation: ctx_owned.operation,
|
||||||
|
package: &ctx_owned.package,
|
||||||
|
affected_paths: &ctx_owned.affected_paths,
|
||||||
|
};
|
||||||
|
run_hook_command(
|
||||||
|
rootfs,
|
||||||
|
&hooks[run.hook_idx],
|
||||||
|
&ctx,
|
||||||
|
&normalized_paths,
|
||||||
|
Some((run_idx + 1, total)),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(total)
|
||||||
|
}
|
||||||
|
|
||||||
fn discover_hook_files(hook_dir: &Path) -> Result<Vec<PathBuf>> {
|
fn discover_hook_files(hook_dir: &Path) -> Result<Vec<PathBuf>> {
|
||||||
if !hook_dir.exists() {
|
if !hook_dir.exists() {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
@@ -392,16 +469,28 @@ fn run_hook_command(
|
|||||||
hook: &TransactionHook,
|
hook: &TransactionHook,
|
||||||
ctx: &HookExecutionContext<'_>,
|
ctx: &HookExecutionContext<'_>,
|
||||||
normalized_paths: &[String],
|
normalized_paths: &[String],
|
||||||
|
sequence: Option<(usize, usize)>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let hook_name = hook.display_name();
|
let hook_name = hook.display_name();
|
||||||
|
if let Some((index, total)) = sequence {
|
||||||
crate::log_info!(
|
crate::log_info!(
|
||||||
"Running transaction hook '{}' ({}) for {}:{}:{}",
|
"Running transaction hook ({}/{}) '{}' for {}:{}:{}",
|
||||||
|
index,
|
||||||
|
total,
|
||||||
hook_name,
|
hook_name,
|
||||||
hook.file_path.display(),
|
|
||||||
ctx.operation.as_str(),
|
ctx.operation.as_str(),
|
||||||
ctx.phase.as_str(),
|
ctx.phase.as_str(),
|
||||||
ctx.package
|
ctx.package
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
crate::log_info!(
|
||||||
|
"Running transaction hook '{}' for {}:{}:{}",
|
||||||
|
hook_name,
|
||||||
|
ctx.operation.as_str(),
|
||||||
|
ctx.phase.as_str(),
|
||||||
|
ctx.package
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let stdin_payload = if hook.needs_paths {
|
let stdin_payload = if hook.needs_paths {
|
||||||
let mut payload = normalized_paths.join("\n");
|
let mut payload = normalized_paths.join("\n");
|
||||||
@@ -468,6 +557,10 @@ fn run_command_with_optional_stdin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn normalize_affected_paths(paths: &[String]) -> Vec<String> {
|
||||||
|
paths.iter().map(|p| normalize_match_target(p)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_match_target(raw: &str) -> String {
|
fn normalize_match_target(raw: &str) -> String {
|
||||||
raw.trim()
|
raw.trim()
|
||||||
.trim_start_matches("./")
|
.trim_start_matches("./")
|
||||||
@@ -613,4 +706,42 @@ command = "touch \"$DEPOT_ROOTFS/should_not_exist\""
|
|||||||
assert_eq!(ran, 0);
|
assert_eq!(ran, 0);
|
||||||
assert!(!tmp.path().join("should_not_exist").exists());
|
assert!(!tmp.path().join("should_not_exist").exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_transaction_hooks_batch_executes_in_context_order() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
write_hook(
|
||||||
|
tmp.path(),
|
||||||
|
"batch.toml",
|
||||||
|
r#"
|
||||||
|
[hook]
|
||||||
|
name = "batch"
|
||||||
|
|
||||||
|
[when]
|
||||||
|
phase = "post"
|
||||||
|
operation = ["install"]
|
||||||
|
|
||||||
|
[exec]
|
||||||
|
command = "printf '%s\n' \"$DEPOT_PACKAGE\" >> \"$DEPOT_ROOTFS/batch.out\""
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let contexts = vec![
|
||||||
|
HookExecutionContextOwned {
|
||||||
|
operation: HookOperation::Install,
|
||||||
|
package: "foo".to_string(),
|
||||||
|
affected_paths: Vec::new(),
|
||||||
|
},
|
||||||
|
HookExecutionContextOwned {
|
||||||
|
operation: HookOperation::Install,
|
||||||
|
package: "bar".to_string(),
|
||||||
|
affected_paths: Vec::new(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let ran = run_transaction_hooks_batch(tmp.path(), HookPhase::Post, &contexts).unwrap();
|
||||||
|
assert_eq!(ran, 2);
|
||||||
|
let out = std::fs::read_to_string(tmp.path().join("batch.out")).unwrap();
|
||||||
|
assert_eq!(out, "foo\nbar\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -880,6 +880,19 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if !spec.build.flags.ltoflags.is_empty() {
|
||||||
|
flags_tbl.insert(
|
||||||
|
"ltoflags".into(),
|
||||||
|
Value::Array(
|
||||||
|
spec.build
|
||||||
|
.flags
|
||||||
|
.ltoflags
|
||||||
|
.iter()
|
||||||
|
.map(|s| Value::String(s.clone()))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if !spec.build.flags.keep.is_empty() {
|
if !spec.build.flags.keep.is_empty() {
|
||||||
flags_tbl.insert(
|
flags_tbl.insert(
|
||||||
"keep".into(),
|
"keep".into(),
|
||||||
@@ -893,6 +906,9 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result<String> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if spec.build.flags.use_lto != defaults.use_lto {
|
||||||
|
flags_tbl.insert("use_lto".into(), Value::Boolean(spec.build.flags.use_lto));
|
||||||
|
}
|
||||||
if spec.build.flags.no_flags != defaults.no_flags {
|
if spec.build.flags.no_flags != defaults.no_flags {
|
||||||
flags_tbl.insert("no_flags".into(), Value::Boolean(spec.build.flags.no_flags));
|
flags_tbl.insert("no_flags".into(), Value::Boolean(spec.build.flags.no_flags));
|
||||||
}
|
}
|
||||||
@@ -1474,8 +1490,10 @@ mod tests {
|
|||||||
flags.config_settings = vec!["editable_mode=compat".into()];
|
flags.config_settings = vec!["editable_mode=compat".into()];
|
||||||
flags.rustflags = vec!["-Ctarget-cpu=native".into()];
|
flags.rustflags = vec!["-Ctarget-cpu=native".into()];
|
||||||
flags.cxxflags = vec!["-O2".into(), "-fno-rtti".into()];
|
flags.cxxflags = vec!["-O2".into(), "-fno-rtti".into()];
|
||||||
|
flags.ltoflags = vec!["-flto=auto".into()];
|
||||||
flags.target = "x86_64-unknown-linux-gnu".into();
|
flags.target = "x86_64-unknown-linux-gnu".into();
|
||||||
flags.keep = vec!["etc/locale.gen".into()];
|
flags.keep = vec!["etc/locale.gen".into()];
|
||||||
|
flags.use_lto = false;
|
||||||
flags.no_flags = true;
|
flags.no_flags = true;
|
||||||
flags.no_strip = true;
|
flags.no_strip = true;
|
||||||
flags.no_delete_static = true;
|
flags.no_delete_static = true;
|
||||||
@@ -1532,9 +1550,11 @@ mod tests {
|
|||||||
assert!(toml.contains("config_setting = ["));
|
assert!(toml.contains("config_setting = ["));
|
||||||
assert!(toml.contains("rustflags = ["));
|
assert!(toml.contains("rustflags = ["));
|
||||||
assert!(toml.contains("cxxflags = ["));
|
assert!(toml.contains("cxxflags = ["));
|
||||||
|
assert!(toml.contains("ltoflags = ["));
|
||||||
assert!(toml.contains("target = \"x86_64-unknown-linux-gnu\""));
|
assert!(toml.contains("target = \"x86_64-unknown-linux-gnu\""));
|
||||||
assert!(toml.contains("keep = ["));
|
assert!(toml.contains("keep = ["));
|
||||||
assert!(toml.contains("\"etc/locale.gen\""));
|
assert!(toml.contains("\"etc/locale.gen\""));
|
||||||
|
assert!(toml.contains("use_lto = false"));
|
||||||
assert!(toml.contains("no_flags = true"));
|
assert!(toml.contains("no_flags = true"));
|
||||||
assert!(toml.contains("no_strip = true"));
|
assert!(toml.contains("no_strip = true"));
|
||||||
assert!(toml.contains("no_delete_static = true"));
|
assert!(toml.contains("no_delete_static = true"));
|
||||||
|
|||||||
@@ -300,6 +300,17 @@ impl PackageSpec {
|
|||||||
self.build.flags.ldflags = vec![s.to_string()];
|
self.build.flags.ldflags = vec![s.to_string()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"ltoflags" | "lto_flags" | "lto-flags" => {
|
||||||
|
if let Some(arr) = v.as_array() {
|
||||||
|
self.build.flags.ltoflags = arr
|
||||||
|
.iter()
|
||||||
|
.filter_map(|x| x.as_str())
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
} else if let Some(s) = v.as_str() {
|
||||||
|
self.build.flags.ltoflags = vec![s.to_string()];
|
||||||
|
}
|
||||||
|
}
|
||||||
"keep" => {
|
"keep" => {
|
||||||
if let Some(arr) = v.as_array() {
|
if let Some(arr) = v.as_array() {
|
||||||
self.build.flags.keep = arr
|
self.build.flags.keep = arr
|
||||||
@@ -505,6 +516,11 @@ impl PackageSpec {
|
|||||||
self.build.flags.no_flags = b;
|
self.build.flags.no_flags = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"use_lto" | "use-lto" => {
|
||||||
|
if let Some(b) = toml_value_as_boolish(v) {
|
||||||
|
self.build.flags.use_lto = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
"no_strip" | "no-strip" => {
|
"no_strip" | "no-strip" => {
|
||||||
if let Some(b) = v.as_bool() {
|
if let Some(b) = v.as_bool() {
|
||||||
self.build.flags.no_strip = b;
|
self.build.flags.no_strip = b;
|
||||||
@@ -694,6 +710,18 @@ impl PackageSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"ltoflags" | "lto_flags" | "lto-flags" => {
|
||||||
|
for v in values {
|
||||||
|
if let Some(arr) = v.as_array() {
|
||||||
|
self.build
|
||||||
|
.flags
|
||||||
|
.ltoflags
|
||||||
|
.extend(arr.iter().filter_map(|x| x.as_str()).map(String::from));
|
||||||
|
} else if let Some(s) = v.as_str() {
|
||||||
|
self.build.flags.ltoflags.push(s.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"keep" => {
|
"keep" => {
|
||||||
for v in values {
|
for v in values {
|
||||||
if let Some(arr) = v.as_array() {
|
if let Some(arr) = v.as_array() {
|
||||||
@@ -1065,6 +1093,11 @@ impl PackageSpec {
|
|||||||
self.build.flags.no_flags = b;
|
self.build.flags.no_flags = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"use_lto" | "use-lto" => {
|
||||||
|
if let Some(b) = values.last().and_then(toml_value_as_boolish) {
|
||||||
|
self.build.flags.use_lto = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
"no_strip" | "no-strip" => {
|
"no_strip" | "no-strip" => {
|
||||||
if let Some(b) = values.last().and_then(|v| v.as_bool()) {
|
if let Some(b) = values.last().and_then(|v| v.as_bool()) {
|
||||||
self.build.flags.no_strip = b;
|
self.build.flags.no_strip = b;
|
||||||
@@ -1686,6 +1719,8 @@ make_vars = ["V=1"]
|
|||||||
make_dirs = ["lib"]
|
make_dirs = ["lib"]
|
||||||
make_test_dirs = ["tests"]
|
make_test_dirs = ["tests"]
|
||||||
make_install_dirs = ["lib"]
|
make_install_dirs = ["lib"]
|
||||||
|
ltoflags = ["-flto=auto"]
|
||||||
|
use_lto = true
|
||||||
no_flags = true
|
no_flags = true
|
||||||
no_strip = true
|
no_strip = true
|
||||||
no_delete_static = true
|
no_delete_static = true
|
||||||
@@ -1719,6 +1754,16 @@ post_configure = ["echo configured"]
|
|||||||
"etc/locale.gen".to_string(),
|
"etc/locale.gen".to_string(),
|
||||||
)])],
|
)])],
|
||||||
);
|
);
|
||||||
|
config.appends.insert(
|
||||||
|
"build.flags.ltoflags".to_string(),
|
||||||
|
vec![toml::Value::Array(vec![toml::Value::String(
|
||||||
|
"-fno-fat-lto-objects".to_string(),
|
||||||
|
)])],
|
||||||
|
);
|
||||||
|
config.appends.insert(
|
||||||
|
"build.flags.use_lto".to_string(),
|
||||||
|
vec![toml::Value::Boolean(false)],
|
||||||
|
);
|
||||||
config.appends.insert(
|
config.appends.insert(
|
||||||
"build.flags.no_strip".to_string(),
|
"build.flags.no_strip".to_string(),
|
||||||
vec![toml::Value::Boolean(false)],
|
vec![toml::Value::Boolean(false)],
|
||||||
@@ -1794,6 +1839,19 @@ post_configure = ["echo configured"]
|
|||||||
.rustflags
|
.rustflags
|
||||||
.contains(&"opt-level=3".to_string())
|
.contains(&"opt-level=3".to_string())
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
spec.build
|
||||||
|
.flags
|
||||||
|
.ltoflags
|
||||||
|
.contains(&"-flto=auto".to_string())
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
spec.build
|
||||||
|
.flags
|
||||||
|
.ltoflags
|
||||||
|
.contains(&"-fno-fat-lto-objects".to_string())
|
||||||
|
);
|
||||||
|
assert!(!spec.build.flags.use_lto);
|
||||||
assert!(spec.build.flags.no_flags);
|
assert!(spec.build.flags.no_flags);
|
||||||
assert!(!spec.build.flags.no_strip);
|
assert!(!spec.build.flags.no_strip);
|
||||||
assert!(!spec.build.flags.no_delete_static);
|
assert!(!spec.build.flags.no_delete_static);
|
||||||
@@ -1917,6 +1975,79 @@ no_flags = true
|
|||||||
assert!(spec.build.flags.no_flags);
|
assert!(spec.build.flags.no_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_ltoflags_and_use_lto_from_spec() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let path = tmp.path().join("pkg.toml");
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
&path,
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0"
|
||||||
|
description = "d"
|
||||||
|
homepage = "h"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[source]
|
||||||
|
url = "https://example.com/foo.tar.gz"
|
||||||
|
sha256 = "skip"
|
||||||
|
extract_dir = "foo"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
type = "custom"
|
||||||
|
|
||||||
|
[build.flags]
|
||||||
|
ltoflags = ["-flto=auto", "-fuse-linker-plugin"]
|
||||||
|
use_lto = false
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let spec = PackageSpec::from_file(&path).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
spec.build.flags.ltoflags,
|
||||||
|
vec!["-flto=auto".to_string(), "-fuse-linker-plugin".to_string()]
|
||||||
|
);
|
||||||
|
assert!(!spec.build.flags.use_lto);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_ltoflags_and_use_lto_aliases_from_spec() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let path = tmp.path().join("pkg.toml");
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
&path,
|
||||||
|
r#"
|
||||||
|
[package]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0"
|
||||||
|
description = "d"
|
||||||
|
homepage = "h"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[source]
|
||||||
|
url = "https://example.com/foo.tar.gz"
|
||||||
|
sha256 = "skip"
|
||||||
|
extract_dir = "foo"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
type = "custom"
|
||||||
|
|
||||||
|
[build.flags]
|
||||||
|
LTOFLAGS = "-flto=auto"
|
||||||
|
"use-lto" = false
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let spec = PackageSpec::from_file(&path).unwrap();
|
||||||
|
assert_eq!(spec.build.flags.ltoflags, vec!["-flto=auto".to_string()]);
|
||||||
|
assert!(!spec.build.flags.use_lto);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_no_strip_no_delete_static_and_no_compress_man_from_spec() {
|
fn parse_no_strip_no_delete_static_and_no_compress_man_from_spec() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
@@ -2800,9 +2931,28 @@ pub struct BuildFlags {
|
|||||||
pub cxxflags_lib32: Vec<String>,
|
pub cxxflags_lib32: Vec<String>,
|
||||||
#[serde(default, deserialize_with = "deserialize_string_or_array")]
|
#[serde(default, deserialize_with = "deserialize_string_or_array")]
|
||||||
pub ldflags: Vec<String>,
|
pub ldflags: Vec<String>,
|
||||||
|
/// Link-time optimization flags exported to `LTOFLAGS`.
|
||||||
|
///
|
||||||
|
/// When `use_lto` is true (default), these flags are also appended to
|
||||||
|
/// `CFLAGS`, `CXXFLAGS`, and `LDFLAGS`.
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
alias = "lto-flags",
|
||||||
|
alias = "lto_flags",
|
||||||
|
alias = "LTOFLAGS",
|
||||||
|
deserialize_with = "deserialize_string_or_array"
|
||||||
|
)]
|
||||||
|
pub ltoflags: Vec<String>,
|
||||||
/// Keep existing files and install package-provided replacement as `<path>.depotnew`.
|
/// Keep existing files and install package-provided replacement as `<path>.depotnew`.
|
||||||
#[serde(default, deserialize_with = "deserialize_string_or_array")]
|
#[serde(default, deserialize_with = "deserialize_string_or_array")]
|
||||||
pub keep: Vec<String>,
|
pub keep: Vec<String>,
|
||||||
|
/// Disable automatic LTOFLAGS injection into CFLAGS/CXXFLAGS/LDFLAGS.
|
||||||
|
#[serde(
|
||||||
|
default = "default_use_lto",
|
||||||
|
alias = "use-lto",
|
||||||
|
deserialize_with = "deserialize_boolish"
|
||||||
|
)]
|
||||||
|
pub use_lto: bool,
|
||||||
/// Disable exporting CFLAGS/CXXFLAGS/LDFLAGS for this package build.
|
/// Disable exporting CFLAGS/CXXFLAGS/LDFLAGS for this package build.
|
||||||
#[serde(default, alias = "no-flags")]
|
#[serde(default, alias = "no-flags")]
|
||||||
pub no_flags: bool,
|
pub no_flags: bool,
|
||||||
@@ -3071,7 +3221,9 @@ impl Default for BuildFlags {
|
|||||||
cxxflags: Vec::new(),
|
cxxflags: Vec::new(),
|
||||||
cxxflags_lib32: Vec::new(),
|
cxxflags_lib32: Vec::new(),
|
||||||
ldflags: Vec::new(),
|
ldflags: Vec::new(),
|
||||||
|
ltoflags: Vec::new(),
|
||||||
keep: Vec::new(),
|
keep: Vec::new(),
|
||||||
|
use_lto: default_use_lto(),
|
||||||
no_flags: false,
|
no_flags: false,
|
||||||
no_strip: false,
|
no_strip: false,
|
||||||
no_delete_static: false,
|
no_delete_static: false,
|
||||||
@@ -3256,6 +3408,10 @@ fn default_cc() -> String {
|
|||||||
"gcc".to_string()
|
"gcc".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_use_lto() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn default_ar() -> String {
|
fn default_ar() -> String {
|
||||||
"ar".to_string()
|
"ar".to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user