Vix Development Guide

Copy Markdown View Source

Repo-specific notes for native builds, precompiled libvips, CI, and releases.

Compilation Modes

Vix supports three VIX_COMPILATION_MODE values:

  • PRECOMPILED_NIF_AND_LIBVIPS is the default. It downloads a precompiled Vix NIF from the Vix GitHub release when one is available. Do not use this as the only check for native C changes, because it can reuse the released NIF instead of compiling local C sources.
  • PRECOMPILED_LIBVIPS compiles the local NIF and links it against the sharp-libvips tarball. Use this for changes to native code, precompiled libvips, link flags, or packaged runtime files.
  • PLATFORM_PROVIDED_LIBVIPS uses pkg-config vips and links against the host libvips. Set VIX_LIBVIPS_PREFIX to test a specific local libvips install.

Useful commands:

rm -rf _build/*/lib/vix cache/ priv/*

ELIXIR_MAKE_CACHE_DIR="$(pwd)/cache" \
VIX_COMPILATION_MODE=PRECOMPILED_LIBVIPS \
mix test

VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS mix test

VIX_LIBVIPS_PREFIX=/path/to/libvips/prefix \
VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS \
mix test

make debug
make V=1 all

Generated paths such as _build/, deps/, priv/, cache/, .libvips/, toolchains/, and checksum.exs should not be committed.

Precompiled libvips

Vix downloads precompiled libvips from akash-akya/sharp-libvips.

Current configuration:

  • sharp-libvips release tag: v8.18.3-rc1
  • upstream libvips version: 8.18.3
  • Vix config: @release_tag in build_scripts/precompiler.exs
  • asset pattern: sharp-libvips-<platform>.tar.gz

Current target mapping:

Vix targetsharp-libvips asset
x86_64-linux-gnusharp-libvips-linux-x64.tar.gz
x86_64-linux-muslsharp-libvips-linuxmusl-x64.tar.gz
aarch64-linux-gnusharp-libvips-linux-arm64v8.tar.gz
aarch64-linux-muslsharp-libvips-linuxmusl-arm64v8.tar.gz
arm-linux-gnueabihfsharp-libvips-linux-armv6.tar.gz
armv7l-linux-gnueabihfsharp-libvips-linux-armv6.tar.gz
x86_64-apple-darwinsharp-libvips-darwin-x64.tar.gz
aarch64-apple-darwinsharp-libvips-darwin-arm64v8.tar.gz

For precompiled POSIX builds, Vix links directly to libvips-cpp:

precompiled_libvips/lib/libvips-cpp.so.*
precompiled_libvips/lib/libvips-cpp.*.dylib

This is expected. The sharp-libvips package exports the libvips C ABI from libvips-cpp, so the precompiled POSIX package does not need a separate libvips.so or libvips.dylib.

The files copied into precompiled Vix NIF archives are controlled by make_precompiler_priv_paths in mix.exs.

Local libvips Builds

Use scripts/build_libvips.sh when testing Vix against an upstream libvips ref without publishing a sharp-libvips release. The script builds a normal C libvips install for PLATFORM_PROVIDED_LIBVIPS.

./scripts/build_libvips.sh --ref vX.Y.Z

VIX_LIBVIPS_PREFIX="$(pwd)/.libvips/vX.Y.Z" \
VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS \
mix test

If --ref is omitted, the script resolves the latest upstream libvips release. Use --overwrite to replace an existing local prefix.

Toolchains

precompile.yaml uses musl cross-compilers for musl NIF targets. The scripts mirror the required musl.cc archives on Vix GitHub releases:

./scripts/download_toolchains.sh
./scripts/mirror_toolchains.sh

The current mirrored version is 11.2.1; update TOOLCHAIN_VERSION in both scripts if the mirror changes.

CI

.github/workflows/ci.yaml runs on pushes and pull requests to master and dev. It covers:

  • Linux PLATFORM_PROVIDED_LIBVIPS against the latest upstream libvips release.
  • Linux PRECOMPILED_LIBVIPS.
  • Linux default precompiled NIF mode with mix elixir_make.checksum --only-local.
  • ARM precompiled smoke test through Docker/QEMU.
  • macOS default precompiled and PRECOMPILED_LIBVIPS through Nix.
  • compile with warnings as errors, unused dependency checks, formatter, Credo, and Dialyzer.

.github/workflows/precompile.yaml runs for v* tags and uploads cache/*.tar.gz NIF artifacts to the matching GitHub release.

Release Process

Standard Vix Release

  1. Bump @version in mix.exs, commit, and push master.

    git add mix.exs
    git commit -m "Bump version to X.Y.Z"
    git push origin master
    
  2. Create and push the release tag.

    git tag -a vX.Y.Z -m "Release vX.Y.Z"
    git push origin vX.Y.Z
    
  3. Wait for .github/workflows/precompile.yaml to upload all NIF tarballs to the GitHub release.

  4. Generate checksums.

    unset VIX_COMPILATION_MODE
    rm -rf cache/ priv/* _build/*/lib/vix
    
    ELIXIR_MAKE_CACHE_DIR="$(pwd)/cache" \
    MIX_ENV=prod \
    mix elixir_make.checksum --all
    
    cat checksum.exs
    

    checksum.exs is generated, ignored by Git, and should not be committed. The checksum task rewrites it, so deleting an old copy first is optional. Keep it in the working tree for mix hex.publish.

  5. Test the release artifacts, then publish.

    unset VIX_COMPILATION_MODE
    rm -rf cache/ priv/* _build/*/lib/vix
    
    ELIXIR_MAKE_CACHE_DIR="$(pwd)/cache" mix test
    mix hex.publish
    

    Before accepting the publish prompt, confirm the file list includes checksum.exs and DEVELOPMENT.md.

  6. Optional cleanup:

    rm -rf cache/ priv/* checksum.exs _build/*/lib/vix doc/ vix-*.tar
    

    This is only housekeeping. checksum.exs can remain locally after publish.

Failed Tag Build

If the tag workflow fails before Hex publish, fix master, push the fix, move the tag, and force-push the tag:

git push origin master
git tag -fa vX.Y.Z -m "Release vX.Y.Z"
git push --force origin vX.Y.Z

The release workflow uses overwrite_files: true, so rerunning the same tag can replace partial GitHub release artifacts. After a Hex package is public, do not move the tag; publish a new patch or release-candidate version instead.

Libvips Update

Use this when Vix should consume a new sharp-libvips release.

  1. Rebase the sharp-libvips fork.

    cd ~/repos/clang/sharp-libvips-2
    git fetch upstream
    git checkout main
    git rebase upstream/main
    

    Preserve only the fork behavior Vix needs: direct GitHub release asset upload, .integrity files, fork-local notices, and any deliberate freshness gate change. Do not reintroduce old C-only packaging changes; Vix expects precompiled POSIX packages to provide libvips-cpp.

  2. Push and tag the sharp-libvips release candidate.

    git push --force-with-lease origin main
    git tag -a v<libvips-version>-rc<N> -m "libvips <libvips-version> rc<N>"
    git push origin v<libvips-version>-rc<N>
    
  3. Verify the release assets.

    curl -fsSL \
      https://api.github.com/repos/akash-akya/sharp-libvips/releases/tags/v8.18.3-rc1 \
      | jq -r '.assets[].name' \
      | sort
    

    Vix needs the mapped tarballs above and their .integrity files.

  4. Update Vix.

    • Change @release_tag in build_scripts/precompiler.exs.
    • If the package layout changed, update c_src/Makefile, make_precompiler_priv_paths in mix.exs, and the target mapping in build_scripts/precompiler.exs.
  5. Test locally.

    rm -rf _build/*/lib/vix cache/ priv/*
    
    ELIXIR_MAKE_CACHE_DIR="$(pwd)/cache" \
    VIX_COMPILATION_MODE=PRECOMPILED_LIBVIPS \
    mix test
    
    VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS mix test
    
  6. Follow the standard Vix release process.

Troubleshooting

Precompiled libvips Download Fails

Check the configured tag and asset URL:

grep '@release_tag' build_scripts/precompiler.exs

curl -I \
  https://github.com/akash-akya/sharp-libvips/releases/download/v8.18.3-rc1/sharp-libvips-linux-x64.tar.gz

If Erlang reports a crypto or SSL error before downloading, fix the local Erlang/OpenSSL installation. For example, an OTP build linked to libcrypto.so.1.1 will fail where that shared library is missing.

make deep_clean
make debug
make V=1 all

find priv/precompiled_libvips/lib -maxdepth 1 -name 'libvips-cpp*' -print
pkg-config --modversion vips
pkg-config --cflags --libs vips

Checksum Verification Fails

Regenerate checksums against the intended GitHub release artifacts:

rm -rf cache/ priv/* _build/*/lib/vix

ELIXIR_MAKE_CACHE_DIR="$(pwd)/cache" \
MIX_ENV=prod \
mix elixir_make.checksum --all