update
This commit is contained in:
3
phoenix/deps/elixir_make/.formatter.exs
Normal file
3
phoenix/deps/elixir_make/.formatter.exs
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{lib,test}/**/*.{ex,exs}"]
|
||||
]
|
||||
BIN
phoenix/deps/elixir_make/.hex
Normal file
BIN
phoenix/deps/elixir_make/.hex
Normal file
Binary file not shown.
88
phoenix/deps/elixir_make/CHANGELOG.md
Normal file
88
phoenix/deps/elixir_make/CHANGELOG.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Changelog
|
||||
|
||||
## v0.9.0 (2024-11-12)
|
||||
|
||||
* Rely on certificate stores provided by Erlang/OTP 25+
|
||||
* Support setting `:force_build` opts in config:
|
||||
|
||||
```elixir
|
||||
config :elixir_make, :force_build, app1: true, app2: false
|
||||
```
|
||||
|
||||
## v0.8.4 (2024-06-04)
|
||||
|
||||
* Support configuring `make_precompiler_downloader`
|
||||
* Support DragonFlyBSD
|
||||
* Support non-UTF8 encoding from compilation (may happen on Windows)
|
||||
|
||||
## v0.8.3 (2024-03-24)
|
||||
|
||||
* Support storing checksum of individial precompilation artefacts
|
||||
|
||||
## v0.8.2 (2024-03-10)
|
||||
|
||||
* Fix checksum error when checksuming NIF version
|
||||
|
||||
## v0.8.1 (2024-03-10)
|
||||
|
||||
* Fix checksum error when falling back to a previous NIF version
|
||||
|
||||
## v0.8.0 (2024-03-10)
|
||||
|
||||
* Fallback to the previous compatible NIF version by default
|
||||
* Deprecate availability in favor of passing a function to `:versions`
|
||||
|
||||
## v0.7.8 (2024-01-17)
|
||||
|
||||
* List certifi as an optional dependency
|
||||
|
||||
## v0.7.7 (2023-06-01)
|
||||
|
||||
* Fix compiler in umbrella apps
|
||||
|
||||
## v0.7.6 (2023-03-17)
|
||||
|
||||
* Do not display error message when a target is unavailable
|
||||
* Allow usage of castore 1.0
|
||||
|
||||
## v0.7.5 (2023-02-18)
|
||||
|
||||
* Support precompiling for multiple NIF versions
|
||||
|
||||
## v0.7.4 (2023-02-17)
|
||||
|
||||
* Preload Erlang/OTP applications for Elixir v1.15+
|
||||
|
||||
## v0.7.3 (2022-12-27)
|
||||
|
||||
* Add `post_precompile_target` to `ElixirMake.Precompiler` behaviour
|
||||
|
||||
## v0.7.2 (2022-12-14)
|
||||
|
||||
* Allow precompiler to configure behaviour for unavailable targets
|
||||
|
||||
## v0.7.1 (2022-12-07)
|
||||
|
||||
* Use CACerts from Erlang/OTP 25 if available
|
||||
|
||||
## v0.7.0 (2022-12-02)
|
||||
|
||||
* Support precompilation with custom precompilers
|
||||
* Don't pass default Erlang environment variables into make
|
||||
|
||||
## v0.6.3 (2021-10-19)
|
||||
|
||||
* Fallback to `make` if `nmake` is not available on Windows.
|
||||
|
||||
## v0.6.2 (2020-12-03)
|
||||
|
||||
* Fix permissions for some files in the repository.
|
||||
|
||||
## v0.6.1 (2020-09-07)
|
||||
|
||||
* Warn on paths that contain spaces.
|
||||
* Use `gmake` on NetBSD.
|
||||
|
||||
## v0.6.0 (2019-06-10)
|
||||
|
||||
* Start tracking CHANGELOG.
|
||||
50
phoenix/deps/elixir_make/README.md
Normal file
50
phoenix/deps/elixir_make/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# A Make compiler for Mix
|
||||
|
||||
[](https://github.com/elixir-lang/elixir_make/actions)
|
||||
[](https://hex.pm/packages/elixir_make)
|
||||
|
||||
This project provides a Mix compiler that makes it straight-forward to use makefiles in your Mix projects.
|
||||
|
||||
## Documentation
|
||||
|
||||
API documentation is available at [https://hexdocs.pm/elixir_make](https://hexdocs.pm/elixir_make)
|
||||
|
||||
## Usage
|
||||
|
||||
The package can be installed by adding `elixir_make` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[{:elixir_make, "~> 0.4", runtime: false}]
|
||||
end
|
||||
```
|
||||
|
||||
Still in your `mix.exs` file, you will need to add `:elixir_make` to your list of compilers in `project/0`:
|
||||
|
||||
```elixir
|
||||
compilers: [:elixir_make] ++ Mix.compilers,
|
||||
```
|
||||
|
||||
And that's it. The command above will invoke `make` for Unix, `nmake` for Windows and `gmake` for FreeBSD and OpenBSD. A "Makefile" file is expected at your project root for Unix systems and "Makefile.win" for Windows systems. Run `mix help compile.elixir_make` for more information and options.
|
||||
|
||||
## Publishing a package to Hex.pm
|
||||
|
||||
When publishing a package to Hex.pm using `elixir_make` requires you to add
|
||||
any file (such as the Makefile and any source files) to the `files` option.
|
||||
See [the hex docs](https://hex.pm/docs/publish#adding-metadata-to-code-classinlinemixexscode)
|
||||
|
||||
```elixir
|
||||
defp package do
|
||||
[
|
||||
# ...
|
||||
files: [
|
||||
"lib", "LICENSE", "mix.exs", "README.md", # These are the default files
|
||||
"src/*.[ch]", "Makefile"], # You will need to add something like this.
|
||||
# ...
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Same as Elixir.
|
||||
20
phoenix/deps/elixir_make/hex_metadata.config
Normal file
20
phoenix/deps/elixir_make/hex_metadata.config
Normal file
@@ -0,0 +1,20 @@
|
||||
{<<"links">>,
|
||||
[{<<"GitHub">>,<<"https://github.com/elixir-lang/elixir_make">>}]}.
|
||||
{<<"name">>,<<"elixir_make">>}.
|
||||
{<<"version">>,<<"0.9.0">>}.
|
||||
{<<"description">>,<<"A Make compiler for Mix">>}.
|
||||
{<<"elixir">>,<<"~> 1.9">>}.
|
||||
{<<"app">>,<<"elixir_make">>}.
|
||||
{<<"licenses">>,[<<"Apache-2.0">>]}.
|
||||
{<<"requirements">>,[]}.
|
||||
{<<"files">>,
|
||||
[<<"lib">>,<<"lib/elixir_make">>,<<"lib/elixir_make/artefact.ex">>,
|
||||
<<"lib/elixir_make/compiler.ex">>,<<"lib/elixir_make/downloader">>,
|
||||
<<"lib/elixir_make/downloader/httpc.ex">>,
|
||||
<<"lib/elixir_make/downloader.ex">>,<<"lib/elixir_make/precompiler.ex">>,
|
||||
<<"lib/mix">>,<<"lib/mix/tasks">>,
|
||||
<<"lib/mix/tasks/elixir_make.checksum.ex">>,
|
||||
<<"lib/mix/tasks/elixir_make.precompile.ex">>,
|
||||
<<"lib/mix/tasks/compile.elixir_make.ex">>,<<".formatter.exs">>,
|
||||
<<"mix.exs">>,<<"README.md">>,<<"CHANGELOG.md">>]}.
|
||||
{<<"build_tools">>,[<<"mix">>]}.
|
||||
293
phoenix/deps/elixir_make/lib/elixir_make/artefact.ex
Normal file
293
phoenix/deps/elixir_make/lib/elixir_make/artefact.ex
Normal file
@@ -0,0 +1,293 @@
|
||||
defmodule ElixirMake.Artefact do
|
||||
@moduledoc false
|
||||
|
||||
require Logger
|
||||
alias ElixirMake.Artefact
|
||||
|
||||
@checksum_algo :sha256
|
||||
defstruct [:basename, :checksum, :checksum_algo]
|
||||
|
||||
@doc """
|
||||
Returns user cache directory.
|
||||
"""
|
||||
def cache_dir() do
|
||||
cache_opts = if System.get_env("MIX_XDG"), do: %{os: :linux}, else: %{}
|
||||
|
||||
cache_dir =
|
||||
Path.expand(
|
||||
System.get_env("ELIXIR_MAKE_CACHE_DIR") ||
|
||||
:filename.basedir(:user_cache, "elixir_make", cache_opts)
|
||||
)
|
||||
|
||||
File.mkdir_p!(cache_dir)
|
||||
cache_dir
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the checksum algorithm
|
||||
"""
|
||||
def checksum_algo do
|
||||
@checksum_algo
|
||||
end
|
||||
|
||||
@doc """
|
||||
Computes the checksum and artefact for the given contents.
|
||||
"""
|
||||
def checksum(basename, contents) do
|
||||
hash = :crypto.hash(checksum_algo(), contents)
|
||||
checksum = Base.encode16(hash, case: :lower)
|
||||
%Artefact{basename: basename, checksum: checksum, checksum_algo: checksum_algo()}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes checksum for the target to disk.
|
||||
"""
|
||||
def write_checksum_for_target!(%Artefact{
|
||||
basename: basename,
|
||||
checksum: checksum,
|
||||
checksum_algo: checksum_algo
|
||||
}) do
|
||||
cache_dir = Artefact.cache_dir()
|
||||
file = Path.join(cache_dir, "#{basename}.#{Atom.to_string(checksum_algo)}")
|
||||
File.write!(file, [checksum, " ", basename, "\n"])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes checksums to disk.
|
||||
"""
|
||||
def write_checksums!(checksums) do
|
||||
file = checksum_file()
|
||||
|
||||
pairs =
|
||||
Enum.map(checksums, fn
|
||||
%Artefact{basename: basename, checksum: checksum, checksum_algo: algo} ->
|
||||
{basename, "#{algo}:#{checksum}"}
|
||||
end)
|
||||
|
||||
lines =
|
||||
for {filename, checksum} <- Enum.sort(pairs) do
|
||||
~s( "#{filename}" => "#{checksum}",\n)
|
||||
end
|
||||
|
||||
File.write!(file, ["%{\n", lines, "}\n"])
|
||||
end
|
||||
|
||||
defp checksum_file() do
|
||||
Path.join(File.cwd!(), "checksum.exs")
|
||||
end
|
||||
|
||||
## Archive handling
|
||||
|
||||
@doc """
|
||||
Returns the full path to the precompiled archive.
|
||||
"""
|
||||
def archive_path(config, target, nif_version) do
|
||||
Path.join(cache_dir(), archive_filename(config, target, nif_version))
|
||||
end
|
||||
|
||||
defp archive_filename(config, target, nif_version) do
|
||||
case config[:make_precompiler] do
|
||||
{:nif, _} ->
|
||||
"#{config[:app]}-nif-#{nif_version}-#{target}-#{config[:version]}.tar.gz"
|
||||
|
||||
{type, _} ->
|
||||
"#{config[:app]}-#{type}-#{target}-#{config[:version]}.tar.gz"
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Compresses the given files and computes its checksum and artefact.
|
||||
"""
|
||||
def compress(archive_path, paths) do
|
||||
:ok = :erl_tar.create(archive_path, paths, [:compressed])
|
||||
checksum(Path.basename(archive_path), File.read!(archive_path))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Verifies and decompresses the given `archive_path` at `app_priv`.
|
||||
"""
|
||||
def verify_and_decompress(archive_path, app_priv) do
|
||||
basename = Path.basename(archive_path)
|
||||
|
||||
case File.read(archive_path) do
|
||||
{:ok, contents} ->
|
||||
verify_and_decompress(basename, archive_path, contents, app_priv)
|
||||
|
||||
{:error, reason} ->
|
||||
{:error,
|
||||
"precompiled #{inspect(basename)} does not exist or cannot download: #{inspect(reason)}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp verify_and_decompress(basename, archive_path, contents, app_priv) do
|
||||
checksum_file()
|
||||
|> read_map_from_file()
|
||||
|> case do
|
||||
%{^basename => algo_with_checksum} ->
|
||||
[algo, checksum] = String.split(algo_with_checksum, ":")
|
||||
algo = String.to_existing_atom(algo)
|
||||
|
||||
case checksum(basename, contents) do
|
||||
%Artefact{checksum: ^checksum, checksum_algo: ^algo} ->
|
||||
case :erl_tar.extract({:binary, contents}, [:compressed, {:cwd, app_priv}]) do
|
||||
:ok ->
|
||||
:ok
|
||||
|
||||
{:error, term} ->
|
||||
{:error,
|
||||
"cannot decompress precompiled #{inspect(archive_path)}: #{inspect(term)}"}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, "precompiled #{inspect(basename)} does not match its checksum"}
|
||||
end
|
||||
|
||||
checksum when checksum == %{} ->
|
||||
{:error, "missing checksum.exs file"}
|
||||
|
||||
_checksum ->
|
||||
{:error, "precompiled #{inspect(basename)} does not exist in checksum.exs"}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_map_from_file(file) do
|
||||
with {:ok, contents} <- File.read(file),
|
||||
{%{} = contents, _} <- Code.eval_string(contents) do
|
||||
contents
|
||||
else
|
||||
_ -> %{}
|
||||
end
|
||||
end
|
||||
|
||||
## 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 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 <- opts.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
|
||||
|
||||
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.
|
||||
"""
|
||||
def available_target_urls(config, precompiler) do
|
||||
targets = precompiler.all_supported_targets(:fetch)
|
||||
|
||||
url_template =
|
||||
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: [current_nif_version]]
|
||||
|
||||
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
|
||||
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
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the url for the current target.
|
||||
"""
|
||||
def current_target_url(config, precompiler, current_nif_version) do
|
||||
case precompiler.current_target() do
|
||||
{:ok, current_target} ->
|
||||
nif_versions =
|
||||
config[:make_precompiler_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/1)
|
||||
opts = %{target: current_target, versions: versions}
|
||||
fallback_version.(opts)
|
||||
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, nif_version_to_use, download_url}
|
||||
|
||||
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, msg} ->
|
||||
{:error, msg}
|
||||
end
|
||||
end
|
||||
|
||||
def download(config, url) do
|
||||
downloader = config[:make_precompiler_downloader] || ElixirMake.Downloader.Httpc
|
||||
downloader.download(url)
|
||||
end
|
||||
end
|
||||
227
phoenix/deps/elixir_make/lib/elixir_make/compiler.ex
Normal file
227
phoenix/deps/elixir_make/lib/elixir_make/compiler.ex
Normal file
@@ -0,0 +1,227 @@
|
||||
defmodule ElixirMake.Compiler do
|
||||
@moduledoc false
|
||||
|
||||
@mac_error_msg """
|
||||
You need to have gcc and make installed. Try running the
|
||||
commands "gcc --version" and / or "make --version". If these programs
|
||||
are not installed, you will be prompted to install them.
|
||||
"""
|
||||
|
||||
@unix_error_msg """
|
||||
You need to have gcc and make installed. If you are using
|
||||
Ubuntu or any other Debian-based system, install the packages
|
||||
"build-essential". Also install "erlang-dev" package if not
|
||||
included in your Erlang/OTP version. If you're on Fedora, run
|
||||
"dnf group install 'Development Tools'".
|
||||
"""
|
||||
|
||||
@windows_error_msg ~S"""
|
||||
One option is to install a recent version of
|
||||
[Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
||||
either manually or using [Chocolatey](https://chocolatey.org/) -
|
||||
`choco install VisualCppBuildTools`.
|
||||
|
||||
After installing Visual C++ Build Tools, look in the "Program Files (x86)"
|
||||
directory and search for "Microsoft Visual Studio". Note down the full path
|
||||
of the folder with the highest version number. Open the "run" command and
|
||||
type in the following command (make sure that the path and version number
|
||||
are correct):
|
||||
|
||||
cmd /K "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
|
||||
|
||||
This should open up a command prompt with the necessary environment variables
|
||||
set, and from which you will be able to run the "mix compile", "mix deps.compile",
|
||||
and "mix test" commands.
|
||||
|
||||
Another option is to install the Linux compatiblity tools from [MSYS2](https://www.msys2.org/).
|
||||
|
||||
After installation start the msys64 bit terminal from the start menu and install the
|
||||
C/C++ compiler toolchain. E.g.:
|
||||
|
||||
pacman -S --noconfirm pacman-mirrors pkg-config
|
||||
pacman -S --noconfirm --needed base-devel autoconf automake make libtool git \
|
||||
mingw-w64-x86_64-toolchain mingw-w64-x86_64-openssl mingw-w64-x86_64-libtool
|
||||
|
||||
This will give you a compilation suite nearly compatible with Unix' standard tools.
|
||||
"""
|
||||
|
||||
def compile(args) do
|
||||
config = Mix.Project.config()
|
||||
Mix.shell().print_app()
|
||||
priv? = File.dir?("priv")
|
||||
Mix.Project.ensure_structure()
|
||||
make(config, args)
|
||||
|
||||
# IF there was no priv before and now there is one, we assume
|
||||
# the user wants to copy it. If priv already existed and was
|
||||
# written to it, then it won't be copied if build_embedded is
|
||||
# set to true.
|
||||
if not priv? and File.dir?("priv") do
|
||||
Mix.Project.build_structure()
|
||||
end
|
||||
|
||||
{:ok, []}
|
||||
end
|
||||
|
||||
def make(config, task_args) do
|
||||
exec =
|
||||
System.get_env("MAKE") ||
|
||||
os_specific_executable(Keyword.get(config, :make_executable, :default))
|
||||
|
||||
makefile = Keyword.get(config, :make_makefile, :default)
|
||||
targets = Keyword.get(config, :make_targets, [])
|
||||
env = Keyword.get(config, :make_env, %{})
|
||||
env = if is_function(env), do: env.(), else: env
|
||||
env = default_env(config, env)
|
||||
|
||||
cwd = Keyword.get(config, :make_cwd, ".") |> Path.expand(File.cwd!())
|
||||
error_msg = Keyword.get(config, :make_error_message, :default) |> os_specific_error_msg()
|
||||
custom_args = Keyword.get(config, :make_args, [])
|
||||
|
||||
if String.contains?(cwd, " ") do
|
||||
IO.warn("""
|
||||
the absolute path to the Makefile for this project contains spaces. \
|
||||
Make might not work properly if spaces are present in the path. \
|
||||
The absolute path is: #{inspect(cwd)}
|
||||
""")
|
||||
end
|
||||
|
||||
base = exec |> Path.basename() |> Path.rootname()
|
||||
args = args_for_makefile(base, makefile) ++ targets ++ custom_args
|
||||
|
||||
case cmd(exec, args, cwd, env, "--verbose" in task_args) do
|
||||
0 ->
|
||||
:ok
|
||||
|
||||
exit_status ->
|
||||
raise_build_error(exec, exit_status, error_msg)
|
||||
end
|
||||
end
|
||||
|
||||
# Runs `exec [args]` in `cwd` and prints the stdout and stderr in real time,
|
||||
# as soon as `exec` prints them (using `IO.Stream`).
|
||||
defp cmd(exec, args, cwd, env, verbose?) do
|
||||
opts = [
|
||||
# There is no guarantee the command will return valid UTF-8,
|
||||
# especially on Windows, so don't try to interpret the stream
|
||||
into: IO.binstream(:stdio, :line),
|
||||
stderr_to_stdout: true,
|
||||
cd: cwd,
|
||||
env: env
|
||||
]
|
||||
|
||||
if verbose? do
|
||||
print_verbose_info(exec, args)
|
||||
end
|
||||
|
||||
{%IO.Stream{}, status} = System.cmd(find_executable(exec), args, opts)
|
||||
status
|
||||
end
|
||||
|
||||
defp find_executable(exec) do
|
||||
System.find_executable(exec) ||
|
||||
Mix.raise("""
|
||||
"#{exec}" not found in the path. If you have set the MAKE environment variable, \
|
||||
please make sure it is correct.
|
||||
""")
|
||||
end
|
||||
|
||||
defp raise_build_error(exec, exit_status, error_msg) do
|
||||
Mix.raise(~s{Could not compile with "#{exec}" (exit status: #{exit_status}).\n} <> error_msg)
|
||||
end
|
||||
|
||||
defp os_specific_executable(exec) when is_binary(exec) do
|
||||
exec
|
||||
end
|
||||
|
||||
defp os_specific_executable(:default) do
|
||||
case :os.type() do
|
||||
{:win32, _} ->
|
||||
cond do
|
||||
System.find_executable("nmake") -> "nmake"
|
||||
System.find_executable("make") -> "make"
|
||||
true -> "nmake"
|
||||
end
|
||||
|
||||
{:unix, type} when type in [:freebsd, :openbsd, :netbsd, :dragonfly] ->
|
||||
"gmake"
|
||||
|
||||
_ ->
|
||||
"make"
|
||||
end
|
||||
end
|
||||
|
||||
defp os_specific_error_msg(msg) when is_binary(msg) do
|
||||
msg
|
||||
end
|
||||
|
||||
defp os_specific_error_msg(:default) do
|
||||
case :os.type() do
|
||||
{:unix, :darwin} -> @mac_error_msg
|
||||
{:unix, _} -> @unix_error_msg
|
||||
{:win32, _} -> @windows_error_msg
|
||||
_ -> ""
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a list of command-line args to pass to make (or nmake/gmake) in
|
||||
# order to specify the makefile to use.
|
||||
defp args_for_makefile("nmake", :default), do: ["/F", "Makefile.win"]
|
||||
defp args_for_makefile("nmake", makefile), do: ["/F", makefile]
|
||||
defp args_for_makefile(_, :default), do: []
|
||||
defp args_for_makefile(_, makefile), do: ["-f", makefile]
|
||||
|
||||
defp print_verbose_info(exec, args) do
|
||||
args =
|
||||
Enum.map_join(args, " ", fn arg ->
|
||||
if String.contains?(arg, " "), do: inspect(arg), else: arg
|
||||
end)
|
||||
|
||||
Mix.shell().info("Compiling with make: #{exec} #{args}")
|
||||
end
|
||||
|
||||
# Returns a map of default environment variables
|
||||
# Defaults may be overwritten.
|
||||
defp default_env(config, default_env) do
|
||||
root_dir = :code.root_dir()
|
||||
erl_interface_dir = Path.join(root_dir, "usr")
|
||||
erts_dir = Path.join(root_dir, "erts-#{:erlang.system_info(:version)}")
|
||||
erts_include_dir = Path.join(erts_dir, "include")
|
||||
erl_ei_lib_dir = Path.join(erl_interface_dir, "lib")
|
||||
erl_ei_include_dir = Path.join(erl_interface_dir, "include")
|
||||
|
||||
Map.merge(
|
||||
%{
|
||||
# Don't use Mix.target/0 here for backwards compatibility
|
||||
"MIX_TARGET" => env("MIX_TARGET", "host"),
|
||||
"MIX_ENV" => to_string(Mix.env()),
|
||||
"MIX_BUILD_PATH" => Mix.Project.build_path(config),
|
||||
"MIX_APP_PATH" => Mix.Project.app_path(config),
|
||||
"MIX_COMPILE_PATH" => Mix.Project.compile_path(config),
|
||||
"MIX_CONSOLIDATION_PATH" => Mix.Project.consolidation_path(config),
|
||||
"MIX_DEPS_PATH" => Mix.Project.deps_path(config),
|
||||
"MIX_MANIFEST_PATH" => Mix.Project.manifest_path(config),
|
||||
|
||||
# Rebar naming
|
||||
"ERL_EI_LIBDIR" => env("ERL_EI_LIBDIR", erl_ei_lib_dir),
|
||||
"ERL_EI_INCLUDE_DIR" => env("ERL_EI_INCLUDE_DIR", erl_ei_include_dir),
|
||||
|
||||
# erlang.mk naming
|
||||
"ERTS_INCLUDE_DIR" => env("ERTS_INCLUDE_DIR", erts_include_dir),
|
||||
"ERL_INTERFACE_LIB_DIR" => env("ERL_INTERFACE_LIB_DIR", erl_ei_lib_dir),
|
||||
"ERL_INTERFACE_INCLUDE_DIR" => env("ERL_INTERFACE_INCLUDE_DIR", erl_ei_include_dir),
|
||||
|
||||
# Disable default erlang values
|
||||
"BINDIR" => nil,
|
||||
"ROOTDIR" => nil,
|
||||
"PROGNAME" => nil,
|
||||
"EMU" => nil
|
||||
},
|
||||
default_env
|
||||
)
|
||||
end
|
||||
|
||||
defp env(var, default) do
|
||||
System.get_env(var) || default
|
||||
end
|
||||
end
|
||||
10
phoenix/deps/elixir_make/lib/elixir_make/downloader.ex
Normal file
10
phoenix/deps/elixir_make/lib/elixir_make/downloader.ex
Normal file
@@ -0,0 +1,10 @@
|
||||
defmodule ElixirMake.Downloader do
|
||||
@moduledoc """
|
||||
The behaviour for downloader modules.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
This callback should download the artefact from the given URL.
|
||||
"""
|
||||
@callback download(url :: String.t()) :: {:ok, iolist() | binary()} | {:error, String.t()}
|
||||
end
|
||||
99
phoenix/deps/elixir_make/lib/elixir_make/downloader/httpc.ex
Normal file
99
phoenix/deps/elixir_make/lib/elixir_make/downloader/httpc.ex
Normal file
@@ -0,0 +1,99 @@
|
||||
defmodule ElixirMake.Downloader.Httpc do
|
||||
@moduledoc false
|
||||
|
||||
@behaviour ElixirMake.Downloader
|
||||
|
||||
@impl ElixirMake.Downloader
|
||||
def download(url) do
|
||||
url_charlist = String.to_charlist(url)
|
||||
|
||||
# TODO: Remove me when we require Elixir v1.15
|
||||
{:ok, _} = Application.ensure_all_started(:inets)
|
||||
{:ok, _} = Application.ensure_all_started(:ssl)
|
||||
{:ok, _} = Application.ensure_all_started(:public_key)
|
||||
|
||||
if proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy") do
|
||||
Mix.shell().info("Using HTTP_PROXY: #{proxy}")
|
||||
%{host: host, port: port} = URI.parse(proxy)
|
||||
|
||||
:httpc.set_options([{:proxy, {{String.to_charlist(host), port}, []}}])
|
||||
end
|
||||
|
||||
if proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy") do
|
||||
Mix.shell().info("Using HTTPS_PROXY: #{proxy}")
|
||||
%{host: host, port: port} = URI.parse(proxy)
|
||||
:httpc.set_options([{:https_proxy, {{String.to_charlist(host), port}, []}}])
|
||||
end
|
||||
|
||||
# https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/inets
|
||||
# TODO: This may no longer be necessary from Erlang/OTP 25.0 or later.
|
||||
https_options = [
|
||||
ssl:
|
||||
[
|
||||
verify: :verify_peer,
|
||||
customize_hostname_check: [
|
||||
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
|
||||
]
|
||||
] ++ cacerts_options()
|
||||
]
|
||||
|
||||
options = [body_format: :binary]
|
||||
|
||||
case :httpc.request(:get, {url_charlist, []}, https_options, options) do
|
||||
{:ok, {{_, 200, _}, _headers, body}} ->
|
||||
{:ok, body}
|
||||
|
||||
other ->
|
||||
{:error, "couldn't fetch NIF from #{url}: #{inspect(other)}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp cacerts_options do
|
||||
cond do
|
||||
path = System.get_env("HEX_CACERTS_PATH") ->
|
||||
[cacertfile: path]
|
||||
|
||||
path = System.get_env("ELIXIR_MAKE_CACERT") ->
|
||||
IO.warn("Setting ELIXIR_MAKE_CACERT is deprecated, please set HEX_CACERTS_PATH instead")
|
||||
[cacertfile: path]
|
||||
|
||||
certs = otp_cacerts() ->
|
||||
[cacerts: certs]
|
||||
|
||||
true ->
|
||||
warn_no_cacerts()
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp otp_cacerts do
|
||||
if System.otp_release() >= "25" do
|
||||
# cacerts_get/0 raises if no certs found
|
||||
try do
|
||||
:public_key.cacerts_get()
|
||||
rescue
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp warn_no_cacerts do
|
||||
Mix.shell().error("""
|
||||
No certificate trust store was found.
|
||||
|
||||
A certificate trust store is required in
|
||||
order to download locales for your configuration.
|
||||
Since elixir_make could not detect a system
|
||||
installed certificate trust store one of the
|
||||
following actions may be taken:
|
||||
|
||||
1. Specify the location of a certificate trust store
|
||||
by configuring it in environment variable:
|
||||
|
||||
export HEX_CACERTS_PATH="/path/to/cacerts.pem"
|
||||
|
||||
2. Use OTP 25+ on an OS that has built-in certificate
|
||||
trust store.
|
||||
""")
|
||||
end
|
||||
end
|
||||
110
phoenix/deps/elixir_make/lib/elixir_make/precompiler.ex
Normal file
110
phoenix/deps/elixir_make/lib/elixir_make/precompiler.ex
Normal file
@@ -0,0 +1,110 @@
|
||||
defmodule ElixirMake.Precompiler do
|
||||
@moduledoc """
|
||||
The behaviour for precompiler modules.
|
||||
"""
|
||||
|
||||
require Logger
|
||||
|
||||
@typedoc """
|
||||
Target triplet.
|
||||
"""
|
||||
@type target :: String.t()
|
||||
|
||||
@doc """
|
||||
This callback should return a list of triplets ("arch-os-abi") for all supported targets
|
||||
of the given operation.
|
||||
|
||||
For the `:compile` operation, `all_supported_targets` should return a list of targets that
|
||||
the current host is capable of (cross-)compiling to.
|
||||
|
||||
For the `:fetch` operation, `all_supported_targets` should return the full list of targets.
|
||||
|
||||
For example, GitHub Actions provides Linux, macOS and Windows CI hosts, when `operation` is
|
||||
`:compile`, the precompiler might return `["x86_64-linux-gnu"]` if it is running in the Linux
|
||||
CI environment, while returning `["x86_64-apple-darwin", "aarch64-apple-darwin"]` on macOS,
|
||||
or `["amd64-windows", "x86-windows"]` on Windows platform.
|
||||
|
||||
When `operation` is `:fetch`, the precompiler should return the full list. The full list for
|
||||
the above example should be:
|
||||
|
||||
[
|
||||
"x86_64-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-darwin",
|
||||
"amd64-windows",
|
||||
"x86-windows"
|
||||
]
|
||||
|
||||
This allows the precompiler to do the compilation work in multilple hosts and gather all the
|
||||
artefacts later with `mix elixir_make.checksum --all`.
|
||||
"""
|
||||
@callback all_supported_targets(operation :: :compile | :fetch) :: [target]
|
||||
|
||||
@doc """
|
||||
This callback should return the target triplet for current node.
|
||||
"""
|
||||
@callback current_target() :: {:ok, target} | {:error, String.t()}
|
||||
|
||||
@doc """
|
||||
This callback will be invoked when the user executes the `mix compile`
|
||||
(or `mix compile.elixir_make`) command.
|
||||
|
||||
The precompiler should then compile the NIF library "natively". Note that
|
||||
it is possible for the precompiler module to pick up other environment variables
|
||||
like `TARGET_ARCH=aarch64` and adjust compile arguments correspondingly.
|
||||
"""
|
||||
@callback build_native(OptionParser.argv()) ::
|
||||
{Mix.Task.Compiler.status(), [Mix.Task.Compiler.Diagnostic.t()]}
|
||||
|
||||
@doc """
|
||||
This callback should precompile the library to the given target(s).
|
||||
|
||||
Returns `:ok` if the requested target has successfully compiled.
|
||||
"""
|
||||
@callback precompile(OptionParser.argv(), target) :: :ok | {:error, String.t()}
|
||||
|
||||
@doc """
|
||||
Optional post actions to run after each precompilation target is archived.
|
||||
|
||||
It will be called when a target is precompiled and archived successfully.
|
||||
For example, actions can be deleting all target-specific files.
|
||||
"""
|
||||
@callback post_precompile_target(target) :: :ok
|
||||
|
||||
@doc """
|
||||
Optional post actions to run after all precompilation tasks are done.
|
||||
|
||||
It will only be called at the end of the `mix elixir_make.precompile` command.
|
||||
For example, actions can be archiving all precompiled artefacts and uploading
|
||||
the archive file to an object storage server.
|
||||
"""
|
||||
@callback post_precompile() :: :ok
|
||||
|
||||
@doc """
|
||||
Optional recover actions when the current target is unavailable.
|
||||
|
||||
There are two reasons that the current target might be unavailable:
|
||||
when the library only has precompiled binaries for some platforms,
|
||||
and it either
|
||||
|
||||
- needs to be compiled on other platforms.
|
||||
|
||||
The callback should return `:compile` for this case.
|
||||
|
||||
- is intended to function as `noop` on other platforms.
|
||||
|
||||
The callback should return `:ignore` for this case.
|
||||
|
||||
Defaults to `:compile` if this callback is not implemented.
|
||||
"""
|
||||
@callback unavailable_target(String.t()) :: :compile | :ignore
|
||||
|
||||
@optional_callbacks post_precompile: 0, unavailable_target: 1, post_precompile_target: 1
|
||||
|
||||
@doc """
|
||||
Invoke the regular Mix toolchain compilation.
|
||||
"""
|
||||
def mix_compile(args) do
|
||||
ElixirMake.Compiler.compile(args)
|
||||
end
|
||||
end
|
||||
243
phoenix/deps/elixir_make/lib/mix/tasks/compile.elixir_make.ex
Normal file
243
phoenix/deps/elixir_make/lib/mix/tasks/compile.elixir_make.ex
Normal file
@@ -0,0 +1,243 @@
|
||||
defmodule Mix.Tasks.Compile.ElixirMake do
|
||||
@moduledoc """
|
||||
Runs `make` in the current project.
|
||||
|
||||
This task runs `make` in the current project; any output coming from `make` is
|
||||
printed in real-time on stdout.
|
||||
|
||||
## Configuration
|
||||
|
||||
This compiler can be configured through the return value of the `project/0`
|
||||
function in `mix.exs`; for example:
|
||||
|
||||
def project() do
|
||||
[app: :myapp,
|
||||
make_executable: "make",
|
||||
make_makefile: "Othermakefile",
|
||||
compilers: [:elixir_make] ++ Mix.compilers,
|
||||
deps: deps()]
|
||||
end
|
||||
|
||||
The following options are available:
|
||||
|
||||
* `:make_executable` - (binary or `:default`) it's the executable to use as the
|
||||
`make` program. If not provided or if `:default`, it defaults to `"nmake"`
|
||||
on Windows, `"gmake"` on FreeBSD, OpenBSD and NetBSD, and `"make"` on everything
|
||||
else. You can, for example, customize which executable to use on a
|
||||
specific OS and use `:default` for every other OS. If the `MAKE`
|
||||
environment variable is present, that is used as the value of this option.
|
||||
|
||||
* `:make_makefile` - (binary or `:default`) it's the Makefile to
|
||||
use. Defaults to `"Makefile"` for Unix systems and `"Makefile.win"` for
|
||||
Windows systems if not provided or if `:default`.
|
||||
|
||||
* `:make_targets` - (list of binaries) it's the list of Make targets that
|
||||
should be run. Defaults to `[]`, meaning `make` will run the first target.
|
||||
|
||||
* `:make_clean` - (list of binaries) it's a list of Make targets to be run
|
||||
when `mix clean` is run. It's only run if a non-`nil` value for
|
||||
`:make_clean` is provided. Defaults to `nil`.
|
||||
|
||||
* `:make_cwd` - (binary) it's the directory where `make` will be run,
|
||||
relative to the root of the project.
|
||||
|
||||
* `:make_env` - (map of binary to binary) it's a map of extra environment
|
||||
variables to be passed to `make`. You can also pass a function in here in
|
||||
case `make_env` needs access to things that are not available during project
|
||||
setup; the function should return a map of binary to binary. Many default
|
||||
environment variables are set, see section below
|
||||
|
||||
* `:make_error_message` - (binary or `:default`) it's a custom error message
|
||||
that can be used to give instructions as of how to fix the error (e.g., it
|
||||
can be used to suggest installing `gcc` if you're compiling a C
|
||||
dependency).
|
||||
|
||||
* `:make_args` - (list of binaries) it's a list of extra arguments to be passed.
|
||||
|
||||
The following options configure precompilation:
|
||||
|
||||
* `:make_precompiler` - a two-element tuple with the precompiled type
|
||||
and module to use. The precompile type is either `:nif` or `:port`
|
||||
and then the precompilation module. If the type is a `:nif`, it looks
|
||||
for a DDL or a shared object as precompilation target given by
|
||||
`:make_precompiler_filename` and the current NIF version is part of
|
||||
the precompiled archive. If `:port`, it looks for an executable with
|
||||
`:make_precompiler_filename`.
|
||||
|
||||
* `:make_precompiler_url` - the download URL template. Defaults to none.
|
||||
Required when `make_precompiler` is set.
|
||||
|
||||
* `:make_precompiler_filename` - the filename of the compiled artefact
|
||||
without its extension. Defaults to the app name.
|
||||
|
||||
* `:make_precompiler_downloader` - a module implementing the `ElixirMake.Downloader`
|
||||
behaviour. You can use this to customize how the precompiled artefacts
|
||||
are downloaded, for example, to add HTTP authentication or to download
|
||||
from an SFTP server. The default implementation uses `:httpc`.
|
||||
|
||||
* `:make_force_build` - if build should be forced even if precompiled artefacts
|
||||
are available. Defaults to true if the app has a `-dev` version flag.
|
||||
|
||||
See [the Precompilation guide](PRECOMPILATION_GUIDE.md) for more information.
|
||||
|
||||
## Default environment variables
|
||||
|
||||
There are also several default environment variables set:
|
||||
|
||||
* `MIX_TARGET`
|
||||
* `MIX_ENV`
|
||||
* `MIX_BUILD_PATH` - same as `Mix.Project.build_path/0`
|
||||
* `MIX_APP_PATH` - same as `Mix.Project.app_path/0`
|
||||
* `MIX_COMPILE_PATH` - same as `Mix.Project.compile_path/0`
|
||||
* `MIX_CONSOLIDATION_PATH` - same as `Mix.Project.consolidation_path/0`
|
||||
* `MIX_DEPS_PATH` - same as `Mix.Project.deps_path/0`
|
||||
* `MIX_MANIFEST_PATH` - same as `Mix.Project.manifest_path/0`
|
||||
* `ERL_EI_LIBDIR`
|
||||
* `ERL_EI_INCLUDE_DIR`
|
||||
* `ERTS_INCLUDE_DIR`
|
||||
* `ERL_INTERFACE_LIB_DIR`
|
||||
* `ERL_INTERFACE_INCLUDE_DIR`
|
||||
|
||||
These may also be overwritten with the `make_env` option.
|
||||
|
||||
## Compilation artifacts and working with priv directories
|
||||
|
||||
Generally speaking, compilation artifacts are written to the `priv`
|
||||
directory, as that the only directory, besides `ebin`, which are
|
||||
available to Erlang/OTP applications.
|
||||
|
||||
However, note that Mix projects supports the `:build_embedded`
|
||||
configuration, which controls if assets in the `_build` directory
|
||||
are symlinked (when `false`, the default) or copied (`true`).
|
||||
In order to support both options for `:build_embedded`, it is
|
||||
important to follow the given guidelines:
|
||||
|
||||
* The "priv" directory must not exist in the source code
|
||||
* The Makefile should copy any artifact to `$MIX_APP_PATH/priv`
|
||||
or, even better, to `$MIX_APP_PATH/priv/$MIX_TARGET`
|
||||
* If there are static assets, the Makefile should copy them over
|
||||
from a directory at the project root (not named "priv")
|
||||
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
alias ElixirMake.Artefact
|
||||
|
||||
@recursive true
|
||||
|
||||
@doc false
|
||||
def run(args) do
|
||||
if function_exported?(Mix, :ensure_application!, 1) do
|
||||
Mix.ensure_application!(:inets)
|
||||
Mix.ensure_application!(:ssl)
|
||||
Mix.ensure_application!(:crypto)
|
||||
end
|
||||
|
||||
config = Mix.Project.config()
|
||||
app = config[:app]
|
||||
version = config[:version]
|
||||
|
||||
force_build =
|
||||
pre_release?(version) or Keyword.get(config, :make_force_build, false) or
|
||||
Keyword.get(Application.get_env(:elixir_make, :force_build, []), app, false)
|
||||
|
||||
{precompiler_type, precompiler} = config[:make_precompiler] || {nil, nil}
|
||||
|
||||
cond do
|
||||
precompiler == nil ->
|
||||
ElixirMake.Compiler.compile(args)
|
||||
|
||||
force_build == true ->
|
||||
precompiler.build_native(args)
|
||||
|
||||
true ->
|
||||
rootname = config[:make_precompiler_filename] || "#{app}"
|
||||
|
||||
extname =
|
||||
case {precompiler_type, :os.type()} do
|
||||
{:nif, {:win32, _}} -> ".dll"
|
||||
{:nif, _} -> ".so"
|
||||
{:port, {:win32, _}} -> ".exe"
|
||||
{:port, _} -> ""
|
||||
{_, _} -> raise_unknown_precompiler_type(precompiler_type)
|
||||
end
|
||||
|
||||
app_priv = Path.join(Mix.Project.app_path(config), "priv")
|
||||
load_path = Path.join(app_priv, rootname <> extname)
|
||||
|
||||
with false <- File.exists?(load_path),
|
||||
{:error, message} <- download_or_reuse_nif(config, precompiler, app_priv) do
|
||||
recover =
|
||||
case message do
|
||||
{:unavailable_target, current_target, _description} ->
|
||||
if function_exported?(precompiler, :unavailable_target, 1) do
|
||||
precompiler.unavailable_target(current_target)
|
||||
else
|
||||
:compile
|
||||
end
|
||||
|
||||
_ ->
|
||||
Mix.shell().error("""
|
||||
Error happened while installing #{app} from precompiled binary: #{inspect(message)}.
|
||||
|
||||
Attempting to compile #{app} from source...\
|
||||
""")
|
||||
|
||||
:compile
|
||||
end
|
||||
|
||||
case recover do
|
||||
:compile -> precompiler.build_native(args)
|
||||
:ignore -> {:ok, []}
|
||||
end
|
||||
else
|
||||
_ -> {:ok, []}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp raise_unknown_precompiler_type(precompiler_type) do
|
||||
Mix.raise("Unknown precompiler type: #{inspect(precompiler_type)} (expected :nif or :port)")
|
||||
end
|
||||
|
||||
# This is called by Elixir when `mix clean` runs
|
||||
# and `:elixir_make` is in the list of compilers.
|
||||
@doc false
|
||||
def clean() do
|
||||
config = Mix.Project.config()
|
||||
{clean_targets, config} = Keyword.pop(config, :make_clean)
|
||||
|
||||
if clean_targets do
|
||||
config
|
||||
|> Keyword.put(:make_targets, clean_targets)
|
||||
|> ElixirMake.Compiler.make([])
|
||||
end
|
||||
end
|
||||
|
||||
defp pre_release?(version) do
|
||||
"dev" in Version.parse!(version).pre
|
||||
end
|
||||
|
||||
defp download_or_reuse_nif(config, precompiler, app_priv) do
|
||||
nif_version = "#{:erlang.system_info(:nif_version)}"
|
||||
|
||||
case Artefact.current_target_url(config, precompiler, nif_version) do
|
||||
{:ok, target, nif_version_to_use, url} ->
|
||||
archived_fullpath = Artefact.archive_path(config, target, nif_version_to_use)
|
||||
|
||||
unless File.exists?(archived_fullpath) do
|
||||
Mix.shell().info("Downloading precompiled NIF to #{archived_fullpath}")
|
||||
|
||||
with {:ok, archived_data} <- Artefact.download(config, url) do
|
||||
File.mkdir_p(Path.dirname(archived_fullpath))
|
||||
File.write(archived_fullpath, archived_data)
|
||||
end
|
||||
end
|
||||
|
||||
Artefact.verify_and_decompress(archived_fullpath, app_priv)
|
||||
|
||||
{:error, msg} ->
|
||||
{:error, msg}
|
||||
end
|
||||
end
|
||||
end
|
||||
172
phoenix/deps/elixir_make/lib/mix/tasks/elixir_make.checksum.ex
Normal file
172
phoenix/deps/elixir_make/lib/mix/tasks/elixir_make.checksum.ex
Normal file
@@ -0,0 +1,172 @@
|
||||
defmodule Mix.Tasks.ElixirMake.Checksum do
|
||||
@shortdoc "Fetch precompiled NIFs and build the checksums"
|
||||
|
||||
@moduledoc """
|
||||
A task responsible for downloading the precompiled NIFs for a given module.
|
||||
|
||||
This task must only be used by package creators who want to ship the
|
||||
precompiled NIFs. The goal is to download the precompiled packages and
|
||||
generate a checksum to check-in alongside the project in the the Hex repository.
|
||||
This is done by passing the `--all` flag.
|
||||
|
||||
You can also use the `--only-local` flag to download only the precompiled
|
||||
package for use during development.
|
||||
|
||||
You can use the `--ignore-unavailable` flag to ignore any NIFs that are not available.
|
||||
This is useful when you are developing a new NIF that does not support all platforms.
|
||||
|
||||
This task also accept the `--print` flag to print the checksums.
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
alias ElixirMake.Artefact
|
||||
|
||||
@recursive true
|
||||
|
||||
@switches [
|
||||
all: :boolean,
|
||||
only_local: :boolean,
|
||||
print: :boolean,
|
||||
ignore_unavailable: :boolean
|
||||
]
|
||||
|
||||
@impl true
|
||||
def run(flags) when is_list(flags) do
|
||||
if function_exported?(Mix, :ensure_application!, 1) do
|
||||
Mix.ensure_application!(:inets)
|
||||
Mix.ensure_application!(:ssl)
|
||||
Mix.ensure_application!(:crypto)
|
||||
end
|
||||
|
||||
config = Mix.Project.config()
|
||||
|
||||
{_, precompiler} =
|
||||
config[:make_precompiler] ||
|
||||
Mix.raise(
|
||||
":make_precompiler project configuration is required when using elixir_make.checksum"
|
||||
)
|
||||
|
||||
{options, _args} = OptionParser.parse!(flags, strict: @switches)
|
||||
|
||||
urls =
|
||||
cond do
|
||||
Keyword.get(options, :all) ->
|
||||
Artefact.available_target_urls(config, precompiler)
|
||||
|
||||
Keyword.get(options, :only_local) ->
|
||||
current_nif_version = "#{:erlang.system_info(:nif_version)}"
|
||||
|
||||
case Artefact.current_target_url(config, precompiler, current_nif_version) do
|
||||
{:ok, target, nif_version_to_use, url} ->
|
||||
[{{target, nif_version_to_use}, url}]
|
||||
|
||||
{:error, {:unavailable_target, current_target, error}} ->
|
||||
recover =
|
||||
if function_exported?(precompiler, :unavailable_target, 1) do
|
||||
precompiler.unavailable_target(current_target)
|
||||
else
|
||||
:compile
|
||||
end
|
||||
|
||||
case recover do
|
||||
:compile ->
|
||||
Mix.raise(error)
|
||||
|
||||
:ignore ->
|
||||
[]
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
Mix.raise(error)
|
||||
end
|
||||
|
||||
true ->
|
||||
Mix.raise("you need to specify either \"--all\" or \"--only-local\" flags")
|
||||
end
|
||||
|
||||
artefacts = download_and_checksum_all(config, urls, options)
|
||||
|
||||
if Keyword.get(options, :print, false) do
|
||||
artefacts
|
||||
|> Enum.map(fn %Artefact{basename: basename, checksum: checksum} -> {basename, checksum} end)
|
||||
|> Enum.sort()
|
||||
|> Enum.map_join("\n", fn {file, checksum} -> "#{checksum} #{file}" end)
|
||||
|> IO.puts()
|
||||
end
|
||||
|
||||
Artefact.write_checksums!(artefacts)
|
||||
end
|
||||
|
||||
defp download_and_checksum_all(config, urls, options) do
|
||||
ignore_unavailable? = Keyword.get(options, :ignore_unavailable, false)
|
||||
|
||||
tasks =
|
||||
Task.async_stream(
|
||||
urls,
|
||||
fn {{_target, _nif_version}, url} ->
|
||||
checksum_algo = Artefact.checksum_algo()
|
||||
checksum_file_url = "#{url}.#{Atom.to_string(checksum_algo)}"
|
||||
artifact_checksum = Artefact.download(config, checksum_file_url)
|
||||
|
||||
with {:ok, body} <- artifact_checksum,
|
||||
[checksum, basename] <- String.split(body, " ", trim: true) do
|
||||
{:checksum, url,
|
||||
%Artefact{
|
||||
basename: String.trim(basename),
|
||||
checksum: checksum,
|
||||
checksum_algo: checksum_algo
|
||||
}}
|
||||
else
|
||||
_ -> {:download, url, Artefact.download(config, url)}
|
||||
end
|
||||
end,
|
||||
timeout: :infinity,
|
||||
ordered: false
|
||||
)
|
||||
|
||||
cache_dir = Artefact.cache_dir()
|
||||
|
||||
Enum.flat_map(tasks, fn
|
||||
{:ok, {:checksum, _url, artefact}} ->
|
||||
Mix.shell().info(
|
||||
"NIF checksum file with checksum #{artefact.checksum} (#{artefact.checksum_algo})"
|
||||
)
|
||||
|
||||
[artefact]
|
||||
|
||||
{:ok, {:download, url, download}} ->
|
||||
case download do
|
||||
{:ok, body} ->
|
||||
basename = basename_from_url(url)
|
||||
path = Path.join(cache_dir, basename)
|
||||
File.write!(path, body)
|
||||
artefact = Artefact.checksum(basename, body)
|
||||
|
||||
Mix.shell().info(
|
||||
"NIF cached at #{path} with checksum #{artefact.checksum} (#{artefact.checksum_algo})"
|
||||
)
|
||||
|
||||
[artefact]
|
||||
|
||||
result ->
|
||||
if ignore_unavailable? do
|
||||
msg = "Skipped unavailable NIF artifact. Reason: #{inspect(result)}"
|
||||
Mix.shell().info(msg)
|
||||
else
|
||||
msg = "Could not finish the download of NIF artifacts. Reason: #{inspect(result)}"
|
||||
Mix.shell().error(msg)
|
||||
end
|
||||
|
||||
[]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp basename_from_url(url) do
|
||||
uri = URI.parse(url)
|
||||
|
||||
uri.path
|
||||
|> String.split("/")
|
||||
|> List.last()
|
||||
end
|
||||
end
|
||||
103
phoenix/deps/elixir_make/lib/mix/tasks/elixir_make.precompile.ex
Normal file
103
phoenix/deps/elixir_make/lib/mix/tasks/elixir_make.precompile.ex
Normal file
@@ -0,0 +1,103 @@
|
||||
defmodule Mix.Tasks.ElixirMake.Precompile do
|
||||
@shortdoc "Precompiles the given project for all targets"
|
||||
|
||||
@moduledoc """
|
||||
Precompiles the given project for all targets.
|
||||
|
||||
This task must only be used by package creators who want to ship the
|
||||
precompiled NIFs. This task is often used on CI to precompile
|
||||
for different targets.
|
||||
|
||||
This is only supported if `:make_precompiler` is specified
|
||||
in your project configuration.
|
||||
"""
|
||||
|
||||
alias ElixirMake.Artefact
|
||||
require Logger
|
||||
use Mix.Task
|
||||
|
||||
@recursive true
|
||||
|
||||
@impl true
|
||||
def run(args) do
|
||||
if function_exported?(Mix, :ensure_application!, 1) do
|
||||
Mix.ensure_application!(:inets)
|
||||
Mix.ensure_application!(:ssl)
|
||||
Mix.ensure_application!(:crypto)
|
||||
end
|
||||
|
||||
config = Mix.Project.config()
|
||||
paths = config[:make_precompiler_priv_paths] || ["."]
|
||||
|
||||
{_, precompiler} =
|
||||
config[:make_precompiler] ||
|
||||
Mix.raise(
|
||||
":make_precompiler project configuration is required when using elixir_make.precompile"
|
||||
)
|
||||
|
||||
targets = precompiler.all_supported_targets(:compile)
|
||||
|
||||
try do
|
||||
precompiled_artefacts =
|
||||
Enum.map(targets, fn target ->
|
||||
case precompiler.precompile(args, target) do
|
||||
:ok ->
|
||||
precompiled_artefacts = create_precompiled_archive(config, target, paths)
|
||||
|
||||
if function_exported?(precompiler, :post_precompile_target, 1) do
|
||||
precompiler.post_precompile_target(target)
|
||||
end
|
||||
|
||||
Artefact.write_checksum_for_target!(precompiled_artefacts)
|
||||
|
||||
precompiled_artefacts
|
||||
|
||||
{:error, msg} ->
|
||||
Mix.raise(msg)
|
||||
end
|
||||
end)
|
||||
|
||||
Artefact.write_checksums!(precompiled_artefacts)
|
||||
|
||||
if function_exported?(precompiler, :post_precompile, 0) do
|
||||
precompiler.post_precompile()
|
||||
else
|
||||
:ok
|
||||
end
|
||||
after
|
||||
app_priv = Path.join(Mix.Project.app_path(config), "priv")
|
||||
|
||||
for include <- paths,
|
||||
file <- Path.wildcard(Path.join(app_priv, include)) do
|
||||
File.rm_rf(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp create_precompiled_archive(config, target, paths) do
|
||||
archive_path = Artefact.archive_path(config, target, :erlang.system_info(:nif_version))
|
||||
|
||||
Mix.shell().info("Creating precompiled archive: #{archive_path}")
|
||||
Mix.shell().info("Paths to archive from priv directory: #{inspect(paths)}")
|
||||
|
||||
app_priv = Path.join(Mix.Project.app_path(config), "priv")
|
||||
File.mkdir_p!(app_priv)
|
||||
File.mkdir_p!(Path.dirname(archive_path))
|
||||
|
||||
artefact =
|
||||
File.cd!(app_priv, fn ->
|
||||
filepaths =
|
||||
for path <- paths,
|
||||
entry <- Path.wildcard(path),
|
||||
do: String.to_charlist(entry)
|
||||
|
||||
Artefact.compress(archive_path, filepaths)
|
||||
end)
|
||||
|
||||
Mix.shell().info(
|
||||
"NIF cached at #{archive_path} with checksum #{artefact.checksum} (#{artefact.checksum_algo})"
|
||||
)
|
||||
|
||||
artefact
|
||||
end
|
||||
end
|
||||
44
phoenix/deps/elixir_make/mix.exs
Normal file
44
phoenix/deps/elixir_make/mix.exs
Normal file
@@ -0,0 +1,44 @@
|
||||
defmodule ElixirMake.Mixfile do
|
||||
use Mix.Project
|
||||
|
||||
@version "0.9.0"
|
||||
def project do
|
||||
[
|
||||
app: :elixir_make,
|
||||
version: @version,
|
||||
elixir: "~> 1.9",
|
||||
description: "A Make compiler for Mix",
|
||||
package: package(),
|
||||
docs: docs(),
|
||||
deps: deps(),
|
||||
xref: [exclude: [Mix, :crypto, :ssl, :public_key, :httpc]]
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[extra_applications: [:logger]]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:ex_doc, "~> 0.20", only: :docs}
|
||||
]
|
||||
end
|
||||
|
||||
defp package do
|
||||
%{
|
||||
licenses: ["Apache-2.0"],
|
||||
links: %{"GitHub" => "https://github.com/elixir-lang/elixir_make"},
|
||||
maintainers: ["Andrea Leopardi", "José Valim"]
|
||||
}
|
||||
end
|
||||
|
||||
defp docs do
|
||||
[
|
||||
main: "Mix.Tasks.Compile.ElixirMake",
|
||||
extras: ["PRECOMPILATION_GUIDE.md", "CHANGELOG.md"],
|
||||
source_ref: "v#{@version}",
|
||||
source_url: "https://github.com/elixir-lang/elixir_make"
|
||||
]
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user