Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally vendor GNUStep's libobjc2 #44

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 1 addition & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,6 @@ jobs:
command: check
args: --verbose --no-default-features

- name: Install GNUStep libobjc2
if: contains(matrix.platform.os, 'ubuntu')
run: |
wget https://github.com/gnustep/libobjc2/archive/refs/tags/v1.9.tar.gz
tar -xzf v1.9.tar.gz
mkdir libobjc2-1.9/build
cd libobjc2-1.9/build
export CC="clang"
export CXX="clang++"
cmake ../
sudo make install

- name: Install GNUStep make
if: contains(matrix.platform.os, 'ubuntu')
run: |
Expand Down Expand Up @@ -122,7 +110,7 @@ jobs:
with:
command: test
# Temporary fix
args: --verbose --no-fail-fast --no-default-features --package objc2_sys --package objc2 --package objc2_encode --package objc2_foundation
args: --verbose --no-fail-fast --no-default-features --features objc2_sys/vendor_gnustep --package objc2_sys --package objc2 --package objc2_encode --package objc2_foundation

- name: Test
if: ${{ !contains(matrix.platform.os, 'ubuntu') }}
Expand Down
7 changes: 7 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[submodule "libobjc2"]
path = gnustep_libobjc2_src/libobjc2
url = https://github.com/gnustep/libobjc2.git
branch = 2.1
[submodule "gnustep-base"]
path = gnustep_base_src/libs-base
url = https://github.com/gnustep/libs-base.git
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]
members = [
"gnustep_libobjc2_src",
"gnustep_base_src",
"objc2",
"objc2_block",
"objc2_encode",
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@

[![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt)
[![CI Status](https://github.com/madsmtm/objc2/workflows/CI/badge.svg)](https://github.com/madsmtm/objc2/actions)

## Submodules

This project uses `git` submodules for vendored dependencies, remember to update them after pulling:
```sh
$ git submodule update --init --recursive
```
15 changes: 15 additions & 0 deletions gnustep_base_src/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "gnustep_base_src"
version = "0.0.1"
authors = ["Mads Marquart <mads@marquart.dk>"]
edition = "2018"

description = "Source of The GNUstep Base Library and logic to build it"
keywords = ["gnustep", "objc", "gnustep-base", "build-dependencies"]
categories = [
"development-tools::build-utils",
]
readme = "README.md"
repository = "https://github.com/madsmtm/gnustep_base_src"
documentation = "https://docs.rs/gnustep_base_src/"
license = "MIT/Apache-2.0"
1 change: 1 addition & 0 deletions gnustep_base_src/libs-base
Submodule libs-base added at 326dc2
99 changes: 99 additions & 0 deletions gnustep_base_src/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

const NO_SOURCES_MSG: &'static str = r#"
GNUStep Base sources not present, please run:

$ git submodule update --init --recursive

If you did not checkout via git, you will
need to fetch the submodule's contents from
https://github.com/gnustep/libs-base
"#;

pub struct Builder {}

impl Builder {
pub fn new() -> Self {
Self {}
}

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++");
}
Comment on lines +23 to +30
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably also a bad idea, though not sure if there's a better alternative...


let source_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("libs-base");
if !source_dir.join("Headers/Foundation/Foundation.h").exists() {
panic!("{}", NO_SOURCES_MSG);
}

let mut configure = Command::new(source_dir.join("configure"));
// .arg("--with-config-file=")
// .arg("--enable-libffi")
// .arg("--with-default-config=")
// .arg("--prefix") // Or GNUSTEP_SYSTEM_ROOT ?

// run `make` and `make install`

let dst: PathBuf = todo!();

Artifacts {
source_dir,
include_dir: dst.join("include"),
lib_dir: dst.join("lib"),
lib_name: "gnustep-base",
}
}
}

pub struct Artifacts {
source_dir: PathBuf,
include_dir: PathBuf,
lib_dir: PathBuf,
lib_name: &'static str,
}

impl Artifacts {
pub fn include_dir(&self) -> &Path {
&self.include_dir
}

pub fn lib_dir(&self) -> &Path {
&self.lib_dir
}

pub fn lib_name(&self) -> &str {
&self.lib_name
}

pub fn print_cargo_metadata(&self) {
println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
println!("cargo:rustc-link-lib=dylib={}", self.lib_name);
println!("cargo:include={}", self.include_dir.display());
println!("cargo:lib={}", self.lib_dir.display());
}

pub fn print_cargo_rerun_if_changed(&self) {
println!("cargo:rerun-if-env-changed=CC");
println!("cargo:rerun-if-env-changed=CXX");
rerun_if(&self.source_dir);
}
}

// https://github.com/rust-lang/git2-rs/blob/0.13.23/libgit2-sys/build.rs#L228-L236
fn rerun_if(path: &Path) {
if path.is_dir() {
for entry in std::fs::read_dir(path).expect("read_dir") {
rerun_if(&entry.expect("entry").path());
}
} else {
println!("cargo:rerun-if-changed={}", path.display());
}
}
24 changes: 24 additions & 0 deletions gnustep_libobjc2_src/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "gnustep_libobjc2_src"
version = "0.0.1"
authors = ["Mads Marquart <mads@marquart.dk>"]
edition = "2018"

description = "Source of GNUStep's libobjc2 and logic to build it"
keywords = ["gnustep", "objc", "libobjc2", "build-dependencies"]
categories = [
"development-tools::build-utils",
]
readme = "README.md"
repository = "https://github.com/madsmtm/gnustep_libobjc2_src"
documentation = "https://docs.rs/gnustep_libobjc2_src/"
license = "MIT/Apache-2.0"

exclude = [
"libobjc2/ABIDoc/*",
"libobjc2/Test/*",
"libobjc2/third_party/robin-map/tests/*",
]

[dependencies]
cmake = "0.1.45"
9 changes: 9 additions & 0 deletions gnustep_libobjc2_src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# `gnustep_libobjc2_src`

Source of GNUStep's libobjc2 and logic to build it for Rust.

You probably want to use `objc2_sys`, but this is kept separate from that so
that users don't download the entire GNUStep source when they don't need to.

Using `clang` with at least version `8.0.0` is recommended. You can specify
your desired compiler using the `CC` and `CXX` environment variables.
1 change: 1 addition & 0 deletions gnustep_libobjc2_src/libobjc2
Submodule libobjc2 added at f64890
178 changes: 178 additions & 0 deletions gnustep_libobjc2_src/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//! TODO
//!
//! See
//! - https://github.com/gnustep/libs-base/blob/base-1_28_0/INSTALL
//! - https://github.com/blas-lapack-rs/blis-src/blob/main/blis-src/build.rs
//! - https://github.com/alexcrichton/openssl-src-rs/blob/master/src/lib.rs
//! - https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.70/autoconf.html#Running-configure-Scripts
//!
//! And gnustep-make would also have to be installed:
//! - https://github.com/gnustep/tools-make/blob/master/Documentation/gnustep-make.texi
//! - http://www.gnustep.org/resources/documentation/Developer/Make/Manual/DESIGN

use std::env;
use std::path::{Path, PathBuf};

const NO_SOURCES_MSG: &'static str = r#"
libobjc2 sources not present, please run:

$ git submodule update --init --recursive

If you did not checkout via git, you will
need to fetch the submodule's contents from
https://github.com/gnustep/libobjc2
"#;

pub struct Builder {
lib_kind: LibKind,
objcxx: bool,
}

impl Builder {
pub fn new() -> Self {
Self {
lib_kind: LibKind::Dynamic,
objcxx: true,
}
}

/// 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
}

/// 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
}

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 {
source_dir,
include_dir: dst.join("include"),
lib_dir: dst.join("lib"),
lib_kind: self.lib_kind,
lib_name: "objc",
}
}
}

pub struct Artifacts {
source_dir: PathBuf,
include_dir: PathBuf,
lib_dir: PathBuf,
lib_kind: LibKind,
lib_name: &'static str,
}

impl Artifacts {
pub fn source_dir(&self) -> &Path {
&self.source_dir
}

pub fn include_dir(&self) -> &Path {
&self.include_dir
}

pub fn lib_dir(&self) -> &Path {
&self.lib_dir
}

pub fn lib_kind(&self) -> LibKind {
self.lib_kind
}

pub fn lib_name(&self) -> &str {
&self.lib_name
}

pub fn print_cargo_metadata(&self) {
let kind = match self.lib_kind {
LibKind::Dynamic => "dylib",
LibKind::Static => "static",
};
println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
println!("cargo:rustc-link-lib={}={}", kind, self.lib_name);
println!("cargo:include={}", self.include_dir.display());
println!("cargo:lib={}", self.lib_dir.display());
}

pub fn print_cargo_rerun_if_changed(&self) {
println!("cargo:rerun-if-env-changed=CC");
println!("cargo:rerun-if-env-changed=CXX");
rerun_if(&self.source_dir);
}
}

fn rerun_if(path: &Path) {
if path.is_dir() {
for entry in std::fs::read_dir(path).expect("read_dir") {
rerun_if(&entry.expect("entry").path());
}
} else {
println!("cargo:rerun-if-changed={}", path.display());
}
}

#[non_exhaustive]
#[derive(Clone, Copy)]
pub enum LibKind {
Dynamic,
Static,
// Framework,
}