Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions examples/BURRITO_SELECTIVE_NIF_SKIP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Burrito Selective NIF Skip Patch

## Problem

Burrito 1.5.0 only supports `skip_nifs: true` (skip ALL NIFs) or `skip_nifs: false` (recompile ALL NIFs). This is problematic when you need to:
- Use precompiled NIFs for some dependencies (e.g., vix with bundled libvips for portability)
- Still allow Burrito to cross-compile other NIFs (e.g., exqlite, bcrypt_elixir)

## Solution

This patch modifies `lib/steps/patch/recompile_nifs.ex` to support `skip_nifs` as a list of atoms, enabling selective NIF recompilation control.

## Usage

### Before (Burrito 1.5.0)
```elixir
# In mix.exs
burrito: [
targets: [
linux_x86: [
os: :linux,
cpu: :x86_64,
skip_nifs: true # Skips ALL NIFs - breaks exqlite, bcrypt, etc.
]
]
]
```

### After (With Patch)
```elixir
# In mix.exs
burrito: [
targets: [
linux_x86: [
os: :linux,
cpu: :x86_64,
skip_nifs: [:vix, :html5ever] # Skips ONLY these NIFs, recompiles others
]
]
]
```

## Supported Formats

After applying this patch, `skip_nifs` accepts:
- `false` - Recompile all NIFs (default behavior)
- `true` - Skip all NIFs (original behavior)
- `[:atom1, :atom2]` - Skip only specified NIFs, recompile others (NEW!)

## Real-World Example: Vix with Precompiled libvips

### Problem Context
- Vix provides precompiled NIFs with bundled libvips for portability
- Burrito's cross-compilation deletes these precompiled binaries and attempts recompilation
- Recompilation requires system libvips-dev, defeating portability

### Solution
1. Install precompiled vix before building:
```bash
./install_vix_precompiled.sh # Downloads from github.com/akash-akya/vix releases
```

2. Configure Burrito to skip vix recompilation:
```elixir
burrito: [
targets: [
linux_x86: [
os: :linux,
cpu: :x86_64,
skip_nifs: [:vix, :html5ever]
]
]
]
```

3. Build with Burrito:
```bash
mix release
```

Result: Portable binary with vix's bundled libvips works on ANY x86_64 Linux system!

## Technical Details

### Changed Behavior
**File:** `lib/steps/patch/recompile_nifs.ex`
**Function:** `execute/1`

**Before:**
```elixir
skip_nifs? = Keyword.get(context.target.qualifiers, :skip_nifs, false)

if context.target.cross_build and not skip_nifs? do
nif_sniff()
|> Enum.each(fn dep ->
maybe_recompile_nif(dep, ...)
end)
end
```

**After:**
```elixir
skip_nifs_config = Keyword.get(context.target.qualifiers, :skip_nifs, false)

skip_nifs_list = case skip_nifs_config do
true -> :all
false -> []
list when is_list(list) -> list
_ -> []
end

if context.target.cross_build and skip_nifs_list != :all do
nif_sniff()
|> Enum.each(fn dep ->
should_skip = skip_nifs_list == :all or (elem(dep, 0) in skip_nifs_list)

unless should_skip do
maybe_recompile_nif(dep, ...)
end
end)
end
```

### Backward Compatibility
✅ **Fully backward compatible!**
- `skip_nifs: false` - Works as before (recompile all)
- `skip_nifs: true` - Works as before (skip all)
- `skip_nifs: [:vix]` - NEW feature (skip specific)

## Testing

After applying the patch:

1. Configure selective NIF skipping in `mix.exs`
2. Install precompiled NIFs you want to preserve
3. Run `MIX_ENV=prod mix release`
4. Verify build output:
- Should see "Going to recompile NIF" for non-skipped NIFs (exqlite, bcrypt)
- Should NOT see recompilation messages for skipped NIFs (vix, html5ever)

## Why This Matters

### Portability
Precompiled NIFs with bundled native libraries (like vix's libvips) enable true portability:
- ✅ Binary works on ANY x86_64 Linux (Ubuntu, Fedora, Debian, Alpine, etc.)
- ✅ No system dependencies required on target machine
- ✅ No "library not found" errors

### Sustainability
Without this patch, you'd need hacky workarounds:
- ❌ Manually copying precompiled NIFs after Burrito deletes them
- ❌ Patching Burrito's NIF detection to not detect certain packages
- ❌ Using `skip_nifs: true` and manually managing ALL NIFs

## License

This patch is provided as-is for the Burrito project (MIT License).

## Related Issues

This solves problems when:
- Using vix (Elixir libvips bindings) with Burrito
- Using rust NIFs that provide precompiled binaries
- Targeting systems without dev libraries installed
- Creating truly portable standalone Elixir applications

## Credits

Created: November 4, 2025
Context: Building portable Silicon Brain releases with vix image processing
Problem: Burrito deleted precompiled vix with bundled libvips during cross-compilation
Solution: Enable selective NIF skip to preserve portable precompiled binaries
37 changes: 25 additions & 12 deletions lib/steps/patch/recompile_nifs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,38 @@ defmodule Burrito.Steps.Patch.RecompileNIFs do
cxxflags = Keyword.get(context.target.qualifiers, :nif_cxxflags, "")
nif_env = Keyword.get(context.target.qualifiers, :nif_env, [])
nif_make_args = Keyword.get(context.target.qualifiers, :nif_make_args, [])
skip_nifs? = Keyword.get(context.target.qualifiers, :skip_nifs, false)
skip_nifs_config = Keyword.get(context.target.qualifiers, :skip_nifs, false)

# Support both boolean (skip all) and list (skip specific NIFs)
skip_nifs_list = case skip_nifs_config do
true -> :all
false -> []
list when is_list(list) -> list
_ -> []
end

if context.target.cross_build and not skip_nifs? do
if context.target.cross_build and skip_nifs_list != :all do
triplet = Target.make_triplet(context.target)

{:local_unpacked, path: erts_location} = context.target.erts_source

nif_sniff()
|> Enum.each(fn dep ->
maybe_recompile_nif(
dep,
context.work_dir,
erts_location,
triplet,
cflags,
cxxflags,
nif_env,
nif_make_args
)
should_skip = skip_nifs_list == :all or
(elem(dep, 0) in skip_nifs_list)

unless should_skip do
maybe_recompile_nif(
dep,
context.work_dir,
erts_location,
triplet,
cflags,
cxxflags,
nif_env,
nif_make_args
)
end
end)
end

Expand Down