diff --git a/libobjc2_src/src/lib.rs b/libobjc2_src/src/lib.rs index b0b5a7fcf..69dc5965e 100644 --- a/libobjc2_src/src/lib.rs +++ b/libobjc2_src/src/lib.rs @@ -11,62 +11,101 @@ need to fetch the submodule's contents from https://github.com/gnustep/libobjc2 "#; -#[non_exhaustive] -pub enum LibKind { - Dynamic, - Static, - // Framework, -} - -pub struct Artifacts { - include_dir: PathBuf, - lib_dir: PathBuf, +pub struct Builder { lib_kind: LibKind, - lib_name: &'static str, + objcxx: bool, } -pub fn build() -> Artifacts { - // GNUStep only compiles with clang, so try that first. - // (But let the user specify a different path if they need to). - if env::var_os("CC").is_none() { - env::set_var("CC", "clang"); +impl Builder { + pub fn new() -> Self { + Self { + lib_kind: LibKind::Dynamic, + objcxx: true, + } } - if env::var_os("CXX").is_none() { - env::set_var("CXX", "clang++"); + + /// Set the type of library to be built, and how linking is performed. + /// + /// Possible options are [`LibKind::Static`] and [`LibKind::Dynamic`]. + /// + /// Defaults to [`LibKind::Dynamic`]. + pub fn lib_kind(&mut self, kind: LibKind) -> &mut Self { + self.lib_kind = kind; + self } - let source_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("libobjc2"); - if !source_dir.join("objc/objc.h").exists() { - panic!("{}", NO_SOURCES_MSG); + /// Enable support for Objective-C++. + /// + /// Namely interoperability between Objective-C and C++ exceptions. + /// + /// Defaults to [`true`]. + pub fn objcxx(&mut self, objcxx: bool) -> &mut Self { + self.objcxx = objcxx; + self } - let dst = cmake::Config::new(source_dir) - // Default to ignoring `gnustep-config` presence, since they usually - // want to install the libraries globally (which requires root). - // Users that want systemwide installation should just install it - // themselves, and shouldn't need to vendor GNUStep. - .define("GNUSTEP_INSTALL_TYPE", "NONE") - .define("BUILD_STATIC_LIBOBJC", "OFF") // Default - .define("DEBUG_ARC_COMPAT", "OFF") // Default - .define("ENABLE_OBJCXX", "ON") // Default (NO_OBJCXX in code) - .define("ENABLE_TRACING", "OFF") // Default (WITH_TRACING in code) - .define("LEGACY_COMPAT", "OFF") // Default (NO_LEGACY in code) - // .define("OLDABI_COMPAT", "?") // Default depends on WIN32 - .define("TESTS", "OFF") - .define("TYPE_DEPENDENT_DISPATCH", "ON") // Default - // .always_configure(false) // TODO - // .static_crt(?) - .build_target("install") - .build(); - - Artifacts { - include_dir: dst.join("include"), - lib_dir: dst.join("lib"), - lib_kind: LibKind::Dynamic, - lib_name: "objc", + pub fn build(&mut self) -> Artifacts { + // GNUStep only compiles with clang, so try that first. + // (But let the user specify a different path if they need to). + if env::var_os("CC").is_none() { + env::set_var("CC", "clang"); + } + if env::var_os("CXX").is_none() { + env::set_var("CXX", "clang++"); + } + + let source_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("libobjc2"); + if !source_dir.join("objc/objc.h").exists() { + panic!("{}", NO_SOURCES_MSG); + } + + let dst = cmake::Config::new(source_dir) + // Default to ignoring `gnustep-config` presence, since they + // usually want to install the libraries globally (which requires + // root). Users that want systemwide installation should just + // install it themselves, and shouldn't need to vendor GNUStep. + .define("GNUSTEP_INSTALL_TYPE", "NONE") + // Don't bother building tests, we're not gonna run them anyways + // (and they're not available when packaged, see Cargo.toml). + .define("TESTS", "OFF") + // Having this on also builds the dynamic library, but not really + // anything we can do to change that. + .define( + "BUILD_STATIC_LIBOBJC", + match self.lib_kind { + LibKind::Static => "ON", + LibKind::Dynamic => "OFF", + }, + ) + .define("ENABLE_OBJCXX", if self.objcxx { "ON" } else { "OFF" }) + // Various other defaults + // .define("OLDABI_COMPAT", "ON") + // .define("DEBUG_ARC_COMPAT", "OFF") + // .define("ENABLE_TRACING", "OFF") + // .define("LEGACY_COMPAT", "OFF") + // .define("LIBOBJC_NAME", "objc") + // .define("TYPE_DEPENDENT_DISPATCH", "ON") + // .define("STRICT_APPLE_COMPATIBILITY", "0") // Default none + // TODO: .static_crt(?) + .build_target("install") + .build(); + + Artifacts { + include_dir: dst.join("include"), + lib_dir: dst.join("lib"), + lib_kind: self.lib_kind, + lib_name: "objc", + } } } +pub struct Artifacts { + include_dir: PathBuf, + lib_dir: PathBuf, + lib_kind: LibKind, + lib_name: &'static str, +} + impl Artifacts { pub fn include_dir(&self) -> &Path { &self.include_dir @@ -76,8 +115,8 @@ impl Artifacts { &self.lib_dir } - pub fn lib_kind(&self) -> &LibKind { - &self.lib_kind + pub fn lib_kind(&self) -> LibKind { + self.lib_kind } pub fn lib_name(&self) -> &str { @@ -86,7 +125,7 @@ impl Artifacts { pub fn print_cargo_metadata(&self) { let kind = match self.lib_kind { - LibKind::Dynamic => "dynamic", + LibKind::Dynamic => "dylib", LibKind::Static => "static", }; println!("cargo:rustc-link-search=native={}", self.lib_dir.display()); @@ -95,3 +134,11 @@ impl Artifacts { println!("cargo:lib={}", self.lib_dir.display()); } } + +#[non_exhaustive] +#[derive(Clone, Copy)] +pub enum LibKind { + Dynamic, + Static, + // Framework, +} diff --git a/objc2_sys/Cargo.toml b/objc2_sys/Cargo.toml index 289c773a5..be15e3850 100644 --- a/objc2_sys/Cargo.toml +++ b/objc2_sys/Cargo.toml @@ -29,8 +29,13 @@ links = "objc" build = "build.rs" [features] -# If enabled, GNUStep's libobjc2 will be compiled and linked from source +# If enabled, GNUStep's libobjc2 will be compiled and linked from source. vendor_gnustep = ["libobjc2_src"] +# Whether to link to libobjc statically. +# +# Only supported for vendored dependencies, it will trigger a compile-time +# error when venoring is not enabled. +static = [] [build-dependencies] libobjc2_src = { path = "../libobjc2_src", optional = true } diff --git a/objc2_sys/build.rs b/objc2_sys/build.rs index 211d33747..3d9b13801 100644 --- a/objc2_sys/build.rs +++ b/objc2_sys/build.rs @@ -1,12 +1,24 @@ #[cfg(feature = "vendor_gnustep")] fn main() { - let artifacts = libobjc2_src::build(); + let mut builder = libobjc2_src::Builder::new(); + + #[cfg(feature = "static")] + builder.lib_kind(libobjc2_src::LibKind::Static); + #[cfg(not(feature = "static"))] + builder.lib_kind(libobjc2_src::LibKind::Dynamic); + + let artifacts = builder.build(); artifacts.print_cargo_metadata(); + + // Add #[cfg(gnustep)] directive println!("cargo:rustc-cfg=gnustep"); } #[cfg(not(feature = "vendor_gnustep"))] fn main() { + #[cfg(feature = "static")] + compile_error!("Can only link statically to libobjc when vendoring is enabled."); + // Only rerun if this file changes; the script doesn't depend on our code println!("cargo:rerun-if-changed=build.rs");