diff --git a/contrib/depot.d/build.toml b/contrib/depot.d/build.toml index fa7fa08..e6921f0 100644 --- a/contrib/depot.d/build.toml +++ b/contrib/depot.d/build.toml @@ -7,6 +7,11 @@ cc = "gcc" cflags = ["-O2"] cflags += ["-pipe"] +# Optional install directory overrides for supported builders +#bindir = "/usr/bin" +#libdir = "/usr/lib" +#sysconfdir = "/etc" + # Default LDFLAGS ldflags = ["-Wl,-O1"] diff --git a/contrib/depot.toml b/contrib/depot.toml index 1da988b..e7ce2c5 100644 --- a/contrib/depot.toml +++ b/contrib/depot.toml @@ -13,6 +13,20 @@ cflags = ["-O2"] # Append additional flags (demonstrates += append support) cflags += ["-g"] +# Optional install directory overrides for supported builders +#bindir = "/usr/bin" +#sbindir = "/usr/bin" +#libdir = "/usr/lib" +#libexecdir = "/usr/lib" +#sysconfdir = "/etc" +#localstatedir = "/var" +#sharedstatedir = "/var/lib" +#includedir = "/usr/include" +#datarootdir = "/usr/share" +#datadir = "/usr/share" +#mandir = "/usr/share/man" +#infodir = "/usr/share/info" + # Linker flags ldflags = ["-Wl,-O1"] diff --git a/contrib/user.depot.toml.example b/contrib/user.depot.toml.example index ea065f1..037707c 100644 --- a/contrib/user.depot.toml.example +++ b/contrib/user.depot.toml.example @@ -8,3 +8,8 @@ cc = "clang" cflags = ["-O2"] cflags += ["-g"] carch = "x86_64" + +# Optional install directory overrides for supported builders +#bindir = "/usr/local/bin" +#sbindir = "/usr/local/sbin" +#libdir = "/usr/local/lib" diff --git a/src/builder/autotools.rs b/src/builder/autotools.rs index 09bd951..3cbb4c9 100755 --- a/src/builder/autotools.rs +++ b/src/builder/autotools.rs @@ -442,25 +442,20 @@ fn default_configure_install_dirs( flags: &crate::package::BuildFlags, help_text: Option<&str>, ) -> Vec { - let libdir = if flags.lib32_variant { - "/usr/lib32" - } else { - "/usr/lib" - }; - let bindir = nonempty_trimmed(&flags.bindir).unwrap_or("/usr/bin"); + let dirs = crate::builder::install_dirs(flags); let defaults = [ - ("--bindir", bindir), - ("--sbindir", "/usr/bin"), - ("--libdir", libdir), - ("--libexecdir", libdir), - ("--sysconfdir", "/etc"), - ("--localstatedir", "/var"), - ("--sharedstatedir", "/var/lib"), - ("--includedir", "/usr/include"), - ("--datarootdir", "/usr/share"), - ("--datadir", "/usr/share"), - ("--mandir", "/usr/share/man"), - ("--infodir", "/usr/share/info"), + ("--bindir", dirs.bindir), + ("--sbindir", dirs.sbindir), + ("--libdir", dirs.libdir), + ("--libexecdir", dirs.libexecdir), + ("--sysconfdir", dirs.sysconfdir), + ("--localstatedir", dirs.localstatedir), + ("--sharedstatedir", dirs.sharedstatedir), + ("--includedir", dirs.includedir), + ("--datarootdir", dirs.datarootdir), + ("--datadir", dirs.datadir), + ("--mandir", dirs.mandir), + ("--infodir", dirs.infodir), ]; defaults diff --git a/src/builder/cmake.rs b/src/builder/cmake.rs index fed7e51..d211833 100755 --- a/src/builder/cmake.rs +++ b/src/builder/cmake.rs @@ -35,12 +35,8 @@ pub fn build( let env_vars = crate::builder::standard_build_env(spec, cross, true, export_compiler_flags); // Extract prefix from configure flags (cmake-style -DCMAKE_INSTALL_PREFIX=) - let prefix = flags - .configure - .iter() - .find(|s| s.contains("CMAKE_INSTALL_PREFIX=")) - .and_then(|s| s.split('=').nth(1)) - .unwrap_or(&flags.prefix); + let prefix = cmake_cache_entry_value(&flags.configure, "CMAKE_INSTALL_PREFIX") + .unwrap_or(flags.prefix.as_str()); // Generate toolchain file if cross-compiling let toolchain_file = if let Some(cc_cfg) = cross { @@ -64,6 +60,9 @@ pub fn build( cmake_cmd.arg("-B").arg(&build_dir); cmake_cmd.arg(format!("-DCMAKE_INSTALL_PREFIX={}", prefix)); cmake_cmd.arg("-DCMAKE_BUILD_TYPE=Release"); + for arg in cmake_install_dir_args(flags) { + cmake_cmd.arg(arg); + } // Add toolchain file for cross-compilation if let Some(ref tf) = toolchain_file { @@ -283,12 +282,47 @@ fn cmake_configure_flags_specify_generator(flags: &[String]) -> bool { } fn cmake_configure_flags_set_make_program(flags: &[String]) -> bool { - flags.iter().any(|flag| { + cmake_cache_entry_value(flags, "CMAKE_MAKE_PROGRAM").is_some() +} + +fn cmake_cache_entry_value<'a>(flags: &'a [String], variable: &str) -> Option<&'a str> { + let plain_prefix = format!("-D{variable}="); + let typed_prefix = format!("-D{variable}:"); + flags.iter().find_map(|flag| { let trimmed = flag.trim(); - trimmed.starts_with("-DCMAKE_MAKE_PROGRAM=") + if let Some(value) = trimmed.strip_prefix(&plain_prefix) { + return Some(value); + } + trimmed + .strip_prefix(&typed_prefix) + .and_then(|rest| rest.split_once('=').map(|(_, value)| value)) }) } +fn cmake_install_dir_args(flags: &crate::package::BuildFlags) -> Vec { + let dirs = crate::builder::install_dirs(flags); + let defaults = [ + ("CMAKE_INSTALL_BINDIR", dirs.bindir), + ("CMAKE_INSTALL_SBINDIR", dirs.sbindir), + ("CMAKE_INSTALL_LIBDIR", dirs.libdir), + ("CMAKE_INSTALL_LIBEXECDIR", dirs.libexecdir), + ("CMAKE_INSTALL_SYSCONFDIR", dirs.sysconfdir), + ("CMAKE_INSTALL_LOCALSTATEDIR", dirs.localstatedir), + ("CMAKE_INSTALL_SHAREDSTATEDIR", dirs.sharedstatedir), + ("CMAKE_INSTALL_INCLUDEDIR", dirs.includedir), + ("CMAKE_INSTALL_DATAROOTDIR", dirs.datarootdir), + ("CMAKE_INSTALL_DATADIR", dirs.datadir), + ("CMAKE_INSTALL_MANDIR", dirs.mandir), + ("CMAKE_INSTALL_INFODIR", dirs.infodir), + ]; + + defaults + .into_iter() + .filter(|(variable, _)| cmake_cache_entry_value(&flags.configure, variable).is_none()) + .map(|(variable, value)| format!("-D{variable}={value}")) + .collect() +} + fn num_cpus() -> usize { std::thread::available_parallelism() .map(|n| n.get()) @@ -429,6 +463,98 @@ mod tests { ])); } + #[test] + fn test_cmake_cache_entry_value_supports_plain_and_typed_entries() { + let flags = vec![ + "-DCMAKE_INSTALL_PREFIX=/usr".to_string(), + "-DCMAKE_INSTALL_LIBDIR:PATH=/usr/lib64".to_string(), + ]; + + assert_eq!( + cmake_cache_entry_value(&flags, "CMAKE_INSTALL_PREFIX"), + Some("/usr") + ); + assert_eq!( + cmake_cache_entry_value(&flags, "CMAKE_INSTALL_LIBDIR"), + Some("/usr/lib64") + ); + assert_eq!( + cmake_cache_entry_value(&flags, "CMAKE_INSTALL_BINDIR"), + None + ); + } + + #[test] + fn test_cmake_install_dir_args_include_defaults() { + let args = cmake_install_dir_args(&BuildFlags::default()); + assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=/usr/bin")); + assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_SBINDIR=/usr/bin")); + assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_LIBDIR=/usr/lib")); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_LIBEXECDIR=/usr/lib") + ); + assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_SYSCONFDIR=/etc")); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_LOCALSTATEDIR=/var") + ); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_SHAREDSTATEDIR=/var/lib") + ); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_INCLUDEDIR=/usr/include") + ); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_DATAROOTDIR=/usr/share") + ); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_DATADIR=/usr/share") + ); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_MANDIR=/usr/share/man") + ); + assert!( + args.iter() + .any(|a| a == "-DCMAKE_INSTALL_INFODIR=/usr/share/info") + ); + } + + #[test] + fn test_cmake_install_dir_args_respect_explicit_user_overrides() { + let flags = BuildFlags { + configure: vec![ + "-DCMAKE_INSTALL_SBINDIR=/sbin".to_string(), + "-DCMAKE_INSTALL_LIBDIR:PATH=/custom/lib".to_string(), + "-DCMAKE_INSTALL_DATADIR=/custom/share".to_string(), + ], + ..BuildFlags::default() + }; + + let args = cmake_install_dir_args(&flags); + assert!( + !args + .iter() + .any(|a| a.starts_with("-DCMAKE_INSTALL_SBINDIR=")) + ); + assert!( + !args + .iter() + .any(|a| a.starts_with("-DCMAKE_INSTALL_LIBDIR=")) + ); + assert!( + !args + .iter() + .any(|a| a.starts_with("-DCMAKE_INSTALL_DATADIR=")) + ); + assert!(args.iter().any(|a| a == "-DCMAKE_INSTALL_BINDIR=/usr/bin")); + } + #[test] fn resolve_actual_src_prefers_srcdir_then_specdir_and_handles_absolute() { let tmp = tempdir().unwrap(); diff --git a/src/builder/meson.rs b/src/builder/meson.rs index d76e518..886df63 100755 --- a/src/builder/meson.rs +++ b/src/builder/meson.rs @@ -162,10 +162,29 @@ fn meson_setup_args( env_vars: &[(String, String)], ) -> Vec { let mut args = Vec::new(); + let dirs = crate::builder::install_dirs(flags); if !has_option(&flags.configure, "--prefix") { args.push(format!("--prefix={}", flags.prefix)); } + for (option, value) in [ + ("--bindir", dirs.bindir), + ("--sbindir", dirs.sbindir), + ("--libdir", dirs.libdir), + ("--libexecdir", dirs.libexecdir), + ("--sysconfdir", dirs.sysconfdir), + ("--localstatedir", dirs.localstatedir), + ("--sharedstatedir", dirs.sharedstatedir), + ("--includedir", dirs.includedir), + ("--datarootdir", dirs.datarootdir), + ("--datadir", dirs.datadir), + ("--mandir", dirs.mandir), + ("--infodir", dirs.infodir), + ] { + if !has_option(&flags.configure, option) { + args.push(format!("{option}={value}")); + } + } if !has_option(&flags.configure, "--buildtype") { args.push("--buildtype=release".to_string()); } @@ -275,6 +294,23 @@ mod tests { assert!(args.iter().any(|a| a == "--buildtype=release")); } + #[test] + fn test_meson_setup_args_include_install_dirs() { + let args = meson_setup_args(&BuildFlags::default(), None, &[]); + assert!(args.iter().any(|a| a == "--bindir=/usr/bin")); + assert!(args.iter().any(|a| a == "--sbindir=/usr/bin")); + assert!(args.iter().any(|a| a == "--libdir=/usr/lib")); + assert!(args.iter().any(|a| a == "--libexecdir=/usr/lib")); + assert!(args.iter().any(|a| a == "--sysconfdir=/etc")); + assert!(args.iter().any(|a| a == "--localstatedir=/var")); + assert!(args.iter().any(|a| a == "--sharedstatedir=/var/lib")); + assert!(args.iter().any(|a| a == "--includedir=/usr/include")); + assert!(args.iter().any(|a| a == "--datarootdir=/usr/share")); + assert!(args.iter().any(|a| a == "--datadir=/usr/share")); + assert!(args.iter().any(|a| a == "--mandir=/usr/share/man")); + assert!(args.iter().any(|a| a == "--infodir=/usr/share/info")); + } + #[test] fn test_meson_setup_args_honor_explicit_prefix() { let flags = BuildFlags { @@ -288,6 +324,24 @@ mod tests { assert!(args.iter().any(|a| a == "--prefix=/opt")); } + #[test] + fn test_meson_setup_args_honor_explicit_install_dirs() { + let flags = BuildFlags { + configure: vec![ + "--sbindir=/sbin".to_string(), + "--libdir=/custom/lib".to_string(), + "--datadir=/custom/share".to_string(), + ], + ..BuildFlags::default() + }; + + let args = meson_setup_args(&flags, None, &[]); + assert!(!args.iter().any(|a| a == "--sbindir=/usr/bin")); + assert!(!args.iter().any(|a| a == "--libdir=/usr/lib")); + assert!(!args.iter().any(|a| a == "--datadir=/usr/share")); + assert!(args.iter().any(|a| a == "--bindir=/usr/bin")); + } + #[test] fn test_resolve_build_dir_uses_flag() { let flags = BuildFlags { diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 3345723..4cff683 100755 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -20,6 +20,22 @@ use std::process::{Command, Stdio}; pub type EnvVars = Vec<(String, String)>; +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct InstallDirs { + pub bindir: String, + pub sbindir: String, + pub libdir: String, + pub libexecdir: String, + pub sysconfdir: String, + pub localstatedir: String, + pub sharedstatedir: String, + pub includedir: String, + pub datarootdir: String, + pub datadir: String, + pub mandir: String, + pub infodir: String, +} + pub fn set_env_var(env_vars: &mut EnvVars, key: &str, value: impl Into) { let value = value.into(); if let Some((_, existing)) = env_vars.iter_mut().find(|(k, _)| k == key) { @@ -29,6 +45,46 @@ pub fn set_env_var(env_vars: &mut EnvVars, key: &str, value: impl Into) } } +fn default_libdir_for_variant(lib32_variant: bool) -> &'static str { + if lib32_variant { + "/usr/lib32" + } else { + "/usr/lib" + } +} + +fn configured_install_dir(value: &str, default: &str) -> String { + let trimmed = value.trim(); + if trimmed.is_empty() { + default.to_string() + } else { + trimmed.to_string() + } +} + +pub(crate) fn install_dirs(flags: &crate::package::BuildFlags) -> InstallDirs { + let libdir = configured_install_dir( + &flags.libdir, + default_libdir_for_variant(flags.lib32_variant), + ); + let datarootdir = configured_install_dir(&flags.datarootdir, "/usr/share"); + + InstallDirs { + bindir: configured_install_dir(&flags.bindir, "/usr/bin"), + sbindir: configured_install_dir(&flags.sbindir, "/usr/bin"), + libdir: libdir.clone(), + libexecdir: configured_install_dir(&flags.libexecdir, &libdir), + sysconfdir: configured_install_dir(&flags.sysconfdir, "/etc"), + localstatedir: configured_install_dir(&flags.localstatedir, "/var"), + sharedstatedir: configured_install_dir(&flags.sharedstatedir, "/var/lib"), + includedir: configured_install_dir(&flags.includedir, "/usr/include"), + datarootdir: datarootdir.clone(), + datadir: configured_install_dir(&flags.datadir, &datarootdir), + mandir: configured_install_dir(&flags.mandir, "/usr/share/man"), + infodir: configured_install_dir(&flags.infodir, "/usr/share/info"), + } +} + fn compiler_flag_sets( flags: &crate::package::BuildFlags, ) -> (Vec, Vec, Vec, Vec) { @@ -551,4 +607,38 @@ mod tests { "expected default CC to take precedence over passthrough CC" ); } + + #[test] + fn test_install_dirs_use_defaults_and_lib32_fallbacks() { + let default_dirs = install_dirs(&BuildFlags::default()); + assert_eq!(default_dirs.bindir, "/usr/bin"); + assert_eq!(default_dirs.sbindir, "/usr/bin"); + assert_eq!(default_dirs.libdir, "/usr/lib"); + assert_eq!(default_dirs.libexecdir, "/usr/lib"); + assert_eq!(default_dirs.datarootdir, "/usr/share"); + assert_eq!(default_dirs.datadir, "/usr/share"); + + let lib32_dirs = install_dirs(&BuildFlags { + lib32_variant: true, + ..BuildFlags::default() + }); + assert_eq!(lib32_dirs.libdir, "/usr/lib32"); + assert_eq!(lib32_dirs.libexecdir, "/usr/lib32"); + } + + #[test] + fn test_install_dirs_respect_explicit_overrides_and_derived_defaults() { + let dirs = install_dirs(&BuildFlags { + bindir: "/opt/bin".into(), + libdir: "/opt/lib64".into(), + datarootdir: "/opt/share-root".into(), + ..BuildFlags::default() + }); + + assert_eq!(dirs.bindir, "/opt/bin"); + assert_eq!(dirs.libdir, "/opt/lib64"); + assert_eq!(dirs.libexecdir, "/opt/lib64"); + assert_eq!(dirs.datarootdir, "/opt/share-root"); + assert_eq!(dirs.datadir, "/opt/share-root"); + } } diff --git a/src/package/interactive.rs b/src/package/interactive.rs index 366726c..62cc86e 100644 --- a/src/package/interactive.rs +++ b/src/package/interactive.rs @@ -1274,6 +1274,72 @@ pub fn spec_to_minimal_toml(spec: &PackageSpec) -> anyhow::Result { Value::String(spec.build.flags.bindir.clone()), ); } + if !spec.build.flags.sbindir.is_empty() { + flags_tbl.insert( + "sbindir".into(), + Value::String(spec.build.flags.sbindir.clone()), + ); + } + if !spec.build.flags.libdir.is_empty() { + flags_tbl.insert( + "libdir".into(), + Value::String(spec.build.flags.libdir.clone()), + ); + } + if !spec.build.flags.libexecdir.is_empty() { + flags_tbl.insert( + "libexecdir".into(), + Value::String(spec.build.flags.libexecdir.clone()), + ); + } + if !spec.build.flags.sysconfdir.is_empty() { + flags_tbl.insert( + "sysconfdir".into(), + Value::String(spec.build.flags.sysconfdir.clone()), + ); + } + if !spec.build.flags.localstatedir.is_empty() { + flags_tbl.insert( + "localstatedir".into(), + Value::String(spec.build.flags.localstatedir.clone()), + ); + } + if !spec.build.flags.sharedstatedir.is_empty() { + flags_tbl.insert( + "sharedstatedir".into(), + Value::String(spec.build.flags.sharedstatedir.clone()), + ); + } + if !spec.build.flags.includedir.is_empty() { + flags_tbl.insert( + "includedir".into(), + Value::String(spec.build.flags.includedir.clone()), + ); + } + if !spec.build.flags.datarootdir.is_empty() { + flags_tbl.insert( + "datarootdir".into(), + Value::String(spec.build.flags.datarootdir.clone()), + ); + } + if !spec.build.flags.datadir.is_empty() { + flags_tbl.insert( + "datadir".into(), + Value::String(spec.build.flags.datadir.clone()), + ); + } + if !spec.build.flags.mandir.is_empty() { + flags_tbl.insert( + "mandir".into(), + Value::String(spec.build.flags.mandir.clone()), + ); + } + if !spec.build.flags.infodir.is_empty() { + flags_tbl.insert( + "infodir".into(), + Value::String(spec.build.flags.infodir.clone()), + ); + } if !spec.build.flags.binary_type.is_empty() { flags_tbl.insert( "binary_type".into(), @@ -1497,6 +1563,17 @@ mod tests { ltoflags: vec!["-flto=auto".into()], target: "x86_64-unknown-linux-gnu".into(), keep: vec!["etc/locale.gen".into()], + sbindir: "/usr/sbin".into(), + libdir: "/usr/lib64".into(), + libexecdir: "/usr/libexec".into(), + sysconfdir: "/etc/custom".into(), + localstatedir: "/var/custom".into(), + sharedstatedir: "/var/lib/custom".into(), + includedir: "/usr/include/custom".into(), + datarootdir: "/usr/share/root".into(), + datadir: "/usr/share/custom".into(), + mandir: "/usr/share/custom/man".into(), + infodir: "/usr/share/custom/info".into(), use_lto: false, no_flags: true, no_strip: true, @@ -1560,6 +1637,17 @@ mod tests { assert!(toml.contains("target = \"x86_64-unknown-linux-gnu\"")); assert!(toml.contains("keep = [")); assert!(toml.contains("\"etc/locale.gen\"")); + assert!(toml.contains("sbindir = \"/usr/sbin\"")); + assert!(toml.contains("libdir = \"/usr/lib64\"")); + assert!(toml.contains("libexecdir = \"/usr/libexec\"")); + assert!(toml.contains("sysconfdir = \"/etc/custom\"")); + assert!(toml.contains("localstatedir = \"/var/custom\"")); + assert!(toml.contains("sharedstatedir = \"/var/lib/custom\"")); + assert!(toml.contains("includedir = \"/usr/include/custom\"")); + assert!(toml.contains("datarootdir = \"/usr/share/root\"")); + assert!(toml.contains("datadir = \"/usr/share/custom\"")); + assert!(toml.contains("mandir = \"/usr/share/custom/man\"")); + assert!(toml.contains("infodir = \"/usr/share/custom/info\"")); assert!(toml.contains("use_lto = false")); assert!(toml.contains("no_flags = true")); assert!(toml.contains("no_strip = true")); diff --git a/src/package/spec.rs b/src/package/spec.rs index 04acb9e..35922f6 100755 --- a/src/package/spec.rs +++ b/src/package/spec.rs @@ -342,6 +342,66 @@ impl PackageSpec { self.build.flags.prefix = s.to_string(); } } + "bindir" => { + if let Some(s) = v.as_str() { + self.build.flags.bindir = s.to_string(); + } + } + "sbindir" => { + if let Some(s) = v.as_str() { + self.build.flags.sbindir = s.to_string(); + } + } + "libdir" => { + if let Some(s) = v.as_str() { + self.build.flags.libdir = s.to_string(); + } + } + "libexecdir" => { + if let Some(s) = v.as_str() { + self.build.flags.libexecdir = s.to_string(); + } + } + "sysconfdir" => { + if let Some(s) = v.as_str() { + self.build.flags.sysconfdir = s.to_string(); + } + } + "localstatedir" => { + if let Some(s) = v.as_str() { + self.build.flags.localstatedir = s.to_string(); + } + } + "sharedstatedir" => { + if let Some(s) = v.as_str() { + self.build.flags.sharedstatedir = s.to_string(); + } + } + "includedir" => { + if let Some(s) = v.as_str() { + self.build.flags.includedir = s.to_string(); + } + } + "datarootdir" => { + if let Some(s) = v.as_str() { + self.build.flags.datarootdir = s.to_string(); + } + } + "datadir" => { + if let Some(s) = v.as_str() { + self.build.flags.datadir = s.to_string(); + } + } + "mandir" => { + if let Some(s) = v.as_str() { + self.build.flags.mandir = s.to_string(); + } + } + "infodir" => { + if let Some(s) = v.as_str() { + self.build.flags.infodir = s.to_string(); + } + } "chost" => { if let Some(s) = v.as_str() { self.build.flags.chost = s.to_string(); @@ -886,6 +946,66 @@ impl PackageSpec { self.build.flags.prefix = s.to_string(); } } + "bindir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.bindir = s.to_string(); + } + } + "sbindir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.sbindir = s.to_string(); + } + } + "libdir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.libdir = s.to_string(); + } + } + "libexecdir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.libexecdir = s.to_string(); + } + } + "sysconfdir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.sysconfdir = s.to_string(); + } + } + "localstatedir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.localstatedir = s.to_string(); + } + } + "sharedstatedir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.sharedstatedir = s.to_string(); + } + } + "includedir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.includedir = s.to_string(); + } + } + "datarootdir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.datarootdir = s.to_string(); + } + } + "datadir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.datadir = s.to_string(); + } + } + "mandir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.mandir = s.to_string(); + } + } + "infodir" => { + if let Some(s) = values.last().and_then(|v| v.as_str()) { + self.build.flags.infodir = s.to_string(); + } + } "chost" => { if let Some(s) = values.last().and_then(|v| v.as_str()) { self.build.flags.chost = s.to_string(); @@ -1773,6 +1893,11 @@ cc = "my-cc" cflags = ["-O2"] cxxflags = ["-O2", "-pipe"] passthrough_env = ["RUSTFLAGS"] +bindir = "/opt/bin" +sbindir = "/opt/sbin" +libdir = "/opt/lib64" +sysconfdir = "/opt/etc" +datarootdir = "/opt/share-root" makeflags = "-j8" make_vars = ["V=1"] make_dirs = ["lib"] @@ -1867,6 +1992,14 @@ post_configure = ["echo configured"] "build.flags.configure_file".to_string(), vec![toml::Value::String("build-aux/configure".to_string())], ); + config.appends.insert( + "build.flags.libexecdir".to_string(), + vec![toml::Value::String("/opt/libexec".to_string())], + ); + config.appends.insert( + "build.flags.datadir".to_string(), + vec![toml::Value::String("/opt/share-data".to_string())], + ); config.appends.insert( "build.flags.config-setting".to_string(), vec![toml::Value::String( @@ -1933,6 +2066,13 @@ post_configure = ["echo configured"] .passthrough_env .contains(&"CARGO_HOME".to_string()) ); + assert_eq!(spec.build.flags.bindir, "/opt/bin"); + assert_eq!(spec.build.flags.sbindir, "/opt/sbin"); + assert_eq!(spec.build.flags.libdir, "/opt/lib64"); + assert_eq!(spec.build.flags.libexecdir, "/opt/libexec"); + assert_eq!(spec.build.flags.sysconfdir, "/opt/etc"); + assert_eq!(spec.build.flags.datarootdir, "/opt/share-root"); + assert_eq!(spec.build.flags.datadir, "/opt/share-data"); assert_eq!(spec.build.flags.makeflags, "-j8 --output-sync=target"); assert!(spec.build.flags.make_vars.contains(&"V=1".to_string())); assert!(spec.build.flags.make_dirs.contains(&"lib".to_string())); @@ -2276,6 +2416,61 @@ configure_file = "build-aux/configure" assert_eq!(spec.build.flags.configure_file, "build-aux/configure"); } + #[test] + fn parse_install_dirs_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 = "cmake" + +[build.flags] +bindir = "/custom/bin" +sbindir = "/custom/sbin" +libdir = "/custom/lib64" +libexecdir = "/custom/libexec" +sysconfdir = "/custom/etc" +localstatedir = "/custom/var" +sharedstatedir = "/custom/var/lib" +includedir = "/custom/include" +datarootdir = "/custom/share-root" +datadir = "/custom/share" +mandir = "/custom/share/man" +infodir = "/custom/share/info" +"#, + ) + .unwrap(); + + let spec = PackageSpec::from_file(&path).unwrap(); + assert_eq!(spec.build.flags.bindir, "/custom/bin"); + assert_eq!(spec.build.flags.sbindir, "/custom/sbin"); + assert_eq!(spec.build.flags.libdir, "/custom/lib64"); + assert_eq!(spec.build.flags.libexecdir, "/custom/libexec"); + assert_eq!(spec.build.flags.sysconfdir, "/custom/etc"); + assert_eq!(spec.build.flags.localstatedir, "/custom/var"); + assert_eq!(spec.build.flags.sharedstatedir, "/custom/var/lib"); + assert_eq!(spec.build.flags.includedir, "/custom/include"); + assert_eq!(spec.build.flags.datarootdir, "/custom/share-root"); + assert_eq!(spec.build.flags.datadir, "/custom/share"); + assert_eq!(spec.build.flags.mandir, "/custom/share/man"); + assert_eq!(spec.build.flags.infodir, "/custom/share/info"); + } + #[test] fn parse_lib32_build_flags_from_spec() { let tmp = tempfile::tempdir().unwrap(); @@ -3262,6 +3457,45 @@ pub struct BuildFlags { /// Binary installation directory relative to DESTDIR (default: /usr/bin) #[serde(default = "default_bindir")] pub bindir: String, + /// System binary installation directory for supported builders (default: /usr/bin). + #[serde(default)] + pub sbindir: String, + /// Library installation directory for supported builders. + /// + /// Defaults to `/usr/lib`, or `/usr/lib32` for the lib32 build variant. + #[serde(default)] + pub libdir: String, + /// Library helper executable installation directory for supported builders. + /// + /// Defaults to the effective `libdir`. + #[serde(default)] + pub libexecdir: String, + /// System configuration directory for supported builders (default: /etc). + #[serde(default)] + pub sysconfdir: String, + /// Variable state directory for supported builders (default: /var). + #[serde(default)] + pub localstatedir: String, + /// Shared variable state directory for supported builders (default: /var/lib). + #[serde(default)] + pub sharedstatedir: String, + /// Header installation directory for supported builders (default: /usr/include). + #[serde(default)] + pub includedir: String, + /// Data root installation directory for supported builders (default: /usr/share). + #[serde(default)] + pub datarootdir: String, + /// Architecture-independent data installation directory for supported builders. + /// + /// Defaults to the effective `datarootdir`. + #[serde(default)] + pub datadir: String, + /// Manual page installation directory for supported builders (default: /usr/share/man). + #[serde(default)] + pub mandir: String, + /// Info page installation directory for supported builders (default: /usr/share/info). + #[serde(default)] + pub infodir: String, /// Subdirectory within extracted source to use as the actual source root. /// Useful for monorepos like llvm-project where you want to build just one component. @@ -3336,6 +3570,17 @@ impl Default for BuildFlags { rustflags: Vec::new(), cargs: Vec::new(), bindir: default_bindir(), + sbindir: String::new(), + libdir: String::new(), + libexecdir: String::new(), + sysconfdir: String::new(), + localstatedir: String::new(), + sharedstatedir: String::new(), + includedir: String::new(), + datarootdir: String::new(), + datadir: String::new(), + mandir: String::new(), + infodir: String::new(), source_subdir: String::new(), build_dir: None, binary_type: String::new(),