From edd7effd1a8710e168056d67fb68cd344f85cb4f Mon Sep 17 00:00:00 2001 From: Philip Sampaio Date: Fri, 15 Sep 2023 12:43:10 -0300 Subject: [PATCH] Support Rustler's NIF version selection using features (#4) * 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. --- README.md | 7 ++- action.yml | 26 +++------- build.sh | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 21 deletions(-) create mode 100755 build.sh diff --git a/README.md b/README.md index 6a2bf03..5013c54 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 } @@ -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 diff --git a/action.yml b/action.yml index 520132a..145a881 100644 --- a/action.yml +++ b/action.yml @@ -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 }} @@ -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 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..23e032a --- /dev/null +++ b/build.sh @@ -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"