feat: add support for customizable installation directories in build flags
This commit is contained in:
+13
-18
@@ -442,25 +442,20 @@ fn default_configure_install_dirs(
|
||||
flags: &crate::package::BuildFlags,
|
||||
help_text: Option<&str>,
|
||||
) -> Vec<String> {
|
||||
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
|
||||
|
||||
+134
-8
@@ -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<String> {
|
||||
let dirs = crate::builder::install_dirs(flags);
|
||||
let defaults = [
|
||||
("CMAKE_INSTALL_BINDIR", dirs.bindir),
|
||||
("CMAKE_INSTALL_SBINDIR", dirs.sbindir),
|
||||
("CMAKE_INSTALL_LIBDIR", dirs.libdir),
|
||||
("CMAKE_INSTALL_LIBEXECDIR", dirs.libexecdir),
|
||||
("CMAKE_INSTALL_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();
|
||||
|
||||
@@ -162,10 +162,29 @@ fn meson_setup_args(
|
||||
env_vars: &[(String, String)],
|
||||
) -> Vec<String> {
|
||||
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 {
|
||||
|
||||
@@ -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<String>) {
|
||||
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<String>)
|
||||
}
|
||||
}
|
||||
|
||||
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<String>, Vec<String>, Vec<String>, Vec<String>) {
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user