Skip to content

Commit

Permalink
Deprecate availability key in nif_versions (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
cocoa-xu committed Mar 10, 2024
1 parent 43f29f8 commit fa66358
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 29 deletions.
47 changes: 25 additions & 22 deletions PRECOMPILATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ def project do
make_precompiler_filename: "nif",
make_precompiler_priv_paths: ["nif.*"],
make_precompiler_nif_versions: [
versions: ["2.14", "2.15", "2.16"],
availability: &target_available_for_nif_version?/2
versions: ["2.14", "2.15", "2.16"]
]
# ...
]
Expand Down Expand Up @@ -113,22 +112,40 @@ The default value for `make_precompiler_nif_versions` is
[versions: ["#{:erlang.system_info(:nif_version)}"]]
```

There're three sub-keys for `make_precompiler_nif_versions`:
There're two sub-keys for `make_precompiler_nif_versions`:

- `versions`
- `fallback_version`
- `availability`

##### `versions` sub-key

The `versions` sub-key is a list of NIF versions that the precompiled artefacts are available for:
The `versions` sub-key is either a list of NIF versions of a function that returns a list of NIF versions that the precompiled artefacts are available for:

```elixir
make_precompiler_nif_versions: [
versions: ["2.15", "2.16"]
]
```

The above example tells `:elixir_make` that all targets have precompiled artefacts for NIF version `2.15` and `2.16`.

For some platforms maybe we only have precompiled artefacts after a certain NIF version, say for x86_64 Windows we have precompiled artefacts available when NIF version >= `2.16` while other platforms have precompiled artefacts available from NIF version >= `2.15`.

In such case we can inform `:elixir_make` that Windows targets don't have precompiled artefacts available except for NIF version `2.16` by passing a function to the `availability` sub-key. This function should accept two arguments, `target` and `nif_version`, and returns a boolean value indicating whether the precompiled artefacts for the target and NIF version are available.

```elixir
make_precompiler_nif_versions: [
versions: fn opts ->
target = opts.target
if String.contains?(target, "windows") do
["2.16"]
else
["2.15", "2.16"]
end
end
]
```

The default behaviour is to use the exact NIF version that is available to the current target. If one is not available, it may fallback (see `fallback_version` next) to the highest matching major version prior to the current version. For example:

- if the current host is using Erlang/OTP 23 (NIF version `2.15`), `elixir_make` will use the precompiled artefacts for NIF version `2.15`;
Expand All @@ -139,25 +156,11 @@ If the current host is using Erlang/OTP with a new major Erlang NIF version (NIF

##### `fallback_version` sub-key

The behaviour when `elixir_make` cannot find the exact NIF version of the precompiled binary can be customized by setting the `fallback_version` sub-key. The value of the `fallback_version` sub-key should be a function that accepts three arguments, `target`, `current_nif_version` and `target_versions`. The `target` is the target triplet (or other name format, defined by the precompiler of your choice), `current_nif_version` is the NIF version on the current host, and `target_versions` is a list of NIF versions that are available to the target.

The `fallback_version` function should return either the NIF version that `elixir_make` should use from the `target_versions` list or the `current_nif_version`.
The behaviour when `elixir_make` cannot find the exact NIF version of the precompiled binary can be customized by setting the `fallback_version` sub-key.

##### `availability` sub-key
The value of the `fallback_version` sub-key should be a function that accepts one argument, `opts`, which is a map that include one key `target`. The `target` is the target triplet (or other naming format, defined by the precompiler of your choice).

For some platforms maybe we only have precompiled artefacts after a certain NIF version, say for x86_64 Windows we have precompiled artefacts available when NIF version >= `2.16` while other platforms have precompiled artefacts available from NIF version >= `2.15`.

In such case we can inform `:elixir_make` that Windows targets don't have precompiled artefacts available except for NIF version `2.16` by passing a function to the `availability` sub-key. This function should accept two arguments, `target` and `nif_version`, and returns a boolean value indicating whether the precompiled artefacts for the target and NIF version are available.

```elixir
defp target_available_for_nif_version?(target, nif_version) do
if String.contains?(target, "windows") do
nif_version == "2.16"
else
true
end
end
```
The `fallback_version` function should return the NIF version that is available and should be chosen for the current target.

### (Optional) Customise Precompilation Targets

Expand Down
30 changes: 23 additions & 7 deletions lib/elixir_make/artefact.ex
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,14 @@ defmodule ElixirMake.Artefact do
{String.to_integer(major), String.to_integer(minor)}
end

defp fallback_version(_current_target, current_nif_version, versions) do
defp fallback_version(opts) do
current_nif_version = "#{:erlang.system_info(:nif_version)}"
{major, minor} = nif_version_to_tuple(current_nif_version)

# Get all matching major versions, earlier than the current version
# and their distance. We want the closest (smallest distance).
candidates =
for version <- versions,
for version <- opts.versions,
{^major, candidate_minor} <- [nif_version_to_tuple(version)],
candidate_minor <= minor,
do: {minor - candidate_minor, version}
Expand All @@ -163,6 +164,16 @@ defmodule ElixirMake.Artefact do
end
end

defp get_versions_for_target(versions, current_target) do
case versions do
version_list when is_list(version_list) ->
version_list

version_func when is_function(version_func, 1) ->
version_func.(%{target: current_target})
end
end

@doc """
Returns all available {{target, nif_version}, url} pairs available.
"""
Expand All @@ -179,15 +190,19 @@ defmodule ElixirMake.Artefact do
config[:make_precompiler_nif_versions] ||
[versions: [current_nif_version]]

versions = nif_versions[:versions]

Enum.reduce(targets, [], fn target, archives ->
versions = get_versions_for_target(nif_versions[:versions], target)

archive_filenames =
Enum.reduce(versions, [], fn nif_version_for_target, acc ->
availability = nif_versions[:availability]

available? =
if is_function(availability, 2) do
IO.warn(
":availability key in elixir_make is deprecated, pass a function as :versions instead"
)

availability.(target, nif_version_for_target)
else
true
Expand Down Expand Up @@ -220,14 +235,15 @@ defmodule ElixirMake.Artefact do
config[:make_precompiler_nif_versions] ||
[versions: []]

versions = nif_versions[:versions]
versions = get_versions_for_target(nif_versions[:versions], current_target)

nif_version_to_use =
if current_nif_version in versions do
current_nif_version
else
fallback_version = nif_versions[:fallback_version] || (&fallback_version/3)
fallback_version.(current_target, current_nif_version, versions)
fallback_version = nif_versions[:fallback_version] || (&fallback_version/1)
opts = %{target: current_target, versions: versions}
fallback_version.(opts)
end

available_urls = available_target_urls(config, precompiler)
Expand Down

0 comments on commit fa66358

Please sign in to comment.