Building from source

Copy Markdown

Elixir 1.20.2 and OTP 28. The default mix test does not compile Rust. Metadata tests and the user-guide examples that avoid F3.read/2 run on that build.

mix deps.get
mix test
mix ci

mix ci is the required CI job: format, compile with warnings as errors, Credo, tests with coverage, and docs. It stops at the first failing step. The test step stops at the first failed test and requires 90% line coverage.

A git checkout compiles Rust only when F3_BUILD_NATIVE=1. Hex has not been published.

Releases

Pushing a tag vX.Y.Z that matches @version in mix.exs runs .github/workflows/ci.yml. That is the same workflow as a pull request. After the tests pass, the decode job packages the NIF and the f3_write helper:

  • aarch64-apple-darwin on macos-15
  • x86_64-unknown-linux-gnu on ubuntu-latest

The NIF archive is libf3_native-vVERSION-nif-2.17-TARGET.so.tar.gz. OTP 28 uses NIF version 2.17. The writer archive is f3_write-vVERSION-TARGET.tar.gz. The release job in .github/workflows/ci.yml attaches those archives to the GitHub release. The publish job then writes checksum-Elixir.F3.Native.exs and checksum-f3_write.exs and runs mix hex.publish. A local mix hex.publish --dry-run omits those files. The publish job writes them first, and the package list picks up whatever checksum-*.exs files are present. It sets F3_SKIP_NATIVE_FETCH=1 so that publish packages the checksums without downloading the archives again. The repository secret HEX_API_KEY is required. Those checksum files are not in git. A Hex install downloads the archives for the two targets above. F3_BUILD_NATIVE=1 still compiles from source, including for any other target.

F3_BUILD_NATIVE=1 mix compile
F3_BUILD_NATIVE=1 mix test --only native

The NIF build calls rustup's nightly-2025-11-15 binaries even when asdf or Homebrew puts a stable cargo earlier on PATH. The writer is a separate f3_write binary. That build fetches pinned fff-poc at bd92506447dc1333820cfe9a34ebbf497fe0219e and compiles it with nightly-2025-02-24. The NIF decodes one CASCADE encoding unit on the dirty CPU scheduler and, only when Elixir asks, one Wasm module with Wasmtime 28. It does not link fff-poc and it does not install a global allocator. Mix recompiles F3.Native when the flag changes. The default mix test excludes the native tests and does not invoke cargo.

scripts/bench.exs times F3.read/2 and F3.stream/2 on the committed fixtures. It does not download data and it is not a CI gate. mix test excludes the :minio tag unless F3_MINIO=1.

Regenerate fixtures with scripts/fixtures/generate.sh. That script clones the pinned upstream commit, builds fff-poc with Rust nightly-2025-02-24, and rewrites test/fixtures/. It is not part of mix test.

The wire-format pin is in upstream compatibility. The component map is in architecture. The test record is in docs/test-reports/.