Skip to content

Commit 4ea76ec

Browse files
authored
Merge pull request #537 from filmor/nif-version-features
Use features for minimal NIF version
2 parents 20f5259 + 19ca337 commit 4ea76ec

File tree

12 files changed

+140
-178
lines changed

12 files changed

+140
-178
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ See [`UPGRADE.md`](./UPGRADE.md) for additional help when upgrading to newer ver
99

1010
## [unreleased]
1111

12+
### Changed
13+
14+
* Use Cargo features to define the NIF version level (#537), deprecating
15+
`RUSTLER_NIF_VERSION`
16+
1217
## [0.28.0] - 2023-04-24
1318

1419
### Added

README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,19 @@ Rustler aims to support the newest three major OTP versions as well as newest th
5757

5858
#### Supported NIF version
5959

60-
Rustler uses `erlang:system_info(nif_version)` to detect the supported NIF version of the Erlang/OTP
61-
system for which the NIF is to be compiled. It is possible to restrict the NIF version to an older
62-
version if the NIF is to be compiled for an older version of Erlang. For example, if the target NIF
63-
version should be `2.14` (Erlang/OTP 21), this can be defined using an environment variable:
64-
65-
```
66-
RUSTLER_NIF_VERSION=2.14 mix compile
60+
The minimal supported NIF version for a library should be defined via Cargo
61+
features. The default is currently `2.14` (Erlang/OTP 21). To use features from
62+
NIF version `2.16` (Erlang/OTP 24), the respective feature flag has to be
63+
enabled on the dependency:
64+
65+
```toml
66+
[dependencies]
67+
rustler = { version = "...", features = ["nif_version_2_16"] }
6768
```
6869

70+
For compatibility reasons, this can be defined (and overridden) by setting the
71+
`RUSTLER_NIF_VERSION` environment variable during build.
72+
6973
#### Community
7074

7175
You can find us in the `#rustler:matrix.org` channel on [Matrix](https://matrix.to/#/#rustler:matrix.org)

UPGRADE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22

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

5+
## 0.28 -> 0.29
6+
7+
`RUSTLER_NIF_VERSION` is deprecated and will not be considered anymore for 0.30.
8+
The NIF version will also not be guessed anymore from a potentially available
9+
installed Erlang version. By default, NIF libraries will now be compiled against
10+
NIF version 2.14 which is compatible down to OTP21. The default will be adjusted
11+
along with the supported OTP versions.
12+
13+
If additional features are required that use newer NIF versions, these can be
14+
included explicitly in the project's `Cargo.toml` as, e.g.
15+
16+
```toml
17+
[dependencies]
18+
rustler = { version = "0.30", features = ["nif_version_2_17"] }
19+
```
20+
21+
With this configuration, the resulting NIF library will only work from OTP26
22+
onwards, but will also have access to the largest feature set.
23+
524
## 0.26 -> 0.27
625

726
`MIX_ENV` is no longer considered for determining the build profile. Now, the

build_common.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
mod common {
2+
use std::env;
3+
4+
pub const MIN_SUPPORTED_VERSION: (u32, u32) = (2, 14);
5+
pub const MAX_SUPPORTED_VERSION: (u32, u32) = (2, 17);
6+
7+
fn get_nif_version_from_env(version: &str) -> (u32, u32) {
8+
let parts: Vec<Result<_, _>> = version
9+
.split('.')
10+
.take(2)
11+
.map(|n| n.parse::<u32>())
12+
.collect();
13+
14+
let mut nif_version = match &parts[..] {
15+
[Ok(major), Ok(minor)] => (*major, *minor),
16+
_other => panic!("The RUSTLER_NIF_VERSION is not a valid version"),
17+
};
18+
19+
if nif_version < MIN_SUPPORTED_VERSION {
20+
panic!(
21+
"The NIF version given from RUSTLER_NIF_VERSION is not supported: {}.{}",
22+
nif_version.0, nif_version.1
23+
);
24+
}
25+
26+
// TODO: This code will need adjustment if the Erlang developers ever decide to introduce
27+
// a new major NIF version.
28+
if nif_version > MAX_SUPPORTED_VERSION {
29+
eprintln!(
30+
"NIF version {}.{} is not yet supported, falling back to {}.{}",
31+
nif_version.0, nif_version.1, MAX_SUPPORTED_VERSION.0, MAX_SUPPORTED_VERSION.1
32+
);
33+
nif_version = MAX_SUPPORTED_VERSION;
34+
}
35+
36+
nif_version
37+
}
38+
39+
pub fn handle_nif_version_from_env() -> Option<(u32, u32)> {
40+
println!("cargo:rerun-if-env-changed=RUSTLER_NIF_VERSION");
41+
env::var("RUSTLER_NIF_VERSION").ok().map(|val| {
42+
let nif_version = get_nif_version_from_env(&val);
43+
44+
// Activate all config flags for the supported NIF versions
45+
for minor in 0..=nif_version.1 {
46+
println!(
47+
"cargo:rustc-cfg=feature=\"nif_version_{}_{}\"",
48+
nif_version.0, minor
49+
);
50+
}
51+
52+
nif_version
53+
})
54+
}
55+
}

rustler/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ readme = "../README.md"
99
edition = "2018"
1010

1111
[features]
12-
default = ["derive"]
12+
default = ["derive", "nif_version_2_14"]
1313
derive = ["rustler_codegen"]
1414
alternative_nif_init_name = []
15+
nif_version_2_14 = ["rustler_sys/nif_version_2_14"]
16+
nif_version_2_15 = ["nif_version_2_14", "rustler_sys/nif_version_2_15"]
17+
nif_version_2_16 = ["nif_version_2_15", "rustler_sys/nif_version_2_16"]
18+
nif_version_2_17 = ["nif_version_2_16", "rustler_sys/nif_version_2_17"]
1519

1620
[dependencies]
1721
lazy_static = "1.4"

rustler/build.rs

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,8 @@
1-
/// Detect NIF version to build against
2-
/// It reads from the RUSTLER_NIF_VERSION env var.
3-
///
4-
/// If this env var is not present we try to read from the installed Erlang.
5-
/// If the environment doesn't have Erlang installed, then we use the latest
6-
/// NIF version and write a warning to stderr.
7-
use std::env;
8-
use std::process::Command;
9-
10-
// keep this sorted by version number
11-
const NIF_VERSION: &[&str] = &["2.14", "2.15", "2.16", "2.17"];
1+
include!("build_common.rs");
122

133
fn main() {
14-
let latest_version = NIF_VERSION.last().unwrap().to_string();
15-
let version = env::var("RUSTLER_NIF_VERSION").unwrap_or_else(|_| {
16-
match get_version_from_erl() {
17-
Some(nif_version) if NIF_VERSION.contains(&nif_version.as_str()) => {
18-
eprintln!("RUSTLER_NIF_VERSION env var is not set. Using version from Erlang: {}", nif_version);
19-
nif_version
20-
},
21-
Some(ref nif_version) => panic!("The NIF version from Erlang is not supported by Rustler: {}", nif_version),
22-
None => {
23-
eprintln!("RUSTLER_NIF_VERSION env var is not set and `erl` command is not found. Using version {}", latest_version);
24-
latest_version
25-
},
26-
}
27-
});
28-
29-
activate_versions(&version);
4+
common::handle_nif_version_from_env();
305

316
// The following lines are important to tell Cargo to recompile if something changes.
327
println!("cargo:rerun-if-changed=build.rs");
33-
println!("cargo:rerun-if-env-changed=RUSTLER_NIF_VERSION");
34-
}
35-
36-
fn get_version_from_erl() -> Option<String> {
37-
let args = vec![
38-
"-noshell",
39-
"-eval",
40-
r#"io:format("~s~n", [erlang:system_info(nif_version)]), init:stop()."#,
41-
];
42-
43-
let version = Command::new("erl").args(args).output().ok()?.stdout;
44-
45-
let version = String::from_utf8(version).ok()?;
46-
47-
Some(version.trim().into())
48-
}
49-
50-
fn activate_versions(version: &str) {
51-
let index = NIF_VERSION
52-
.iter()
53-
.position(|&v| v == version)
54-
.unwrap_or_else(|| {
55-
panic!(
56-
"Erlang version {} not handled, please file a a bug report.",
57-
version
58-
)
59-
});
60-
61-
#[allow(clippy::needless_range_loop)]
62-
for i in 0..=index {
63-
println!(
64-
"cargo:rustc-cfg=nif_version_{}",
65-
version_feature(NIF_VERSION[i])
66-
);
67-
}
68-
}
69-
70-
fn version_feature(version: &str) -> String {
71-
version.replace('.', "_")
728
}

rustler/build_common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../build_common.rs

rustler_mix/lib/rustler/compiler.ex

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@ defmodule Rustler.Compiler do
2828
System.cmd(cmd, args,
2929
cd: crate_full_path,
3030
stderr_to_stdout: true,
31-
env: [
32-
{"CARGO_TARGET_DIR", config.target_dir},
33-
{"RUSTLER_NIF_VERSION", nif_version()}
34-
| config.env
35-
],
31+
env: [{"CARGO_TARGET_DIR", config.target_dir} | config.env],
3632
into: IO.stream(:stdio, :line)
3733
)
3834

@@ -50,10 +46,6 @@ defmodule Rustler.Compiler do
5046
config
5147
end
5248

53-
defp nif_version do
54-
System.get_env("RUSTLER_NIF_VERSION") || to_string(:erlang.system_info(:nif_version))
55-
end
56-
5749
defp make_base_command(:system), do: ["cargo", "rustc"]
5850
defp make_base_command({:system, channel}), do: ["cargo", channel, "rustc"]
5951
defp make_base_command({:bin, path}), do: [path, "rustc"]

rustler_sys/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ build = "build.rs"
3434

3535
categories = ["external-ffi-bindings"]
3636

37+
[features]
38+
# Default version: 2.14
39+
default = ["nif_version_2_14"]
40+
nif_version_2_14 = []
41+
nif_version_2_15 = ["nif_version_2_14"]
42+
nif_version_2_16 = ["nif_version_2_15"]
43+
nif_version_2_17 = ["nif_version_2_16"]
44+
3745
[dependencies]
3846
unreachable = "1.0"
3947

0 commit comments

Comments
 (0)