Skip to content

Commit

Permalink
Add rb-sys-env to ease integration pains (#99)
Browse files Browse the repository at this point in the history
* Add `rb-sys-env` to ease integration pains

* Fix readme

* Make RB_SYS_ENV_DEBUG cause rebuild

* Make it work on 1.51

* Fix abi version test

* Inline module docs

* Include CPPFLAGS and CFLAGS when generating bindings

* Change check for windows when linking
  • Loading branch information
ianks authored Nov 12, 2022
1 parent ea0f7ca commit 61c5e86
Show file tree
Hide file tree
Showing 20 changed files with 704 additions and 49 deletions.
6 changes: 5 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ members = [
"crates/rb-sys",
"crates/rb-sys-tests",
"crates/rb-sys-build",
"crates/rb-sys-env",
]
exclude = ["examples/rust_reverse/ext/rust_reverse"]
exclude = ["examples/rust_reverse/ext/rust_reverse"]
7 changes: 5 additions & 2 deletions crates/rb-sys-build/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ use std::path::PathBuf;

/// Generate bindings for the Ruby using bindgen.
pub fn generate(rbconfig: &RbConfig, static_ruby: bool) {
let clang_args = vec![
let mut clang_args = vec![
format!("-I{}", rbconfig.get("rubyhdrdir")),
format!("-I{}", rbconfig.get("rubyarchhdrdir")),
"-fms-extensions".to_string(),
];

clang_args.extend(rbconfig.cflags.clone());
clang_args.extend(rbconfig.cppflags());

eprintln!("Using bindgen with clang args: {:?}", clang_args);

let mut src_wrapper_h = File::open("wrapper.h").unwrap();
Expand Down Expand Up @@ -161,7 +164,7 @@ fn push_cargo_cfg_from_bindings() -> Result<(), Box<dyn std::error::Error>> {
let name = val.name().to_lowercase();
let val = val.as_bool();
println!("cargo:rustc-cfg=ruby_{}=\"{}\"", name, val);
println!("cargo:defines_{}=\"{}\"", name, val);
println!("cargo:defines_{}={}", name, val);
}
}

Expand Down
23 changes: 20 additions & 3 deletions crates/rb-sys-build/src/rb_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use library::*;
use search_path::*;
use std::ffi::OsString;

use crate::utils::{is_msvc, shellsplit};
use crate::utils::{is_msvc, is_mswin_or_mingw, shellsplit};

use self::flags::Flags;

Expand Down Expand Up @@ -155,6 +155,16 @@ impl RbConfig {
self.get("ruby_version")
}

/// Get the CPPFLAGS from the RbConfig, making sure to subsitute variables.
pub fn cppflags(&self) -> Vec<String> {
if let Some(cppflags) = self.get_optional("CPPFLAGS") {
let flags = self.subst_shell_variables(&cppflags);
shellsplit(&flags)
} else {
vec![]
}
}

/// Returns the value of the given key from the either the matching
/// `RBCONFIG_{key}` environment variable or `RbConfig::CONFIG[{key}]` hash.
pub fn get(&self, key: &str) -> String {
Expand Down Expand Up @@ -237,9 +247,16 @@ impl RbConfig {

/// Print to rb_config output for cargo
pub fn print_cargo_args(&self) {
for arg in self.cargo_args() {
let cargo_args = self.cargo_args();

for arg in &cargo_args {
println!("{}", arg);
}

let encoded_cargo_args = cargo_args.join("\x1E");
let encoded_cargo_args = encoded_cargo_args.replace('\n', "\x1F");

println!("cargo:encoded_cargo_args={}", encoded_cargo_args);
}

/// Adds items to the rb_config based on a string from LDFLAGS/DLDFLAGS
Expand Down Expand Up @@ -378,7 +395,7 @@ fn capture_name(regex: &Regex, arg: &str) -> Option<String> {
// Needed because Rust 1.51 does not support link-arg, and thus rpath
// See <https://doc.rust-lang.org/cargo/reference/environment-variables.html#dynamic-library-paths
fn append_ld_library_path(search_paths: Vec<&str>) {
let env_var_name = if cfg!(windows) {
let env_var_name = if is_mswin_or_mingw() {
"PATH"
} else if cfg!(target_os = "macos") {
"DYLD_FALLBACK_LIBRARY_PATH"
Expand Down
9 changes: 9 additions & 0 deletions crates/rb-sys-build/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ pub fn is_msvc() -> bool {
}
}

/// Check if current platform is mswin or mingw.
pub fn is_mswin_or_mingw() -> bool {
if let Ok(target) = std::env::var("TARGET") {
target.contains("msvc") || target.contains("pc-windows-gnu")
} else {
false
}
}

/// Splits shell words.
pub fn shellsplit(s: &str) -> Vec<String> {
match shell_words::split(s) {
Expand Down
13 changes: 13 additions & 0 deletions crates/rb-sys-env/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "rb-sys-env"
version = "0.1.0"
edition = "2018"
description = "Integrates rb-sys into high level frameworks"
homepage = "https://github.com/oxidize-rb/rb-sys"
license = "MIT OR Apache-2.0"
repository = "https://github.com/oxidize-rb/rb-sys"
readme = "readme.md"

[lib]
bench = false
doctest = false
119 changes: 119 additions & 0 deletions crates/rb-sys-env/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# `rb-sys-env`

Helpers to integrate `rb-sys` into your high-level Ruby bindings library.

## Features

- Provides the neccesary Cargo configuration to ensure that Rust crates compile properly across all platforms
- Sets useful rustc-cfg flags that you can use from your crate
- Exposes all `RbConfig::CONFIG` values from rb-sys

## Usage

Add this to your `Cargo.toml`:

```toml
[build-dependencies]
rb-sys-env = "0.1"
```

Then, in your crate's `build.rs`:

```rust
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let _rb_env = rb_sys_env::activate()?;

Ok(())
}
```

## Available `rustc-cfg`

Here is an example of the `rustc-cfg` flags that are set by this crate:

- `#[cfg(ruby_have_ruby_re_h)]`
- `#[cfg(ruby_use_rgengc)]`
- `#[cfg(ruby_use_symbol_as_method_name)]`
- `#[cfg(ruby_have_ruby_util_h)]`
- `#[cfg(ruby_have_ruby_oniguruma_h)]`
- `#[cfg(ruby_have_ruby_defines_h)]`
- `#[cfg(ruby_use_flonum)]`
- `#[cfg(ruby_have_ruby_onigmo_h)]`
- `#[cfg(ruby_use_unaligned_member_access)]`
- `#[cfg(ruby_use_transient_heap)]`
- `#[cfg(ruby_have_ruby_atomic_h)]`
- `#[cfg(ruby_have_rb_scan_args_optional_hash)]`
- `#[cfg(ruby_have_rb_data_type_t_parent)]`
- `#[cfg(ruby_have_ruby_debug_h)]`
- `#[cfg(ruby_have_ruby_encoding_h)]`
- `#[cfg(ruby_have_ruby_ruby_h)]`
- `#[cfg(ruby_have_ruby_intern_h)]`
- `#[cfg(ruby_use_mjit)]`
- `#[cfg(ruby_have_rb_data_type_t_function)]`
- `#[cfg(ruby_have_rb_fd_init)]`
- `#[cfg(ruby_have_rb_reg_new_str)]`
- `#[cfg(ruby_have_rb_io_t)]`
- `#[cfg(ruby_have_ruby_memory_view_h)]`
- `#[cfg(ruby_have_ruby_version_h)]`
- `#[cfg(ruby_have_ruby_st_h)]`
- `#[cfg(ruby_have_ruby_thread_native_h)]`
- `#[cfg(ruby_have_ruby_random_h)]`
- `#[cfg(ruby_have_ruby_regex_h)]`
- `#[cfg(ruby_have_rb_define_alloc_func)]`
- `#[cfg(ruby_have_ruby_fiber_scheduler_h)]`
- `#[cfg(ruby_have_ruby_missing_h)]`
- `#[cfg(ruby_have_rb_ext_ractor_safe)]`
- `#[cfg(ruby_have_ruby_thread_h)]`
- `#[cfg(ruby_have_ruby_vm_h)]`
- `#[cfg(ruby_use_rincgc)]`
- `#[cfg(ruby_have_ruby_ractor_h)]`
- `#[cfg(ruby_have_ruby_io_h)]`
- `#[cfg(ruby_3)]`
- `#[cfg(ruby_3_1)]`
- `#[cfg(ruby_3_1_2)]`
- `#[cfg(ruby_gte_2_2)]`
- `#[cfg(ruby_gt_2_2)]`
- `#[cfg(ruby_gte_2_3)]`
- `#[cfg(ruby_gt_2_3)]`
- `#[cfg(ruby_gte_2_4)]`
- `#[cfg(ruby_gt_2_4)]`
- `#[cfg(ruby_gte_2_5)]`
- `#[cfg(ruby_gt_2_5)]`
- `#[cfg(ruby_gte_2_6)]`
- `#[cfg(ruby_gt_2_6)]`
- `#[cfg(ruby_gte_2_7)]`
- `#[cfg(ruby_gt_2_7)]`
- `#[cfg(ruby_gte_3_0)]`
- `#[cfg(ruby_gt_3_0)]`
- `#[cfg(ruby_lte_3_1)]`
- `#[cfg(ruby_3_1)]`
- `#[cfg(ruby_eq_3_1)]`
- `#[cfg(ruby_gte_3_1)]`
- `#[cfg(ruby_lt_3_2)]`
- `#[cfg(ruby_lte_3_2)]`
- `#[cfg(ruby_lt_3_3)]`
- `#[cfg(ruby_lte_3_3)]`
- `#[cfg(ruby_gte_1)]`
- `#[cfg(ruby_gt_1)]`
- `#[cfg(ruby_gte_2)]`
- `#[cfg(ruby_gt_2)]`
- `#[cfg(ruby_lte_3)]`
- `#[cfg(ruby_3)]`
- `#[cfg(ruby_eq_3)]`
- `#[cfg(ruby_gte_3)]`
- `#[cfg(ruby_lt_4)]`
- `#[cfg(ruby_lte_4)]`

## License

Licensed under either of

- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as
defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
37 changes: 37 additions & 0 deletions crates/rb-sys-env/src/defines.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::{collections::HashMap, rc::Rc};

/// The DEFINES variables from libruby.
#[derive(Debug, Clone)]
pub struct Defines {
raw_environment: Rc<HashMap<String, String>>,
}

impl Defines {
pub(crate) fn from_raw_environment(raw_environment: Rc<HashMap<String, String>>) -> Self {
Self { raw_environment }
}

/// Determines the given key is true.
pub fn is_value_true(&self, key: &str) -> bool {
self.raw_environment
.get(format!("DEFINES_{}", key).as_str())
.map(|v| v == "1" || v == "true")
.unwrap_or(false)
}

/// Fetches the raw value for the given key.
pub fn get_raw_value(&self, key: &str) -> Option<&str> {
self.raw_environment
.get(format!("DEFINES_{}", key).as_str())
.map(|v| v.as_str())
}

pub(crate) fn print_cargo_rustc_cfg(&self) {
for (key, val) in self.raw_environment.iter() {
if key.starts_with("DEFINES_") && val == "true" {
let key = key.trim_start_matches("DEFINES_");
rustc_cfg!("ruby_{}", key.to_lowercase());
}
}
}
}
Loading

0 comments on commit 61c5e86

Please sign in to comment.