Skip to content

Commit

Permalink
Merge pull request #537 from filmor/nif-version-features
Browse files Browse the repository at this point in the history
Use features for minimal NIF version
  • Loading branch information
filmor authored May 25, 2023
2 parents 20f5259 + 19ca337 commit 4ea76ec
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 178 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ See [`UPGRADE.md`](./UPGRADE.md) for additional help when upgrading to newer ver

## [unreleased]

### Changed

* Use Cargo features to define the NIF version level (#537), deprecating
`RUSTLER_NIF_VERSION`

## [0.28.0] - 2023-04-24

### Added
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,19 @@ Rustler aims to support the newest three major OTP versions as well as newest th

#### Supported NIF version

Rustler uses `erlang:system_info(nif_version)` to detect the supported NIF version of the Erlang/OTP
system for which the NIF is to be compiled. It is possible to restrict the NIF version to an older
version if the NIF is to be compiled for an older version of Erlang. For example, if the target NIF
version should be `2.14` (Erlang/OTP 21), this can be defined using an environment variable:

```
RUSTLER_NIF_VERSION=2.14 mix compile
The minimal supported NIF version for a library should be defined via Cargo
features. The default is currently `2.14` (Erlang/OTP 21). To use features from
NIF version `2.16` (Erlang/OTP 24), the respective feature flag has to be
enabled on the dependency:

```toml
[dependencies]
rustler = { version = "...", features = ["nif_version_2_16"] }
```

For compatibility reasons, this can be defined (and overridden) by setting the
`RUSTLER_NIF_VERSION` environment variable during build.

#### Community

You can find us in the `#rustler:matrix.org` channel on [Matrix](https://matrix.to/#/#rustler:matrix.org)
Expand Down
19 changes: 19 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

This document is intended to simplify upgrading to newer versions by extending the changelog.

## 0.28 -> 0.29

`RUSTLER_NIF_VERSION` is deprecated and will not be considered anymore for 0.30.
The NIF version will also not be guessed anymore from a potentially available
installed Erlang version. By default, NIF libraries will now be compiled against
NIF version 2.14 which is compatible down to OTP21. The default will be adjusted
along with the supported OTP versions.

If additional features are required that use newer NIF versions, these can be
included explicitly in the project's `Cargo.toml` as, e.g.

```toml
[dependencies]
rustler = { version = "0.30", features = ["nif_version_2_17"] }
```

With this configuration, the resulting NIF library will only work from OTP26
onwards, but will also have access to the largest feature set.

## 0.26 -> 0.27

`MIX_ENV` is no longer considered for determining the build profile. Now, the
Expand Down
55 changes: 55 additions & 0 deletions build_common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
mod common {
use std::env;

pub const MIN_SUPPORTED_VERSION: (u32, u32) = (2, 14);
pub const MAX_SUPPORTED_VERSION: (u32, u32) = (2, 17);

fn get_nif_version_from_env(version: &str) -> (u32, u32) {
let parts: Vec<Result<_, _>> = version
.split('.')
.take(2)
.map(|n| n.parse::<u32>())
.collect();

let mut nif_version = match &parts[..] {
[Ok(major), Ok(minor)] => (*major, *minor),
_other => panic!("The RUSTLER_NIF_VERSION is not a valid version"),
};

if nif_version < MIN_SUPPORTED_VERSION {
panic!(
"The NIF version given from RUSTLER_NIF_VERSION is not supported: {}.{}",
nif_version.0, nif_version.1
);
}

// TODO: This code will need adjustment if the Erlang developers ever decide to introduce
// a new major NIF version.
if nif_version > MAX_SUPPORTED_VERSION {
eprintln!(
"NIF version {}.{} is not yet supported, falling back to {}.{}",
nif_version.0, nif_version.1, MAX_SUPPORTED_VERSION.0, MAX_SUPPORTED_VERSION.1
);
nif_version = MAX_SUPPORTED_VERSION;
}

nif_version
}

pub fn handle_nif_version_from_env() -> Option<(u32, u32)> {
println!("cargo:rerun-if-env-changed=RUSTLER_NIF_VERSION");
env::var("RUSTLER_NIF_VERSION").ok().map(|val| {
let nif_version = get_nif_version_from_env(&val);

// Activate all config flags for the supported NIF versions
for minor in 0..=nif_version.1 {
println!(
"cargo:rustc-cfg=feature=\"nif_version_{}_{}\"",
nif_version.0, minor
);
}

nif_version
})
}
}
6 changes: 5 additions & 1 deletion rustler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ readme = "../README.md"
edition = "2018"

[features]
default = ["derive"]
default = ["derive", "nif_version_2_14"]
derive = ["rustler_codegen"]
alternative_nif_init_name = []
nif_version_2_14 = ["rustler_sys/nif_version_2_14"]
nif_version_2_15 = ["nif_version_2_14", "rustler_sys/nif_version_2_15"]
nif_version_2_16 = ["nif_version_2_15", "rustler_sys/nif_version_2_16"]
nif_version_2_17 = ["nif_version_2_16", "rustler_sys/nif_version_2_17"]

[dependencies]
lazy_static = "1.4"
Expand Down
68 changes: 2 additions & 66 deletions rustler/build.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,8 @@
/// Detect NIF version to build against
/// It reads from the RUSTLER_NIF_VERSION env var.
///
/// If this env var is not present we try to read from the installed Erlang.
/// If the environment doesn't have Erlang installed, then we use the latest
/// NIF version and write a warning to stderr.
use std::env;
use std::process::Command;

// keep this sorted by version number
const NIF_VERSION: &[&str] = &["2.14", "2.15", "2.16", "2.17"];
include!("build_common.rs");

fn main() {
let latest_version = NIF_VERSION.last().unwrap().to_string();
let version = env::var("RUSTLER_NIF_VERSION").unwrap_or_else(|_| {
match get_version_from_erl() {
Some(nif_version) if NIF_VERSION.contains(&nif_version.as_str()) => {
eprintln!("RUSTLER_NIF_VERSION env var is not set. Using version from Erlang: {}", nif_version);
nif_version
},
Some(ref nif_version) => panic!("The NIF version from Erlang is not supported by Rustler: {}", nif_version),
None => {
eprintln!("RUSTLER_NIF_VERSION env var is not set and `erl` command is not found. Using version {}", latest_version);
latest_version
},
}
});

activate_versions(&version);
common::handle_nif_version_from_env();

// The following lines are important to tell Cargo to recompile if something changes.
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed=RUSTLER_NIF_VERSION");
}

fn get_version_from_erl() -> Option<String> {
let args = vec![
"-noshell",
"-eval",
r#"io:format("~s~n", [erlang:system_info(nif_version)]), init:stop()."#,
];

let version = Command::new("erl").args(args).output().ok()?.stdout;

let version = String::from_utf8(version).ok()?;

Some(version.trim().into())
}

fn activate_versions(version: &str) {
let index = NIF_VERSION
.iter()
.position(|&v| v == version)
.unwrap_or_else(|| {
panic!(
"Erlang version {} not handled, please file a a bug report.",
version
)
});

#[allow(clippy::needless_range_loop)]
for i in 0..=index {
println!(
"cargo:rustc-cfg=nif_version_{}",
version_feature(NIF_VERSION[i])
);
}
}

fn version_feature(version: &str) -> String {
version.replace('.', "_")
}
1 change: 1 addition & 0 deletions rustler/build_common.rs
10 changes: 1 addition & 9 deletions rustler_mix/lib/rustler/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ defmodule Rustler.Compiler do
System.cmd(cmd, args,
cd: crate_full_path,
stderr_to_stdout: true,
env: [
{"CARGO_TARGET_DIR", config.target_dir},
{"RUSTLER_NIF_VERSION", nif_version()}
| config.env
],
env: [{"CARGO_TARGET_DIR", config.target_dir} | config.env],
into: IO.stream(:stdio, :line)
)

Expand All @@ -50,10 +46,6 @@ defmodule Rustler.Compiler do
config
end

defp nif_version do
System.get_env("RUSTLER_NIF_VERSION") || to_string(:erlang.system_info(:nif_version))
end

defp make_base_command(:system), do: ["cargo", "rustc"]
defp make_base_command({:system, channel}), do: ["cargo", channel, "rustc"]
defp make_base_command({:bin, path}), do: [path, "rustc"]
Expand Down
8 changes: 8 additions & 0 deletions rustler_sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ build = "build.rs"

categories = ["external-ffi-bindings"]

[features]
# Default version: 2.14
default = ["nif_version_2_14"]
nif_version_2_14 = []
nif_version_2_15 = ["nif_version_2_14"]
nif_version_2_16 = ["nif_version_2_15"]
nif_version_2_17 = ["nif_version_2_16"]

[dependencies]
unreachable = "1.0"

Expand Down
Loading

0 comments on commit 4ea76ec

Please sign in to comment.