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
|
||||
Reference in New Issue
Block a user