├── .formatter.exs ├── .github └── workflows │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── lib ├── rustler_precompilation_example.ex └── rustler_precompilation_example │ └── native.ex ├── mix.exs ├── mix.lock ├── native └── example │ ├── .cargo │ └── config │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Cross.toml │ ├── README.md │ └── src │ └── lib.rs └── test ├── rustler_precompilation_example_test.exs └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build precompiled NIFs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '*' 9 | 10 | jobs: 11 | build_release: 12 | name: NIF ${{ matrix.nif }} - ${{ matrix.job.target }} (${{ matrix.job.os }}) 13 | runs-on: ${{ matrix.job.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | nif: ["2.16", "2.15"] 18 | job: 19 | - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04 , use-cross: true } 20 | - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04 , use-cross: true } 21 | - { target: aarch64-unknown-linux-musl , os: ubuntu-20.04 , use-cross: true } 22 | - { target: aarch64-apple-darwin , os: macos-12 } 23 | - { target: riscv64gc-unknown-linux-gnu , os: ubuntu-20.04 , use-cross: true } 24 | - { target: x86_64-apple-darwin , os: macos-12 } 25 | - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 } 26 | - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04 , use-cross: true } 27 | - { target: x86_64-pc-windows-gnu , os: windows-2019 } 28 | - { target: x86_64-pc-windows-msvc , os: windows-2019 } 29 | 30 | steps: 31 | - name: Checkout source code 32 | uses: actions/checkout@v3 33 | 34 | - name: Extract project version 35 | shell: bash 36 | run: | 37 | # Get the project version from mix.exs 38 | echo "PROJECT_VERSION=$(sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs | head -n1)" >> $GITHUB_ENV 39 | 40 | - name: Install Rust toolchain 41 | uses: dtolnay/rust-toolchain@stable 42 | with: 43 | toolchain: stable 44 | target: ${{ matrix.job.target }} 45 | 46 | - name: Build the project 47 | id: build-crate 48 | uses: philss/rustler-precompiled-action@v1.0.1 49 | with: 50 | project-name: example 51 | project-version: ${{ env.PROJECT_VERSION }} 52 | target: ${{ matrix.job.target }} 53 | nif-version: ${{ matrix.nif }} 54 | use-cross: ${{ matrix.job.use-cross }} 55 | project-dir: "native/example" 56 | 57 | - name: Artifact upload 58 | uses: actions/upload-artifact@v3 59 | with: 60 | name: ${{ steps.build-crate.outputs.file-name }} 61 | path: ${{ steps.build-crate.outputs.file-path }} 62 | 63 | - name: Publish archives and packages 64 | uses: softprops/action-gh-release@v1 65 | with: 66 | files: | 67 | ${{ steps.build-crate.outputs.file-path }} 68 | if: startsWith(github.ref, 'refs/tags/') 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | rustler_precompilation_example-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | 28 | # Ignore the generated "native" dir from Rustler 29 | /priv/native/ 30 | 31 | # checksum file should only be included in Hex packages 32 | checksum-*.exs 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.5.0] - 2023-01-23 10 | 11 | - Nothing changed. Only the "release" workflow that is now using the following GitHub Action: 12 | https://github.com/philss/rustler-precompiled-action 13 | 14 | ## [0.4.0] - 2022-04-28 15 | 16 | ### Changed 17 | 18 | - Bump `rustler_precompiled` requirement. 19 | - Add `rustler` as an optional dependency. 20 | 21 | ## [0.3.0] - 2022-03-09 22 | 23 | ### Changed 24 | 25 | - Change the RustlerPrecompiled version to v0.2.0 so we can use the simplified API 26 | that falls back to Rustler if in a development version, or with an env var. 27 | 28 | ## [0.2.0] - 2021-12-20 29 | 30 | ### Added 31 | 32 | - Start using precompilation. Although it's required to generate the checksum file, 33 | users can experiment this project without having to compile from source. 34 | 35 | ## [0.1.1] - 2021-12-20 36 | 37 | ### Fixed 38 | 39 | - Fix compilation on Windows with the Rustler version of GitHub. 40 | 41 | ## [0.1.0] - 2021-12-19 42 | 43 | ### Added 44 | 45 | - Start the project with a basic NIF example. This version is not going to use 46 | precompiled NIFs, but will build them in the CI. 47 | 48 | [Unreleased]: https://github.com/philss/rustler_precompilation_example/compare/v0.4.0...HEAD 49 | [0.4.0]: https://github.com/philss/rustler_precompilation_example/compare/v0.3.0...v0.4.0 50 | [0.3.0]: https://github.com/philss/rustler_precompilation_example/compare/v0.2.0...v0.3.0 51 | [0.2.0]: https://github.com/philss/rustler_precompilation_example/compare/v0.1.1...v0.2.0 52 | [0.1.1]: https://github.com/philss/rustler_precompilation_example/compare/v0.1.0...v0.1.1 53 | [0.1.0]: https://github.com/philss/rustler_precompilation_example/releases/tag/v0.1.0 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustler Precompilation Example 2 | 3 | This is a sample project for using precompiled NIFs with Rustler. 4 | 5 | In order to use it, you either set the `RUSTLER_PRECOMPILATION_EXAMPLE_BUILD` env var 6 | to force compilation, or you first run the following mix task: 7 | 8 | mix rustler_precompiled.download RustlerPrecompilationExample.Native --only-local 9 | 10 | This is going to save the checksum file locally, which is required for the project to 11 | load the NIF from the internet. 12 | 13 | After that, enter the console with `iex -S mix` and test the `add/2` function: 14 | 15 | ```elixir 16 | RustlerPrecompilationExample.Native.add(2, 2) 17 | ``` 18 | 19 | The GitHub Action responsible for building the NIFs can be found at [.github/workflows/release.yml](https://github.com/philss/rustler_precompilation_example/blob/main/.github/workflows/release.yml). 20 | 21 | For further details check the [RustlerPrecompiled project](https://github.com/philss/rustler_precompiled). 22 | -------------------------------------------------------------------------------- /lib/rustler_precompilation_example.ex: -------------------------------------------------------------------------------- 1 | defmodule RustlerPrecompilationExample do 2 | @moduledoc """ 3 | Documentation for `RustlerPrecompilationExample`. 4 | """ 5 | 6 | @doc """ 7 | Hello world. 8 | 9 | ## Examples 10 | 11 | iex> RustlerPrecompilationExample.hello() 12 | :world 13 | 14 | """ 15 | def hello do 16 | :world 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/rustler_precompilation_example/native.ex: -------------------------------------------------------------------------------- 1 | defmodule RustlerPrecompilationExample.Native do 2 | version = Mix.Project.config()[:version] 3 | 4 | use RustlerPrecompiled, 5 | otp_app: :rustler_precompilation_example, 6 | crate: "example", 7 | base_url: 8 | "https://github.com/philss/rustler_precompilation_example/releases/download/v#{version}", 9 | force_build: System.get_env("RUSTLER_PRECOMPILATION_EXAMPLE_BUILD") in ["1", "true"], 10 | targets: 11 | Enum.uniq(["aarch64-unknown-linux-musl" | RustlerPrecompiled.Config.default_targets()]), 12 | version: version 13 | 14 | # When your NIF is loaded, it will override this function. 15 | def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) 16 | end 17 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule RustlerPrecompilationExample.MixProject do 2 | use Mix.Project 3 | 4 | @version "0.5.0" 5 | @source_url "https://github.com/philss/rustler_precompilation_example" 6 | 7 | def project do 8 | [ 9 | app: :rustler_precompilation_example, 10 | version: @version, 11 | elixir: "~> 1.13", 12 | start_permanent: Mix.env() == :prod, 13 | description: "A rustler precomplication example", 14 | package: package(), 15 | deps: deps() 16 | ] 17 | end 18 | 19 | # When publishing a library to with precompiled NIFs to Hex, 20 | # is is mandatory to include a checksum file (along with other 21 | # necessary files in the library). 22 | # 23 | # Refer to the "The release flow" 24 | # in the "Precompilation guide" for more details: 25 | # https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#the-release-flow 26 | defp package do 27 | [ 28 | files: [ 29 | "lib", 30 | "native", 31 | "checksum-*.exs", 32 | "mix.exs" 33 | ], 34 | licenses: ["Apache-2.0"], 35 | links: %{"GitHub" => @source_url} 36 | ] 37 | end 38 | 39 | # Run "mix help compile.app" to learn about applications. 40 | def application do 41 | [ 42 | extra_applications: [:logger] 43 | ] 44 | end 45 | 46 | # Run "mix help deps" to learn about dependencies. 47 | defp deps do 48 | [ 49 | {:rustler_precompiled, "~> 0.4"}, 50 | {:rustler, ">= 0.0.0", optional: true} 51 | ] 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, 3 | "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, 4 | "rustler": {:hex, :rustler, "0.27.0", "53ffe86586fd1a2ea60ad07f1506962914eb669dba26c23010cf672662ec8d64", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "d7f5ccaec6e7a96f700330898ff2e9d48818e40789fd2951ba41ecf457986e92"}, 5 | "rustler_precompiled": {:hex, :rustler_precompiled, "0.5.5", "a075a92c8e748ce5c4f7b2cf573a072d206a6d8d99c53f627e81d3f2b10616a3", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "e8a7f1abfec8d68683bb25d14efc88496f091ef113f7f4c45d39f3606f7223f6"}, 6 | "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, 7 | } 8 | -------------------------------------------------------------------------------- /native/example/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.x86_64-apple-darwin] 2 | rustflags = [ 3 | "-C", "link-arg=-undefined", 4 | "-C", "link-arg=dynamic_lookup", 5 | ] 6 | 7 | [target.aarch64-apple-darwin] 8 | rustflags = [ 9 | "-C", "link-arg=-undefined", 10 | "-C", "link-arg=dynamic_lookup", 11 | ] 12 | 13 | # See https://github.com/rust-lang/rust/issues/59302 14 | [target.x86_64-unknown-linux-musl] 15 | rustflags = [ 16 | "-C", "target-feature=-crt-static" 17 | ] 18 | 19 | # See https://github.com/rust-lang/rust/issues/59302 20 | [target.aarch64-unknown-linux-musl] 21 | rustflags = [ 22 | "-C", "target-feature=-crt-static" 23 | ] 24 | 25 | [profile.release] 26 | lto = true 27 | -------------------------------------------------------------------------------- /native/example/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /native/example/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "example" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "rustler", 19 | ] 20 | 21 | [[package]] 22 | name = "heck" 23 | version = "0.4.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 26 | 27 | [[package]] 28 | name = "lazy_static" 29 | version = "1.4.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 32 | 33 | [[package]] 34 | name = "memchr" 35 | version = "2.4.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 38 | 39 | [[package]] 40 | name = "proc-macro2" 41 | version = "1.0.36" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 44 | dependencies = [ 45 | "unicode-xid", 46 | ] 47 | 48 | [[package]] 49 | name = "quote" 50 | version = "1.0.15" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 53 | dependencies = [ 54 | "proc-macro2", 55 | ] 56 | 57 | [[package]] 58 | name = "regex" 59 | version = "1.5.6" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" 62 | dependencies = [ 63 | "aho-corasick", 64 | "memchr", 65 | "regex-syntax", 66 | ] 67 | 68 | [[package]] 69 | name = "regex-syntax" 70 | version = "0.6.26" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" 73 | 74 | [[package]] 75 | name = "rustler" 76 | version = "0.27.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "b7a4037063eab996ea56fada36d3cc982d501ed485b36a73eabdaec99b4acf6e" 79 | dependencies = [ 80 | "lazy_static", 81 | "rustler_codegen", 82 | "rustler_sys", 83 | ] 84 | 85 | [[package]] 86 | name = "rustler_codegen" 87 | version = "0.27.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "b0dbdc98de074c324945ecc2e3ecefa47948ad8e6ed4ecf6f3424f1293a6188d" 90 | dependencies = [ 91 | "heck", 92 | "proc-macro2", 93 | "quote", 94 | "syn", 95 | ] 96 | 97 | [[package]] 98 | name = "rustler_sys" 99 | version = "2.2.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "3ff26a42e62d538f82913dd34f60105ecfdffbdb25abdc3c3580b0c622285332" 102 | dependencies = [ 103 | "regex", 104 | "unreachable", 105 | ] 106 | 107 | [[package]] 108 | name = "syn" 109 | version = "1.0.86" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 112 | dependencies = [ 113 | "proc-macro2", 114 | "quote", 115 | "unicode-xid", 116 | ] 117 | 118 | [[package]] 119 | name = "unicode-xid" 120 | version = "0.2.2" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 123 | 124 | [[package]] 125 | name = "unreachable" 126 | version = "1.0.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 129 | dependencies = [ 130 | "void", 131 | ] 132 | 133 | [[package]] 134 | name = "void" 135 | version = "1.0.2" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 138 | -------------------------------------------------------------------------------- /native/example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | authors = [] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "example" 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | rustler = "0.27.0" 14 | -------------------------------------------------------------------------------- /native/example/Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = [ 3 | "RUSTLER_NIF_VERSION" 4 | ] 5 | -------------------------------------------------------------------------------- /native/example/README.md: -------------------------------------------------------------------------------- 1 | # NIF for Elixir.RustlerPrecompilationExample.Native 2 | 3 | ## To build the NIF module: 4 | 5 | - Your NIF will now build along with your project. 6 | 7 | ## To load the NIF: 8 | 9 | ```elixir 10 | defmodule RustlerPrecompilationExample.Native do 11 | use Rustler, otp_app: :rustler_precompilation_example, crate: "example" 12 | 13 | # When your NIF is loaded, it will override this function. 14 | def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) 15 | end 16 | ``` 17 | 18 | ## Examples 19 | 20 | [This](https://github.com/hansihe/NifIo) is a complete example of a NIF written in Rust. 21 | -------------------------------------------------------------------------------- /native/example/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[rustler::nif] 2 | fn add(a: i64, b: i64) -> i64 { 3 | a + b 4 | } 5 | 6 | rustler::init!("Elixir.RustlerPrecompilationExample.Native", [add]); 7 | -------------------------------------------------------------------------------- /test/rustler_precompilation_example_test.exs: -------------------------------------------------------------------------------- 1 | defmodule RustlerPrecompilationExampleTest do 2 | use ExUnit.Case 3 | doctest RustlerPrecompilationExample 4 | 5 | test "greets the world" do 6 | assert RustlerPrecompilationExample.hello() == :world 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------