From 3fde230e65edc5790c6a3bc56758e24e208fc295 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 04:32:02 +0800 Subject: [PATCH 01/17] added support for using minimum nif version --- PRECOMPILATION_GUIDE.md | 99 +++++++++++++++++++++++++- lib/elixir_make/artefact.ex | 138 ++++++++++++++++++++++++++++++------ 2 files changed, 212 insertions(+), 25 deletions(-) diff --git a/PRECOMPILATION_GUIDE.md b/PRECOMPILATION_GUIDE.md index c4099ab..1521959 100644 --- a/PRECOMPILATION_GUIDE.md +++ b/PRECOMPILATION_GUIDE.md @@ -105,12 +105,101 @@ Directory structures and symbolic links are preserved. #### `make_precompiler_nif_versions` (optional config key) -The third optional config key is `make_precompiler_nif_versions`. The default value is +The third optional config key is `make_precompiler_nif_versions`. + +There are two ways configure `elixir_make` on how to compile and reuse precompiled binaries. + +##### Use minimum NIF version (recommended) +The first way is to use the minimum NIF version that is available to the current target. For example, if the NIF library +only uses functions and feature available in NIF version `2.14`, the following configuration should be set: ```elixir -[versions: ["#{:erlang.system_info(:nif_version)}"]] +make_precompiler_nif_versions: [ + use_minimum_version: true, + minimum_version: "2.14" +] +``` + +In this way, we can reuse the same precompiled binary for newer Erlang/OTP versions instead of compiling the same code over and over again. And if the user is using the NIF library on an older Erlang/OTP version, `elixir_make` will try to compile the NIF library for the target. + +Additionally, if the NIF library can optionally use functions and/or features that are only available in NIF version `2.17`, then the following configuration can be set to provide better support for newer Erlang/OTP versions while still providing support for older Erlang/OTP versions: + +```elixir +make_precompiler_nif_versions: [ + use_minimum_version: true, + minimum_version: fn _target, current_nif_version -> + if current_nif_version >= "2.17" do + "2.17" + else + "2.14" + end + end +] +``` + +The above example tells `elixir_make` that it can reuse binaries compiled with NIF version `2.17` if the current host supports it; otherwise, it will use binaries compiled with NIF version `2.14`. + +You can further change the above example to use different target name to control which feature set is available, for example, to use NIF version `2.17` for any `aarch64` targets and `2.14` for other targets: + +```elixir +make_precompiler_nif_versions: [ + use_minimum_version: true, + minimum_version: fn target, current_nif_version -> + if current_nif_version >= "2.17" and String.contains?(target, "aarch64") do + "2.17" + else + "2.14" + end + end +] +``` + +However, please remember to set the `versions_for_target` sub-key to inform `:elixir_make` that the precompiled artefacts may have different NIF versions for different targets. For example, + +```elixir +make_precompiler_nif_versions: [ + use_minimum_version: true, + minimum_version: fn target, current_nif_version -> + if current_nif_version >= "2.17" and String.contains?(target, "aarch64") do + "2.17" + else + "2.14" + end + end, + versions_for_target: fn target -> + if String.contains?(target, "aarch64") do + ["2.17", "2.14"] + else + ["2.14"] + end + end +] ``` +This information is used to determine all available precompiled artefacts and is used to fetch all of them and generate the checksum file. + +```elixir +make_precompiler_nif_versions: [ + use_minimum_version: true, + minimum_version: fn target, current_nif_version -> + if current_nif_version >= "2.17" do + "2.17" + else + "2.14" + end + end +] +``` + +##### Use exact NIF version +The second (and the old) way is to use the exact NIF version that is available to the current target. In this case, a list of supported NIF versions should be set in key `versions`. For example, + +```elixir +make_precompiler_nif_versions: [versions: ["2.14", "2.15", "2.16"]] +``` + +The above example tells `elixir_make` that precompiled artefacts are available for these NIF versions. If there's no precompiled artefacts available on the current host, `elixir_make` will try to compile the NIF library. + If you'd like to aim for an older NIF version, say `2.15` for Erlang/OTP 23 and 24, then you need to setup CI correspondingly and set the value of this key to `[versions: ["2.15", "2.16"]]`. This optional key will only be checked when downloading precompiled artefacts. 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`. @@ -127,6 +216,12 @@ defp target_available_for_nif_version?(target, nif_version) do end ``` +The default value for `make_precompiler_nif_versions` is + +```elixir +[versions: ["#{:erlang.system_info(:nif_version)}"]] +``` + ### (Optional) Customise Precompilation Targets To override the default configuration, please set the `cc_precompile` key in `project`. For example, diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 3e5d273..a43c045 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -66,12 +66,26 @@ defmodule ElixirMake.Artefact do end defp archive_filename(config, target, nif_version) do + precompiler_archive_filename = config[:make_precompiler_archive_filename] || :default + case config[:make_precompiler] do {:nif, _} -> - "#{config[:app]}-nif-#{nif_version}-#{target}-#{config[:version]}.tar.gz" + case precompiler_archive_filename do + func when is_function(func, 1) -> + func.(target: target) + + _ -> + "#{config[:app]}-nif-#{nif_version}-#{target}-#{config[:version]}.tar.gz" + end {type, _} -> - "#{config[:app]}-#{type}-#{target}-#{config[:version]}.tar.gz" + case precompiler_archive_filename do + func when is_function(func, 1) -> + func.(target: target) + + _ -> + "#{config[:app]}-#{type}-#{target}-#{config[:version]}.tar.gz" + end end end @@ -151,37 +165,87 @@ defmodule ElixirMake.Artefact do config[:make_precompiler_url] || Mix.raise("`make_precompiler_url` is not specified in `project`") + current_nif_version = "#{:erlang.system_info(:nif_version)}" + nif_versions = config[:make_precompiler_nif_versions] || - [versions: ["#{:erlang.system_info(:nif_version)}"]] + [versions: [current_nif_version]] - Enum.reduce(targets, [], fn target, archives -> - archive_filenames = - Enum.reduce(nif_versions[:versions], [], fn nif_version, acc -> - availability = nif_versions[:availability] + use_minimum_version = nif_versions[:use_minimum_version] || false - available? = - if is_function(availability, 2) do - availability.(target, nif_version) + if use_minimum_version do + minimum_version = + nif_versions[:minimum_version] || + raise ArgumentError, """ + `minimum_version` is not set in mix.exs while `use_minimum_version` is set to `true`. + `minimum_version` should be a string, e.g., "2.14" or a function that accepts two arguments, `target` and `current_nif_version`. + """ + + versions_for_target = nif_versions[:versions_for_target] + + if is_binary(minimum_version) or is_function(minimum_version, 2) do + Enum.reduce(targets, [], fn target, archives -> + target_versions = + if is_function(versions_for_target, 1) do + versions_for_target.(target) else - true + target_minimum_version = + case minimum_version do + v when is_binary(v) -> + v + + f when is_function(f, 2) -> + f.(target, current_nif_version) + end + + [target_minimum_version] end - if available? do - archive_filename = archive_filename(config, target, nif_version) + archive_filenames = + Enum.map(target_versions, fn nif_version_for_target -> + archive_filename = archive_filename(config, target, nif_version_for_target) - [ - {{target, nif_version}, + {{target, nif_version_for_target}, String.replace(url_template, "@{artefact_filename}", archive_filename)} - | acc - ] - else - acc - end + end) + + archive_filenames ++ archives end) + else + raise ArgumentError, """ + `minimum_version` should be a string, e.g., "2.14" or a function that accepts two arguments, `target` and `current_nif_version`. + got #{inspect(minimum_version)} + """ + end + else + Enum.reduce(targets, [], fn target, archives -> + archive_filenames = + Enum.reduce(nif_versions[:versions], [], fn nif_version, acc -> + availability = nif_versions[:availability] + + available? = + if is_function(availability, 2) do + availability.(target, nif_version) + else + true + end + + if available? do + archive_filename = archive_filename(config, target, nif_version) + + [ + {{target, nif_version}, + String.replace(url_template, "@{artefact_filename}", archive_filename)} + | acc + ] + else + acc + end + end) - archive_filenames ++ archives - end) + archive_filenames ++ archives + end) + end end @doc """ @@ -191,7 +255,35 @@ defmodule ElixirMake.Artefact do case precompiler.current_target() do {:ok, current_target} -> available_urls = available_target_urls(config, precompiler) - target_at_nif_version = {current_target, nif_version} + IO.puts("available_urls: #{inspect(available_urls)}") + + nif_versions = + config[:make_precompiler_nif_versions] || + [versions: ["#{:erlang.system_info(:nif_version)}"]] + + use_minimum_version = nif_versions[:use_minimum_version] || false + + target_at_nif_version = + if use_minimum_version do + minimum_version = + nif_versions[:minimum_version] || + raise ArgumentError, """ + `minimum_version` is not set in mix.exs while `use_minimum_version` is set to `true` + """ + + minimum_version = + case minimum_version do + v when is_binary(v) -> + v + + f when is_function(f, 2) -> + f.(current_target, nif_version) + end + + {current_target, minimum_version} + else + {current_target, nif_version} + end case List.keyfind(available_urls, target_at_nif_version, 0) do {^target_at_nif_version, download_url} -> From 4322f564b297e626d372a868df81d51fde06fd51 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 18:44:40 +0800 Subject: [PATCH 02/17] minor updates --- lib/mix/tasks/elixir_make.checksum.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/mix/tasks/elixir_make.checksum.ex b/lib/mix/tasks/elixir_make.checksum.ex index 088f915..dea63de 100644 --- a/lib/mix/tasks/elixir_make.checksum.ex +++ b/lib/mix/tasks/elixir_make.checksum.ex @@ -54,9 +54,11 @@ defmodule Mix.Tasks.ElixirMake.Checksum do Artefact.available_target_urls(config, precompiler) Keyword.get(options, :only_local) -> - case Artefact.current_target_url(config, precompiler, :erlang.system_info(:nif_version)) do + current_nif_version = "#{:erlang.system_info(:nif_version)}" + + case Artefact.current_target_url(config, precompiler, current_nif_version) do {:ok, target, url} -> - [{{target, "#{:erlang.system_info(:nif_version)}"}, url}] + [{{target, current_nif_version}, url}] {:error, {:unavailable_target, current_target, error}} -> recover = From c8429343643a7f29e7c0873b53ae9ef7c0fdbcea Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 18:51:08 +0800 Subject: [PATCH 03/17] enable minimum version by default --- lib/elixir_make/artefact.ex | 161 +++++++++++++++++------------------- 1 file changed, 74 insertions(+), 87 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index a43c045..76eb722 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -155,6 +155,31 @@ defmodule ElixirMake.Artefact do ## Archive/NIF urls + defp nif_version_to_tuple(nif_version) do + [major, minor | _] = String.split(nif_version, ".") + {String.to_integer(major), String.to_integer(minor)} + end + + defp find_nif_version(current_nif_version, versions) do + if current_nif_version in versions do + current_nif_version + else + {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, + {^major, candidate_minor} <- [nif_version_to_tuple(version)], + candidate_minor <= minor, + do: {minor - candidate_minor, version} + + case Enum.sort(candidates) do + [{_, version} | _] -> version + _ -> :no_candidates + end + end + end + @doc """ Returns all available {{target, nif_version}, url} pairs available. """ @@ -171,120 +196,82 @@ defmodule ElixirMake.Artefact do config[:make_precompiler_nif_versions] || [versions: [current_nif_version]] - use_minimum_version = nif_versions[:use_minimum_version] || false + versions = nif_versions[:versions] + versions_for_target = nif_versions[:versions_for_target] - if use_minimum_version do - minimum_version = - nif_versions[:minimum_version] || - raise ArgumentError, """ - `minimum_version` is not set in mix.exs while `use_minimum_version` is set to `true`. - `minimum_version` should be a string, e.g., "2.14" or a function that accepts two arguments, `target` and `current_nif_version`. - """ + Enum.reduce(targets, [], fn target, archives -> + target_versions = + if is_function(versions_for_target, 1) do + versions_for_target.(target) + else + versions + end - versions_for_target = nif_versions[:versions_for_target] + archive_filenames = + Enum.reduce(target_versions, [], fn nif_version_for_target, acc -> + availability = nif_versions[:availability] - if is_binary(minimum_version) or is_function(minimum_version, 2) do - Enum.reduce(targets, [], fn target, archives -> - target_versions = - if is_function(versions_for_target, 1) do - versions_for_target.(target) + available? = + if is_function(availability, 2) do + availability.(target, nif_version_for_target) else - target_minimum_version = - case minimum_version do - v when is_binary(v) -> - v - - f when is_function(f, 2) -> - f.(target, current_nif_version) - end - - [target_minimum_version] + true end - archive_filenames = - Enum.map(target_versions, fn nif_version_for_target -> - archive_filename = archive_filename(config, target, nif_version_for_target) + if available? do + archive_filename = archive_filename(config, target, nif_version_for_target) + [ {{target, nif_version_for_target}, String.replace(url_template, "@{artefact_filename}", archive_filename)} - end) - - archive_filenames ++ archives + | acc + ] + else + acc + end end) - else - raise ArgumentError, """ - `minimum_version` should be a string, e.g., "2.14" or a function that accepts two arguments, `target` and `current_nif_version`. - got #{inspect(minimum_version)} - """ - end - else - Enum.reduce(targets, [], fn target, archives -> - archive_filenames = - Enum.reduce(nif_versions[:versions], [], fn nif_version, acc -> - availability = nif_versions[:availability] - - available? = - if is_function(availability, 2) do - availability.(target, nif_version) - else - true - end - - if available? do - archive_filename = archive_filename(config, target, nif_version) - - [ - {{target, nif_version}, - String.replace(url_template, "@{artefact_filename}", archive_filename)} - | acc - ] - else - acc - end - end) - archive_filenames ++ archives - end) - end + archive_filenames ++ archives + end) end @doc """ Returns the url for the current target. """ - def current_target_url(config, precompiler, nif_version) do + def current_target_url(config, precompiler, current_nif_version) do case precompiler.current_target() do {:ok, current_target} -> - available_urls = available_target_urls(config, precompiler) - IO.puts("available_urls: #{inspect(available_urls)}") - nif_versions = config[:make_precompiler_nif_versions] || - [versions: ["#{:erlang.system_info(:nif_version)}"]] - - use_minimum_version = nif_versions[:use_minimum_version] || false + [versions: [current_nif_version]] - target_at_nif_version = - if use_minimum_version do - minimum_version = - nif_versions[:minimum_version] || - raise ArgumentError, """ - `minimum_version` is not set in mix.exs while `use_minimum_version` is set to `true` - """ + versions = nif_versions[:versions] + fallback_version = nif_versions[:fallback_version] + versions_for_target = nif_versions[:versions_for_target] - minimum_version = - case minimum_version do - v when is_binary(v) -> - v + target_versions = + if is_function(versions_for_target, 1) do + versions_for_target.(current_target) + else + versions + end - f when is_function(f, 2) -> - f.(current_target, nif_version) + nif_version_to_use = + case find_nif_version(current_nif_version, target_versions) do + :no_candidates -> + if is_function(fallback_version, 3) do + fallback_version.(current_target, current_nif_version, target_versions) + else + current_nif_version end - {current_target, minimum_version} - else - {current_target, nif_version} + version when is_binary(version) -> + version end + available_urls = available_target_urls(config, precompiler) + target_at_nif_version = {current_target, nif_version_to_use} + case List.keyfind(available_urls, target_at_nif_version, 0) do {^target_at_nif_version, download_url} -> {:ok, current_target, download_url} From f9bf1ea557c7c4d5ae1d3e3c63ddc5b04542917d Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 19:59:08 +0800 Subject: [PATCH 04/17] updated precompilation guide --- PRECOMPILATION_GUIDE.md | 142 +++++++++++++++++------------------- lib/elixir_make/artefact.ex | 2 +- 2 files changed, 69 insertions(+), 75 deletions(-) diff --git a/PRECOMPILATION_GUIDE.md b/PRECOMPILATION_GUIDE.md index 1521959..0129220 100644 --- a/PRECOMPILATION_GUIDE.md +++ b/PRECOMPILATION_GUIDE.md @@ -105,123 +105,117 @@ Directory structures and symbolic links are preserved. #### `make_precompiler_nif_versions` (optional config key) -The third optional config key is `make_precompiler_nif_versions`. +The third optional config key is `make_precompiler_nif_versions`, which configures `elixir_make` on how to compile and reuse precompiled binaries. -There are two ways configure `elixir_make` on how to compile and reuse precompiled binaries. +The default value for `make_precompiler_nif_versions` is + +```elixir +[versions: ["#{:erlang.system_info(:nif_version)}"]] +``` + +There're two sub-keys for `make_precompiler_nif_versions`: + +- `versions` +- `fallback_version` +- `versions_for_target` -##### Use minimum NIF version (recommended) -The first way is to use the minimum NIF version that is available to the current target. For example, if the NIF library -only uses functions and feature available in NIF version `2.14`, the following configuration should be set: +##### `versions` sub-key + +The `versions` sub-key is a list of NIF versions that the precompiled artefacts are available for: ```elixir make_precompiler_nif_versions: [ - use_minimum_version: true, - minimum_version: "2.14" + versions: ["2.16", "2.17"] ] ``` -In this way, we can reuse the same precompiled binary for newer Erlang/OTP versions instead of compiling the same code over and over again. And if the user is using the NIF library on an older Erlang/OTP version, `elixir_make` will try to compile the NIF library for the target. +The default behaviour is to use the exact NIF version that is available to the current target. In the example above, the precompiled binaries are available for NIF version `2.16` and `2.17`. + +- if the current host is using Erlang/OTP 24 or 25 (NIF version `2.16`), `elixir_make` will use the precompiled artefacts for NIF version `2.16`; +- if the current host is using Erlang/OTP 26 or 27 (NIF version `2.17`), `elixir_make` will use the precompiled artefacts for NIF version `2.17`; +- if the current host is using Erlang/OTP 23 or older (NIF version <= `2.16`), `elixir_make` will try to compile the NIF library for the target by default. +- if the current host is using Erlang/OTP 28 or newer (NIF version <= `2.18`), `elixir_make` will use the precompiled artefacts for NIF version `2.17` instead of compiling the NIF library for the target by default. + +##### `fallback_version` sub-key -Additionally, if the NIF library can optionally use functions and/or features that are only available in NIF version `2.17`, then the following configuration can be set to provide better support for newer Erlang/OTP versions while still providing support for older Erlang/OTP versions: +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 atom `:compile` to indicate that the NIF library should be compiled for the target. ```elixir make_precompiler_nif_versions: [ - use_minimum_version: true, - minimum_version: fn _target, current_nif_version -> - if current_nif_version >= "2.17" do - "2.17" + versions: ["2.16", "2.17"], + fallback_version: fn target, current_nif_version, target_versions -> + if current_nif_version >= "2.19" do + # compile from source for all targets if NIF version >= 2.19 + :compile else - "2.14" + # reuse precompiled artefacts for NIF version 2.17 + # for all aaarch64 targets + if String.contains?(target, "aarch64") do + "2.17" + else + # use NIF version 2.16 for other targets + "2.16" + end end end ] ``` -The above example tells `elixir_make` that it can reuse binaries compiled with NIF version `2.17` if the current host supports it; otherwise, it will use binaries compiled with NIF version `2.14`. +The above example also demonstrates how to use the `fallback_version` sub-key to provide better support for newer Erlang/OTP versions for some targets (in this case, targets with `aarch64` as part of their name) while still fallback to older NIF versions for other targets. + + +##### `versions_for_target` sub-key + +The `versions_for_target` sub-key is a function that provides a list of NIF versions that are available for a specific target. The function should accept one argument, `target`, and return a list of NIF versions that are available for the target. -You can further change the above example to use different target name to control which feature set is available, for example, to use NIF version `2.17` for any `aarch64` targets and `2.14` for other targets: +The default value of `versions_for_target` is retrieved from the `versions` sub-key. If the `versions` sub-key is not set, then the default value of `versions_for_target` is `["#{:erlang.system_info(:nif_version)}"]`. ```elixir -make_precompiler_nif_versions: [ - use_minimum_version: true, - minimum_version: fn target, current_nif_version -> - if current_nif_version >= "2.17" and String.contains?(target, "aarch64") do - "2.17" - else - "2.14" - end - end -] +Mix.Project.config()[:make_precompiler_nif_versions][:versions] || [] ``` -However, please remember to set the `versions_for_target` sub-key to inform `:elixir_make` that the precompiled artefacts may have different NIF versions for different targets. For example, +This information is used to determine all available precompiled artefacts and is used to fetch all of them and generate the checksum file. ```elixir make_precompiler_nif_versions: [ - use_minimum_version: true, - minimum_version: fn target, current_nif_version -> - if current_nif_version >= "2.17" and String.contains?(target, "aarch64") do - "2.17" + versions: ["2.16", "2.17"], + fallback_version: fn target, current_nif_version, target_versions -> + if current_nif_version >= "2.19" do + # compile from source for all targets if NIF version >= 2.19 + :compile else - "2.14" + # reuse precompiled artefacts for NIF version 2.17 + # for all aaarch64 targets + if String.contains?(target, "aarch64") do + "2.17" + else + # use NIF version 2.16 for other targets + "2.16" + end end end, versions_for_target: fn target -> if String.contains?(target, "aarch64") do - ["2.17", "2.14"] + ["2.16", "2.17"] else - ["2.14"] + ["2.16"] end end ] ``` -This information is used to determine all available precompiled artefacts and is used to fetch all of them and generate the checksum file. +##### Use a minimal NIF version for all targets + +However, if the NIF library only uses functions and/or features that are available since some NIF version, say `2.14`, for all targets, then we can simply set `versions` to `["2.14"]` and avoid compiling the same code over and over again for each NIF versions. ```elixir make_precompiler_nif_versions: [ - use_minimum_version: true, - minimum_version: fn target, current_nif_version -> - if current_nif_version >= "2.17" do - "2.17" - else - "2.14" - end - end + versions: ["2.14"] ] ``` -##### Use exact NIF version -The second (and the old) way is to use the exact NIF version that is available to the current target. In this case, a list of supported NIF versions should be set in key `versions`. For example, - -```elixir -make_precompiler_nif_versions: [versions: ["2.14", "2.15", "2.16"]] -``` - -The above example tells `elixir_make` that precompiled artefacts are available for these NIF versions. If there's no precompiled artefacts available on the current host, `elixir_make` will try to compile the NIF library. - -If you'd like to aim for an older NIF version, say `2.15` for Erlang/OTP 23 and 24, then you need to setup CI correspondingly and set the value of this key to `[versions: ["2.15", "2.16"]]`. This optional key will only be checked when downloading precompiled artefacts. - -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. - -```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 default value for `make_precompiler_nif_versions` is - -```elixir -[versions: ["#{:erlang.system_info(:nif_version)}"]] -``` - ### (Optional) Customise Precompilation Targets To override the default configuration, please set the `cc_precompile` key in `project`. For example, diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 76eb722..23eda55 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -243,7 +243,7 @@ defmodule ElixirMake.Artefact do {:ok, current_target} -> nif_versions = config[:make_precompiler_nif_versions] || - [versions: [current_nif_version]] + [versions: []] versions = nif_versions[:versions] fallback_version = nif_versions[:fallback_version] From 9fa65af9f3fb0d853cd5a13124cca15c928f562e Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:01:57 +0800 Subject: [PATCH 05/17] deprecate availability key in nif_versions --- PRECOMPILATION_GUIDE.md | 3 +-- lib/elixir_make/artefact.ex | 25 ++++++------------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/PRECOMPILATION_GUIDE.md b/PRECOMPILATION_GUIDE.md index 0129220..f465b0a 100644 --- a/PRECOMPILATION_GUIDE.md +++ b/PRECOMPILATION_GUIDE.md @@ -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"] ] # ... ] diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 23eda55..6c4e9f2 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -209,26 +209,13 @@ defmodule ElixirMake.Artefact do archive_filenames = Enum.reduce(target_versions, [], fn nif_version_for_target, acc -> - availability = nif_versions[:availability] + archive_filename = archive_filename(config, target, nif_version_for_target) - available? = - if is_function(availability, 2) do - availability.(target, nif_version_for_target) - else - true - end - - if available? do - archive_filename = archive_filename(config, target, nif_version_for_target) - - [ - {{target, nif_version_for_target}, - String.replace(url_template, "@{artefact_filename}", archive_filename)} - | acc - ] - else - acc - end + [ + {{target, nif_version_for_target}, + String.replace(url_template, "@{artefact_filename}", archive_filename)} + | acc + ] end) archive_filenames ++ archives From 347a76c879d722a4c8abf7f886a48a65b8953554 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:09:58 +0800 Subject: [PATCH 06/17] mix format --- lib/elixir_make/artefact.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 6c4e9f2..55e2720 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -213,7 +213,7 @@ defmodule ElixirMake.Artefact do [ {{target, nif_version_for_target}, - String.replace(url_template, "@{artefact_filename}", archive_filename)} + String.replace(url_template, "@{artefact_filename}", archive_filename)} | acc ] end) From a50a5c991bf1a8d713b545ea7d5db9b0785ac45a Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:40:51 +0800 Subject: [PATCH 07/17] test if fallback_version returns :compile --- lib/elixir_make/artefact.ex | 26 ++++++++++++++++---------- lib/mix/tasks/compile.elixir_make.ex | 3 +++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 55e2720..e4fb345 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -256,19 +256,25 @@ defmodule ElixirMake.Artefact do version end - available_urls = available_target_urls(config, precompiler) - target_at_nif_version = {current_target, nif_version_to_use} + case nif_version_to_use do + :compile -> + {:error, :compile_from_source} - case List.keyfind(available_urls, target_at_nif_version, 0) do - {^target_at_nif_version, download_url} -> - {:ok, current_target, download_url} + _ -> + available_urls = available_target_urls(config, precompiler) + target_at_nif_version = {current_target, nif_version_to_use} + + case List.keyfind(available_urls, target_at_nif_version, 0) do + {^target_at_nif_version, download_url} -> + {:ok, current_target, download_url} - nil -> - available_targets = Enum.map(available_urls, fn {target, _url} -> target end) + nil -> + available_targets = Enum.map(available_urls, fn {target, _url} -> target end) - {:error, - {:unavailable_target, current_target, - "cannot find download url for current target `#{inspect(current_target)}`. Available targets are: #{inspect(available_targets)}"}} + {:error, + {:unavailable_target, current_target, + "cannot find download url for current target `#{inspect(current_target)}`. Available targets are: #{inspect(available_targets)}"}} + end end {:error, msg} -> diff --git a/lib/mix/tasks/compile.elixir_make.ex b/lib/mix/tasks/compile.elixir_make.ex index 7dfe673..7da4311 100644 --- a/lib/mix/tasks/compile.elixir_make.ex +++ b/lib/mix/tasks/compile.elixir_make.ex @@ -167,6 +167,9 @@ defmodule Mix.Tasks.Compile.ElixirMake do :compile end + :compile_from_source -> + :compile + _ -> Mix.shell().error(""" Error happened while installing #{app} from precompiled binary: #{inspect(message)}. From a3aac0389a69b6400289012abf92ea5758f6b685 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:43:45 +0800 Subject: [PATCH 08/17] revert changes in archive_filename/3 --- lib/elixir_make/artefact.ex | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index e4fb345..c7d355c 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -66,26 +66,12 @@ defmodule ElixirMake.Artefact do end defp archive_filename(config, target, nif_version) do - precompiler_archive_filename = config[:make_precompiler_archive_filename] || :default - case config[:make_precompiler] do {:nif, _} -> - case precompiler_archive_filename do - func when is_function(func, 1) -> - func.(target: target) - - _ -> - "#{config[:app]}-nif-#{nif_version}-#{target}-#{config[:version]}.tar.gz" - end + "#{config[:app]}-nif-#{nif_version}-#{target}-#{config[:version]}.tar.gz" {type, _} -> - case precompiler_archive_filename do - func when is_function(func, 1) -> - func.(target: target) - - _ -> - "#{config[:app]}-#{type}-#{target}-#{config[:version]}.tar.gz" - end + "#{config[:app]}-#{type}-#{target}-#{config[:version]}.tar.gz" end end From 896aa8bc4f9ae80996a4c93b4b870cb410e407e3 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:46:53 +0800 Subject: [PATCH 09/17] revert changes related to `versions_for_target` --- lib/elixir_make/artefact.ex | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index c7d355c..244abf1 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -183,18 +183,10 @@ defmodule ElixirMake.Artefact do [versions: [current_nif_version]] versions = nif_versions[:versions] - versions_for_target = nif_versions[:versions_for_target] Enum.reduce(targets, [], fn target, archives -> - target_versions = - if is_function(versions_for_target, 1) do - versions_for_target.(target) - else - versions - end - archive_filenames = - Enum.reduce(target_versions, [], fn nif_version_for_target, acc -> + Enum.reduce(versions, [], fn nif_version_for_target, acc -> archive_filename = archive_filename(config, target, nif_version_for_target) [ @@ -220,20 +212,12 @@ defmodule ElixirMake.Artefact do versions = nif_versions[:versions] fallback_version = nif_versions[:fallback_version] - versions_for_target = nif_versions[:versions_for_target] - - target_versions = - if is_function(versions_for_target, 1) do - versions_for_target.(current_target) - else - versions - end nif_version_to_use = - case find_nif_version(current_nif_version, target_versions) do + case find_nif_version(current_nif_version, versions) do :no_candidates -> if is_function(fallback_version, 3) do - fallback_version.(current_target, current_nif_version, target_versions) + fallback_version.(current_target, current_nif_version, versions) else current_nif_version end From 085dba444c550e42f4e5512652f839b1c9da55d1 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:49:05 +0800 Subject: [PATCH 10/17] revert changes related to `availability` --- lib/elixir_make/artefact.ex | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 244abf1..8079e75 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -187,13 +187,26 @@ defmodule ElixirMake.Artefact do Enum.reduce(targets, [], fn target, archives -> archive_filenames = Enum.reduce(versions, [], fn nif_version_for_target, acc -> - archive_filename = archive_filename(config, target, nif_version_for_target) + availability = nif_versions[:availability] - [ - {{target, nif_version_for_target}, - String.replace(url_template, "@{artefact_filename}", archive_filename)} - | acc - ] + available? = + if is_function(availability, 2) do + availability.(target, nif_version_for_target) + else + true + end + + if available? do + archive_filename = archive_filename(config, target, nif_version_for_target) + + [ + {{target, nif_version_for_target}, + String.replace(url_template, "@{artefact_filename}", archive_filename)} + | acc + ] + else + acc + end end) archive_filenames ++ archives From 17e7c2a1d1affa0899650b62717ae5c77d4372e7 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 20:54:11 +0800 Subject: [PATCH 11/17] updated precompilation guide --- PRECOMPILATION_GUIDE.md | 48 +++++++++++------------------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/PRECOMPILATION_GUIDE.md b/PRECOMPILATION_GUIDE.md index f465b0a..39234a2 100644 --- a/PRECOMPILATION_GUIDE.md +++ b/PRECOMPILATION_GUIDE.md @@ -43,7 +43,8 @@ def project do make_precompiler_filename: "nif", make_precompiler_priv_paths: ["nif.*"], make_precompiler_nif_versions: [ - versions: ["2.14", "2.15", "2.16"] + versions: ["2.14", "2.15", "2.16"], + availability: &target_available_for_nif_version?/2 ] # ... ] @@ -116,7 +117,7 @@ There're two sub-keys for `make_precompiler_nif_versions`: - `versions` - `fallback_version` -- `versions_for_target` +- `availability` ##### `versions` sub-key @@ -164,45 +165,20 @@ make_precompiler_nif_versions: [ The above example also demonstrates how to use the `fallback_version` sub-key to provide better support for newer Erlang/OTP versions for some targets (in this case, targets with `aarch64` as part of their name) while still fallback to older NIF versions for other targets. +##### `availability` sub-key -##### `versions_for_target` sub-key +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`. -The `versions_for_target` sub-key is a function that provides a list of NIF versions that are available for a specific target. The function should accept one argument, `target`, and return a list of NIF versions that are available for the target. - -The default value of `versions_for_target` is retrieved from the `versions` sub-key. If the `versions` sub-key is not set, then the default value of `versions_for_target` is `["#{:erlang.system_info(:nif_version)}"]`. +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 -Mix.Project.config()[:make_precompiler_nif_versions][:versions] || [] -``` - -This information is used to determine all available precompiled artefacts and is used to fetch all of them and generate the checksum file. - -```elixir -make_precompiler_nif_versions: [ - versions: ["2.16", "2.17"], - fallback_version: fn target, current_nif_version, target_versions -> - if current_nif_version >= "2.19" do - # compile from source for all targets if NIF version >= 2.19 - :compile - else - # reuse precompiled artefacts for NIF version 2.17 - # for all aaarch64 targets - if String.contains?(target, "aarch64") do - "2.17" - else - # use NIF version 2.16 for other targets - "2.16" - end - end - end, - versions_for_target: fn target -> - if String.contains?(target, "aarch64") do - ["2.16", "2.17"] - else - ["2.16"] - end +defp target_available_for_nif_version?(target, nif_version) do + if String.contains?(target, "windows") do + nif_version == "2.16" + else + true end -] +end ``` ##### Use a minimal NIF version for all targets From 76417c67c012511aeb791d59b7d7b3f0c7595b4f Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 21:08:22 +0800 Subject: [PATCH 12/17] Update lib/elixir_make/artefact.ex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Valim --- lib/elixir_make/artefact.ex | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 8079e75..2689f2f 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -224,20 +224,8 @@ defmodule ElixirMake.Artefact do [versions: []] versions = nif_versions[:versions] - fallback_version = nif_versions[:fallback_version] - - nif_version_to_use = - case find_nif_version(current_nif_version, versions) do - :no_candidates -> - if is_function(fallback_version, 3) do - fallback_version.(current_target, current_nif_version, versions) - else - current_nif_version - end - - version when is_binary(version) -> - version - end + fallback_version = nif_versions[:fallback_version] || &find_nif_version/3 + nif_version_to_use = fallback_version.(current_target, current_nif_version, versions) case nif_version_to_use do :compile -> From a410b84bd2df0b160c8f8bfbb853e5cf928b3114 Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 21:12:41 +0800 Subject: [PATCH 13/17] revert commit a50a5c99 --- lib/elixir_make/artefact.ex | 26 ++++++++++---------------- lib/mix/tasks/compile.elixir_make.ex | 3 --- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 2689f2f..af67163 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -227,25 +227,19 @@ defmodule ElixirMake.Artefact do fallback_version = nif_versions[:fallback_version] || &find_nif_version/3 nif_version_to_use = fallback_version.(current_target, current_nif_version, versions) - case nif_version_to_use do - :compile -> - {:error, :compile_from_source} + available_urls = available_target_urls(config, precompiler) + target_at_nif_version = {current_target, nif_version_to_use} - _ -> - available_urls = available_target_urls(config, precompiler) - target_at_nif_version = {current_target, nif_version_to_use} - - case List.keyfind(available_urls, target_at_nif_version, 0) do - {^target_at_nif_version, download_url} -> - {:ok, current_target, download_url} + case List.keyfind(available_urls, target_at_nif_version, 0) do + {^target_at_nif_version, download_url} -> + {:ok, current_target, download_url} - nil -> - available_targets = Enum.map(available_urls, fn {target, _url} -> target end) + nil -> + available_targets = Enum.map(available_urls, fn {target, _url} -> target end) - {:error, - {:unavailable_target, current_target, - "cannot find download url for current target `#{inspect(current_target)}`. Available targets are: #{inspect(available_targets)}"}} - end + {:error, + {:unavailable_target, current_target, + "cannot find download url for current target `#{inspect(current_target)}`. Available targets are: #{inspect(available_targets)}"}} end {:error, msg} -> diff --git a/lib/mix/tasks/compile.elixir_make.ex b/lib/mix/tasks/compile.elixir_make.ex index 7da4311..7dfe673 100644 --- a/lib/mix/tasks/compile.elixir_make.ex +++ b/lib/mix/tasks/compile.elixir_make.ex @@ -167,9 +167,6 @@ defmodule Mix.Tasks.Compile.ElixirMake do :compile end - :compile_from_source -> - :compile - _ -> Mix.shell().error(""" Error happened while installing #{app} from precompiled binary: #{inspect(message)}. From 9529ff10cb256ba7a43a95c8eea5f79b2859dd9b Mon Sep 17 00:00:00 2001 From: Cocoa Date: Sat, 9 Mar 2024 21:14:56 +0800 Subject: [PATCH 14/17] fix find_nif_version --- lib/elixir_make/artefact.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index af67163..565236a 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -146,7 +146,7 @@ defmodule ElixirMake.Artefact do {String.to_integer(major), String.to_integer(minor)} end - defp find_nif_version(current_nif_version, versions) do + defp find_nif_version(_current_target, current_nif_version, versions) do if current_nif_version in versions do current_nif_version else @@ -161,7 +161,7 @@ defmodule ElixirMake.Artefact do case Enum.sort(candidates) do [{_, version} | _] -> version - _ -> :no_candidates + _ -> current_nif_version end end end @@ -224,7 +224,7 @@ defmodule ElixirMake.Artefact do [versions: []] versions = nif_versions[:versions] - fallback_version = nif_versions[:fallback_version] || &find_nif_version/3 + fallback_version = nif_versions[:fallback_version] || (&find_nif_version/3) nif_version_to_use = fallback_version.(current_target, current_nif_version, versions) available_urls = available_target_urls(config, precompiler) From d59f1e033ee8d7548fbad3aceb8aac1d69f2784a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 9 Mar 2024 16:41:26 +0100 Subject: [PATCH 15/17] Update PRECOMPILATION_GUIDE.md --- PRECOMPILATION_GUIDE.md | 48 +++++++---------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/PRECOMPILATION_GUIDE.md b/PRECOMPILATION_GUIDE.md index 39234a2..8ed891f 100644 --- a/PRECOMPILATION_GUIDE.md +++ b/PRECOMPILATION_GUIDE.md @@ -113,7 +113,7 @@ The default value for `make_precompiler_nif_versions` is [versions: ["#{:erlang.system_info(:nif_version)}"]] ``` -There're two sub-keys for `make_precompiler_nif_versions`: +There're three sub-keys for `make_precompiler_nif_versions`: - `versions` - `fallback_version` @@ -125,45 +125,23 @@ The `versions` sub-key is a list of NIF versions that the precompiled artefacts ```elixir make_precompiler_nif_versions: [ - versions: ["2.16", "2.17"] + versions: ["2.15", "2.16"] ] ``` -The default behaviour is to use the exact NIF version that is available to the current target. In the example above, the precompiled binaries are available for NIF version `2.16` and `2.17`. +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`; - if the current host is using Erlang/OTP 24 or 25 (NIF version `2.16`), `elixir_make` will use the precompiled artefacts for NIF version `2.16`; -- if the current host is using Erlang/OTP 26 or 27 (NIF version `2.17`), `elixir_make` will use the precompiled artefacts for NIF version `2.17`; -- if the current host is using Erlang/OTP 23 or older (NIF version <= `2.16`), `elixir_make` will try to compile the NIF library for the target by default. -- if the current host is using Erlang/OTP 28 or newer (NIF version <= `2.18`), `elixir_make` will use the precompiled artefacts for NIF version `2.17` instead of compiling the NIF library for the target by default. +- if the current host is using Erlang/OTP 26 or newer (NIF version `2.17`), `elixir_make` will fallback to the precompiled artefacts for NIF version `2.16`; + +If the current host is using Erlang/OTP with a new major Erlang NIF version (NIF version `3.0`) or anything earlier than the precompiled versions (`2.14`), `elixir_make` will compile from scratch. ##### `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 atom `:compile` to indicate that the NIF library should be compiled for the target. - -```elixir -make_precompiler_nif_versions: [ - versions: ["2.16", "2.17"], - fallback_version: fn target, current_nif_version, target_versions -> - if current_nif_version >= "2.19" do - # compile from source for all targets if NIF version >= 2.19 - :compile - else - # reuse precompiled artefacts for NIF version 2.17 - # for all aaarch64 targets - if String.contains?(target, "aarch64") do - "2.17" - else - # use NIF version 2.16 for other targets - "2.16" - end - end - end -] -``` - -The above example also demonstrates how to use the `fallback_version` sub-key to provide better support for newer Erlang/OTP versions for some targets (in this case, targets with `aarch64` as part of their name) while still fallback to older NIF versions for other targets. +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`. ##### `availability` sub-key @@ -181,16 +159,6 @@ defp target_available_for_nif_version?(target, nif_version) do end ``` -##### Use a minimal NIF version for all targets - -However, if the NIF library only uses functions and/or features that are available since some NIF version, say `2.14`, for all targets, then we can simply set `versions` to `["2.14"]` and avoid compiling the same code over and over again for each NIF versions. - -```elixir -make_precompiler_nif_versions: [ - versions: ["2.14"] -] -``` - ### (Optional) Customise Precompilation Targets To override the default configuration, please set the `cc_precompile` key in `project`. For example, From e72165c55a4697214d11bff2c9a2c698a1655f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 9 Mar 2024 16:43:46 +0100 Subject: [PATCH 16/17] Update lib/elixir_make/artefact.ex --- lib/elixir_make/artefact.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 565236a..2ecaab6 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -151,6 +151,7 @@ defmodule ElixirMake.Artefact do current_nif_version else {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 = From 7d47851567a270ad3bc46e75fdfe7316a5369f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 9 Mar 2024 16:46:19 +0100 Subject: [PATCH 17/17] Apply suggestions from code review --- lib/elixir_make/artefact.ex | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/elixir_make/artefact.ex b/lib/elixir_make/artefact.ex index 2ecaab6..ca84591 100644 --- a/lib/elixir_make/artefact.ex +++ b/lib/elixir_make/artefact.ex @@ -146,24 +146,20 @@ defmodule ElixirMake.Artefact do {String.to_integer(major), String.to_integer(minor)} end - defp find_nif_version(_current_target, current_nif_version, versions) do - if current_nif_version in versions do - current_nif_version - else - {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, - {^major, candidate_minor} <- [nif_version_to_tuple(version)], - candidate_minor <= minor, - do: {minor - candidate_minor, version} - - case Enum.sort(candidates) do - [{_, version} | _] -> version - _ -> current_nif_version - end + defp fallback_version(_current_target, current_nif_version, versions) do + {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, + {^major, candidate_minor} <- [nif_version_to_tuple(version)], + candidate_minor <= minor, + do: {minor - candidate_minor, version} + + case Enum.sort(candidates) do + [{_, version} | _] -> version + _ -> current_nif_version end end @@ -225,8 +221,14 @@ defmodule ElixirMake.Artefact do [versions: []] versions = nif_versions[:versions] - fallback_version = nif_versions[:fallback_version] || (&find_nif_version/3) - nif_version_to_use = fallback_version.(current_target, current_nif_version, versions) + + 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) + end available_urls = available_target_urls(config, precompiler) target_at_nif_version = {current_target, nif_version_to_use}