Skip to content

Commit

Permalink
Support Rustler's NIF version selection using features (#4)
Browse files Browse the repository at this point in the history
* Support Rustler NIF version selection using features

This adds support for the new way to select the NIF version of a project
by following the convention that Rustler introduced in v0.29.0.

The idea is to have cargo features in your NIF project that maps to the
features of Rustler.

So one would need to add something like this in the Cargo.toml:

```toml
[dependencies]
rustler = { version = "0.29", default-features = false, features = ["derive"] }

[features]
default = ["nif_version_2_15"]
nif_version_2_15 = ["rustler/nif_version_2_15"]
nif_version_2_16 = ["rustler/nif_version_2_16"]
nif_version_2_17 = ["rustler/nif_version_2_17"]
```

And then, use the feature whenever a given functionality is restricted
to some NIF version in the native code.

Alternatively, your project can build for an specific NIF version, by
enabling it directly in your dependency declaration:

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

See the upgrade guide for more details:
https://github.com/rusterlium/rustler/blob/master/UPGRADE.md#028---029

* Add mention to RustlerPrecompiled's guide

* Fix checks of strings

Use pattern match style must of the times.
  • Loading branch information
philss authored Sep 15, 2023
1 parent a08977c commit edd7eff
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 21 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ It's **important to notice that this Action won't install Rust**. So you need to
install it first. For that we recommend the [dtolnay/rust-toolchain] action that
is well maintained.

See the [RustlerPrecompiled's guide] before trying to setup your project, because
there is a lot of things **you must configure in your project** before using this
GH Action.

## Usage

```yaml
Expand Down Expand Up @@ -67,7 +71,7 @@ jobs:
strategy:
fail-fast: false
matrix:
nif: ["2.16", "2.15"]
nif: ["2.15"]
job:
- { target: aarch64-apple-darwin , os: macos-11 }
- { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04 , use-cross: true }
Expand Down Expand Up @@ -142,3 +146,4 @@ limitations under the License.
[RustlerPrecompiled]: https://github.com/philss/rustler_precompiled
[RustlerPrecompiledExample]: https://github.com/philss/rustler_precompilation_example
[dtolnay/rust-toolchain]: https://github.com/dtolnay/rust-toolchain
[RustlerPrecompiled's guide]: https://hexdocs.pm/rustler_precompiled/precompilation_guide.html
26 changes: 6 additions & 20 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ runs:
run: |
echo "RUSTLER_NIF_VERSION=${{ inputs.nif-version }}" >> $GITHUB_ENV
- name: Add the action path to the gh paths to find scripts
shell: bash
run: |
echo "${{ github.action_path }}" >> $GITHUB_PATH
- name: Download cross from GitHub releases
uses: giantswarm/install-binary-action@v1.1.0
if: ${{ inputs.use-cross }}
Expand All @@ -81,26 +86,7 @@ runs:
- name: Build
shell: bash
run: |
INITIAL_DIR=$(pwd)
echo "Going from path: $INITIAL_DIR"
echo "To path: ${{ inputs.project-dir }}"
cd "${{ inputs.project-dir }}"
if [ "${{ inputs.use-cross }}" == "true" ]; then
if grep --quiet "RUSTLER_NIF_VERSION" "Cross.toml"; then
echo "Cross configuration looks good."
else
echo "::error file=Cross.toml,line=1::Missing configuration for passing the RUSTLER_NIF_VERSION env var to cross. Please read the precompilation guide: https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#additional-configuration-before-build"
exit 1
fi
cross build --release --target=${{ inputs.target }}
else
cargo build --release --target=${{ inputs.target }}
fi
cd "$INITIAL_DIR"
build.sh "$(pwd)" "${{ inputs.project-dir }}" "${{ inputs.target }}" "${{ inputs.nif-version }}" "${{ inputs.use-cross }}"
- name: Rename lib to the final name
id: rename
Expand Down
145 changes: 145 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/bin/bash

# Usage: ./build.sh "$(pwd)" "$(pwd)" "x86_64-unknown-linux-gnu" 2.17 true
#
# It composes the "build" command and executes it at the end.
#
# This script is also responsible for selecting the correct NIF version
# using an ENV var or a cargo feature, depending on Rustler version.

initial_dir=$1
project_dir=$2
target_arch=$3
nif_version=$4
use_cross=${5:-"false"}
logging=$(mktemp)

# Version 0.29 is where RUSTLER_NIF_VERSION env var was deprecated.
rustler_features_since="v0.29.0"
# Version 0.30 is where RUSTLER_NIF_VERSION env var was removed.
rustler_features_required_since="v0.30.0"

function build_tool() {
if [ "$1" = "true" ]; then
echo "cross"
else
echo "cargo"
fi
}

function check_cross_config() {
if [[ "$1" < "$rustler_features_since" ]]; then
if grep --quiet "RUSTLER_NIF_VERSION" "Cross.toml"; then
echo "Cross configuration looks good." >> "$logging"
else
echo "::error file=Cross.toml,line=1::Missing configuration for passing the RUSTLER_NIF_VERSION env var to cross. Please read the precompilation guide: https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#additional-configuration-before-build"
exit 1
fi
else
echo "No need to check env var presence." >> $logging
fi
}

# Get the Rustler version from "cargo tree -e features" output.
#
# It takes the first line that is in the format of "{p};{f}"
# from the command:
#
# $ cargo tree -e features --depth 1 -i rustler -f "{p};{f}" --prefix none
#
# ## Examples
#
# my_string="rustler v0.29.0;derive,nif_version_2_14,nif_version_2_15,rustler_codegen"
# rustler_version "$my_string"
# #=> "v0.29.0"
#
function rustler_version() {
echo "$1" | cut -d\; -f1 | cut -d' ' -f2
}

# Get the Rustler NIF versions features from "cargo tree -e features" output.
#
# It takes the first line that is in the format of "{p};{f}"
# from the command:
#
# $ cargo tree -e features --depth 1 -i rustler -f "{p};{f}" --prefix none
#
# Alternatively one can pass the "--features" flag, in order to activate a desired
# feature - of the project being compiled - and get the list of active features from Rustler.
#
# The NIF versions are returned sorted by the higher to the lowest version,
# separated by a new line.
#
# ## Examples
#
# my_string="rustler v0.29.0;derive,nif_version_2_14,nif_version_2_15,rustler_codegen"
# rustler_nif_versions "$my_string"
# #=> "nif_version_2_15\nnif_version_2_14"
#
function rustler_nif_versions() {
echo "$1" | cut -d\; -f2 | tr ',' '\n' | grep "nif_version" | sort -r
}

echo "Going from path: $initial_dir" >> "$logging"
echo "To path: $project_dir" >> "$logging"

cd "$project_dir"

cargo_features=$(cargo tree -e features --depth 1 -i rustler -f "{p};{f}" --prefix none | head -n 1)

rustler_version=$(rustler_version "$cargo_features")
rustler_nif_versions=$(rustler_nif_versions "$cargo_features")
highest_nif_version=$(echo "$rustler_nif_versions" | head -n1)

nif_version=$(echo -n "$nif_version" | tr '.' '_')
desired_feature="nif_version_$nif_version"

tool=$(build_tool "$use_cross")

if [ "$tool" = "cross" ]; then
check_cross_config "$rustler_version"
fi

args="build --release --target=$target_arch"

echo "Rustler version: $rustler_version"
echo "NIF version: $nif_version"
echo "Tool: $tool"

# We try to modify args in case the desired feature is not the highest
# activated, and the Rustler version is at least v0.29.0.
#
# In case Rustler v0.30 or above is in use and the desired NIF version feature
# could not be activated, we log an error and exit.
if [ "$highest_nif_version" != "$desired_feature" ]; then
if [[ "$rustler_version" > "$rustler_features_since" ]] || [[ "$rustler_version" == "$rustler_features_since" ]]; then
# So we test if the desired feature appears when we active it in the project.
cargo_features=$(cargo tree -e features --depth 1 -i rustler -f "{p};{f}" --prefix none -F "$desired_feature" 2>/dev/null | head -n 1)

rustler_nif_versions=$(rustler_nif_versions "$cargo_features")
highest_nif_version=$(echo "$rustler_nif_versions" | head -n1)

if [[ "$highest_nif_version" == "$desired_feature" ]]; then
args="$args --features $desired_feature"
else
if [[ "$rustler_version" > "$rustler_features_required_since" ]] || [[ "$rustler_version" == "$rustler_features_required_since" ]]; then
echo "::error file=Cargo.toml,line=1::The desired feature \"$desired_feature\" is not equal to the highest NIF version that is active: \"$highest_nif_version\""
echo "::error file=Cargo.toml,line=1::Missing setup of NIF features that is required since Rustler $rustler_features_required_since. Please read the precompilation guide: https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#additional-configuration-before-build"
exit 1
fi
fi
fi
fi

echo "Arguments: $args"
echo
echo "Logs:"
echo "$(cat $logging)"

## Finally executes the command
echo "Building..."
eval "$tool" "$args"
echo "Done."

echo "Going back to original dir: $initial_dir"
cd "$initial_dir"

0 comments on commit edd7eff

Please # to comment.