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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Bump upstream Vize crates 0.109 → 0.112.
- Fix CSS AST print round-trip for `image-set(...)`.
- Automatically build NIF from source on targets without precompiled artifacts.

## 0.11.0

Expand Down
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,33 @@ def deps do
end
```

Requires a Rust toolchain (`rustup` recommended). The NIF compiles automatically on `mix compile`.
Vize uses precompiled NIFs on supported targets. On targets without a
precompiled artifact, Vize automatically builds the NIF from source, if
required tools are available.

Source builds require a Rust toolchain (`rustup` recommended) and Rustler
in your app's deps:

```elixir
def deps do
[
{:vize, "~> 0.11.0"},
{:rustler, "~> 0.37", optional: true}
]
end
```

To force a source build on any target:

```sh
VIZE_EX_BUILD=1 mix deps.compile vize --force
```

You can also make source builds permanent with compile-time config:

```elixir
config :rustler_precompiled, :force_build, vize: true
```

## Usage

Expand Down
70 changes: 62 additions & 8 deletions lib/vize/native.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,72 @@
defmodule Vize.Native.Build do
@moduledoc false

@targets ~w(
aarch64-apple-darwin
aarch64-unknown-linux-gnu
x86_64-apple-darwin
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
)

def targets, do: @targets

def force_build?(env_value, config_value, target \\ current_target()) do
env_value in ["1", "true"] or config_value == true or target not in @targets
end

def ensure_rustler_available!(force_build?, rustler_loaded? \\ Code.ensure_loaded?(Rustler))
def ensure_rustler_available!(false, _rustler_loaded?), do: :ok

def ensure_rustler_available!(true, rustler_loaded?) do
unless rustler_loaded? do
raise """
Vize needs to compile its Rust NIF from source, but Rustler is not available.

Add Rustler to your application's dependencies:

{:rustler, "~> 0.37", optional: true}

Then run:

mix deps.get
mix deps.compile vize --force
"""
end

:ok
end

defp current_target do
case RustlerPrecompiled.target() do
{:ok, "nif-" <> target} ->
target
|> String.split("-", parts: 2)
|> List.last()

{:error, _reason} ->
nil
end
end
end

defmodule Vize.Native do
version = Mix.Project.config()[:version]

force_build? =
Vize.Native.Build.force_build?(
System.get_env("VIZE_EX_BUILD"),
Application.compile_env(:rustler_precompiled, [:force_build, :vize], false)
)

Vize.Native.Build.ensure_rustler_available!(force_build?)

use RustlerPrecompiled,
otp_app: :vize,
crate: "vize_ex_nif",
base_url: "https://github.com/elixir-volt/vize_ex/releases/download/v#{version}",
force_build: System.get_env("VIZE_EX_BUILD") in ["1", "true"],
targets: ~w(
aarch64-apple-darwin
aarch64-unknown-linux-gnu
x86_64-apple-darwin
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
),
force_build: force_build?,
targets: Vize.Native.Build.targets(),
version: version

@spec parse_sfc_nif(String.t()) :: {:ok, map()} | {:error, String.t()}
Expand Down
20 changes: 20 additions & 0 deletions test/vize_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ defmodule VizeTest do
defp rewrite_css_url(%{"url" => from} = node, from, to), do: %{node | "url" => to}
defp rewrite_css_url(node, _from, _to), do: node

describe "native build config" do
test "honors explicit source build settings" do
assert Vize.Native.Build.force_build?("1", false, "x86_64-unknown-linux-gnu")
assert Vize.Native.Build.force_build?("true", false, "x86_64-unknown-linux-gnu")
assert Vize.Native.Build.force_build?(nil, true, "x86_64-unknown-linux-gnu")
refute Vize.Native.Build.force_build?(nil, false, "x86_64-unknown-linux-gnu")
end

test "source builds when no precompiled target is available" do
assert Vize.Native.Build.force_build?(nil, false, "x86_64-unknown-freebsd")
refute Vize.Native.Build.force_build?(nil, false, "x86_64-unknown-linux-gnu")
end

test "raises a clear error when source build needs Rustler" do
assert_raise RuntimeError, ~r/Rustler is not available/, fn ->
Vize.Native.Build.ensure_rustler_available!(true, false)
end
end
end

describe "parse_sfc/1" do
test "parses template block" do
{:ok, descriptor} = Vize.parse_sfc(@simple_sfc)
Expand Down