update
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user