Skip to content

Commit

Permalink
Support building with a variant name (#5)
Browse files Browse the repository at this point in the history
* Support building with a variant name

This is part of philss/rustler_precompiled#61

* Add the "features" option and improve logging

* Change the `features` input to `cargo-args`

The idea is to have more flexibility, enabling more arguments to be
passed.

* Add a flag to enable debug mode compilation

* Fix term and docs
  • Loading branch information
philss authored Sep 22, 2023
1 parent edd7eff commit a4b602e
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 22 deletions.
46 changes: 37 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,34 @@ See the example workflow below.
The following inputs are accepted:
| Name | Description | Required | Default |
|-------------------|-----------------------------------------------------------------------|----------|------------|
| `cross-version` | The version desired for cross. Only relevant if `use-cross` is true. | false | `"v0.2.4"` |
| `nif-version` | The NIF version that we are aiming to. | false | `"2.16"` |
| `project-dir` | A relative path where the project is located. | true | `"./"` |
| `project-name` | Name of the crate that is being built. Same as in Cargo.toml | true | |
| `project-version` | The version of the Elixir package that the crate is in. | true | |
| `target` | The Rust target we are building to. | true | |
| `use-cross` | If the target requires the usage of cross. | false | |
| Name | Description | Required | Default | Example |
|-------------------|-----------------------------------------------------------------------|----------|------------|------------------------------|
| `cross-version` | The version desired for cross. Only relevant if `use-cross` is true. | false | `"v0.2.4"` | |
| `nif-version` | The NIF version that we are aiming to. | false | `"2.16"` | |
| `project-dir` | A relative path where the project is located. | true | `"./"` | `"native/example"` |
| `project-name` | Name of the crate that is being built. Same as in Cargo.toml | true | | |
| `project-version` | The version of the Elixir package that the crate is in. | true | | |
| `target` | The Rust target we are building to. | true | | `"x86_64-unknown-linux-gnu"` |
| `use-cross` | If the target requires the usage of cross. | false | `false` | |
| `variant` | A name that represents an alternative version, added as a suffix. | false | `""` | `"old_glibc"` |
| `cargo-args` | A string with cargo arguments to be appended to the build command. | false | `""` | `"--features a,b,c"` |

Note that the build is going to activate cargo features for the NIF version,
depending on which version of Rustler the project is using.
Please read [RustlerPrecompiled's guide] for details.

### Environment variables

Some env vars can be used to control how the build is made. Right now, the following are accepted:

* `RUSTFLAGS` - used to pass down the compilation flags to the Rust compiler.

* `RUSTLER_PRECOMPILED_DEBUG_MODE` - a flag that enables debug mode compilation (the default in development).
By default it's unset, but you can set it to `"true"` to activate it.

* `DRY_RUN` - a flag that is useful to see the command that is going to be executed. This is
useful for debugging. Note that the build will fail, since it cannot generate the artifacts.
By default it's unset, but you can set it to `"true"` to activate it.

## Outputs

Expand Down Expand Up @@ -126,6 +145,15 @@ jobs:
```

## Debugging

It's possible to read the logs of the build in the GitHub Actions page.

But if you need to play with the setup without building the project every
time, you can set the environment variable `DRY_RUN` to `true`, and you
can see the output of the command instead of running it.
Be aware that the build will fail because it won't be able to find the artifacts.

## License

Copyright 2023 Philip Sampaio
Expand Down
44 changes: 41 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,29 @@ inputs:
For example, "native/my_nif".
required: true
default: "./"
variant:
description: >
An alternative version for a given target that is useful when you are building for
the same target, but with different dependencies, or hardware capatibilities.
This name should be a valid string that can be represented as an Elixir atom.
Prefer to use only alphanumerics and underscores.
This is going to be appended to the artifact name.
required: false
default: ""
cargo-args:
description: >
A string with additional arguments to be appended to the build command.
This is useful to pass flags like `--features`.
Notice that the flags `--release` and `--target` are going to be
passed down automatically.
The flag `--features` may be passed, depending of the NIF version. But you can
always pass this flag multiple times.
required: false
default: ""

outputs:
file-name:
Expand Down Expand Up @@ -82,11 +105,13 @@ runs:
rustc -V
rustc --print=cfg
echo "env RUSTLER_NIF_VERSION=$RUSTLER_NIF_VERSION"
echo "env RUSTFLAGS=$RUSTFLAGS"
echo "env RUSTLER_PRECOMPILED_DEBUG_MODE=$RUSTLER_PRECOMPILED_DEBUG_MODE"
- name: Build
shell: bash
run: |
build.sh "$(pwd)" "${{ inputs.project-dir }}" "${{ inputs.target }}" "${{ inputs.nif-version }}" "${{ inputs.use-cross }}"
build.sh "$(pwd)" "${{ inputs.project-dir }}" "${{ inputs.target }}" "${{ inputs.nif-version }}" "${{ inputs.use-cross }}" "${{ inputs.cargo-args }}"
- name: Rename lib to the final name
id: rename
Expand All @@ -112,9 +137,16 @@ runs:
*-pc-windows-*) LIB_SUFFIX=".dll" ;;
esac;
TARGET_MODE_DIR="release"
# A scape hatch to improve build times for when a release is not the intention.
if [ "$RUSTLER_PRECOMPILED_DEBUG_MODE" = "true" ]; then
TARGET_MODE_DIR="debug"
fi
# Setup paths
LIB_NAME="${LIB_PREFIX}${{ inputs.project-name }}${LIB_SUFFIX}"
LIB_DIR="target/${{ inputs.target }}/release"
LIB_DIR="target/${{ inputs.target }}/${TARGET_MODE_DIR}"
# Final name
# In the end we use ".so" for MacOS in the final build
Expand All @@ -124,8 +156,14 @@ runs:
*-apple-darwin) LIB_FINAL_SUFFIX=".so" ;;
esac;
MAYBE_VARIANT_NAME="${{ inputs.variant }}"
if [[ $MAYBE_VARIANT_NAME != "" ]]; then
MAYBE_VARIANT_NAME="--${MAYBE_VARIANT_NAME}"
fi
# It saves in the format RustlerPrecompiled expects.
LIB_FINAL_NAME="${LIB_PREFIX}${{ inputs.project-name }}-v${{ inputs.project-version }}-nif-${{ inputs.nif-version }}-${{ inputs.target }}${LIB_FINAL_SUFFIX}"
LIB_FINAL_NAME="${LIB_PREFIX}${{ inputs.project-name }}-v${{ inputs.project-version }}-nif-${{ inputs.nif-version }}-${{ inputs.target }}${MAYBE_VARIANT_NAME}${LIB_FINAL_SUFFIX}"
TAR_GZ_FILE="${LIB_FINAL_NAME}.tar.gz"
cd "${LIB_DIR}"
Expand Down
68 changes: 58 additions & 10 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
#!/bin/bash

# Usage: ./build.sh "$(pwd)" "$(pwd)" "x86_64-unknown-linux-gnu" 2.17 true
# Usage: ./build.sh "$(pwd)" "$(pwd)" "x86_64-unknown-linux-gnu" 2.17 true "--features a,b --no-default-features"
#
# 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.
#
# Set the env var "DRY_RUN" to "true" in order to see the command that
# is going to build the project, instead of building it.

initial_dir=$1
project_dir=$2
target_arch=$3
nif_version=$4
use_cross=${5:-"false"}
cargo_args=${6:-""}

logging=$(mktemp)

# Version 0.29 is where RUSTLER_NIF_VERSION env var was deprecated.
Expand Down Expand Up @@ -87,10 +92,15 @@ cd "$project_dir"

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

echo "Cargo features: $cargo_features" >> $logging

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

echo "Rustler version: $rustler_version" >> $logging
echo "Highest NIF version: $highest_nif_version" >> $logging

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

Expand All @@ -100,46 +110,84 @@ if [ "$tool" = "cross" ]; then
check_cross_config "$rustler_version"
fi

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

# A scape hatch to improve build times for when a release is not the intention.
if [ "$RUSTLER_PRECOMPILED_DEBUG_MODE" != "true" ]; then
args="$args --release"
fi

# This is going to add arbritary args to the command, if the user provide it.
if [ "$cargo_args" != "" ]; then
args="$args $cargo_args"
fi

echo "Rustler version: $rustler_version"
echo "NIF version: $nif_version"
echo "Desired feature for NIF version: $desired_feature"
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.
# activated by default, 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.
# 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
echo "Highest NIF version is not equal to desired feature. We need to find a compatible NIF version feature..." >> $logging

if [[ "$rustler_version" > "$rustler_features_since" ]] || [[ "$rustler_version" == "$rustler_features_since" ]]; then
echo "Rustler version is equal or above $rustler_features_since" >> $logging

# 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)
cargo_features=$(cargo tree -e features --depth 1 -i rustler -f "{p};{f}" --prefix none -F "$desired_feature" 2>>"$logging" | head -n 1)

echo "Rustler features when desired feature is activated in this project: $cargo_features" >> $logging

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

echo "Rustler NIF features related to versions: $rustler_nif_versions" >> $logging
echo "Highest Rustler NIF version from features: $highest_nif_version" >> $logging

if [[ "$highest_nif_version" == "$desired_feature" ]]; then
echo "Successfully found feature to activate: $desired_feature" >> $logging

args="$args --features $desired_feature"
else
if [[ "$rustler_version" > "$rustler_features_required_since" ]] || [[ "$rustler_version" == "$rustler_features_required_since" ]]; then
echo "Rustler version is equal or above $rustler_features_required_since, and it's not possible to activate the feature \"$desired_feature\" for the NIF version." >> $logging
echo "$logging"
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
else
echo "Rustler version is older than the one that uses features to activate NIF versions. So we ignore it for now." >> $logging
fi
fi

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

## Finally executes the command
echo "Building..."
eval "$tool" "$args"
echo "Done."
if [ "$DRY_RUN" = "true" ]; then
echo "Only printing command to run:"
echo
echo "$tool $args"
echo
else
echo "Building..."
echo
# Eval is OK here :)
eval "$tool" "$args"
echo
fi

echo "Done."
echo "Going back to original dir: $initial_dir"

cd "$initial_dir"

0 comments on commit a4b602e

Please sign in to comment.