Packages
igniter
0.3.38
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
installer/lib/private/deps_compile.ex
defmodule Igniter.Util.DepsCompile do
@moduledoc false
# Copied from core mix task deps.compile, with small alterations
# the primary difference is reenabling the mix tasks
# Compiles dependencies.
# By default, this task attempts to compile all dependencies.
# A list of dependencies can be given to compile multiple
# dependencies in order.
# This task attempts to detect if the project contains one of
# the following files and act accordingly:
# * `mix.exs` - invokes `mix compile`
# * `rebar.config` - invokes `rebar compile`
# * `Makefile.win`- invokes `nmake /F Makefile.win` (only on Windows)
# * `Makefile` - invokes `gmake` on DragonFlyBSD, FreeBSD, NetBSD, and OpenBSD,
# invokes `make` on any other operating system (except on Windows)
# The compilation can be customized by passing a `compile` option
# in the dependency:
# {:some_dependency, "0.1.0", compile: "command to compile"}
# If a list of dependencies is given, Mix will attempt to compile
# them as is. For example, if project `a` depends on `b`, calling
# `mix deps.compile a` will compile `a` even if `b` is out of
# date. This is to allow parts of the dependency tree to be
# recompiled without propagating those changes upstream. To ensure
# `b` is included in the compilation step, pass `--include-children`.
# ## Command line options
# * `--force` - force compilation of deps
# * `--skip-umbrella-children` - skips umbrella applications from compiling
# * `--skip-local-deps` - skips non-remote dependencies, such as path deps, from compiling
def run(_opts \\ []) do
Mix.Project.get!()
deps = Mix.Dep.load_and_cache()
opts =
[include_children: true]
compile(filter_available_and_local_deps(deps), opts)
Mix.Task.reenable("deps.loadpaths")
Mix.Task.run("deps.loadpaths", ["--no-deps-check"])
end
@doc false
def compile(deps, options \\ []) do
shell = Mix.shell()
config = Mix.Project.deps_config()
Mix.Task.run("deps.precompile")
compiled =
deps
|> Enum.sort_by(fn %{app: app} ->
app != :igniter
end)
|> Enum.map(fn %Mix.Dep{app: app, status: status, opts: opts, scm: scm} = dep ->
check_unavailable!(app, scm, status)
maybe_clean(dep, options)
compiled? =
cond do
not is_nil(opts[:compile]) ->
do_compile(dep, config)
Mix.Dep.mix?(dep) ->
do_mix(dep, config)
Mix.Dep.make?(dep) ->
do_make(dep, config)
dep.manager == :rebar3 ->
do_rebar3(dep, config)
true ->
shell.error(
"Could not compile #{inspect(app)}, no \"mix.exs\", \"rebar.config\" or \"Makefile\" " <>
"(pass :compile as an option to customize compilation, set it to \"false\" to do nothing)"
)
false
end
# We should touch fetchable dependencies even if they
# did not compile otherwise they will always be marked
# as stale, even when there is nothing to do.
fetchable? = touch_fetchable(scm, opts[:build])
compiled? and fetchable?
end)
if true in compiled, do: Mix.Task.run("will_recompile"), else: :ok
end
defp maybe_clean(dep, opts) do
# If a dependency was marked as fetched or with an out of date lock
# or missing the app file, we always compile it from scratch.
if Keyword.get(opts, :force, false) or Mix.Dep.compilable?(dep) do
File.rm_rf!(Path.join([Mix.Project.build_path(), "lib", Atom.to_string(dep.app)]))
end
end
defp touch_fetchable(scm, path) do
if scm.fetchable?() do
path = Path.join(path, ".mix")
File.mkdir_p!(path)
File.touch!(Path.join(path, "compile.fetch"))
true
else
false
end
end
defp check_unavailable!(app, scm, {:unavailable, path}) do
if scm.fetchable?() do
Mix.raise(
"Cannot compile dependency #{inspect(app)} because " <>
"it isn't available, run \"mix deps.get\" first"
)
else
Mix.raise(
"Cannot compile dependency #{inspect(app)} because " <>
"it isn't available, please ensure the dependency is at " <>
inspect(Path.relative_to_cwd(path))
)
end
end
defp check_unavailable!(_, _, _) do
:ok
end
defp do_mix(dep, _config) do
Mix.Dep.in_dependency(dep, fn _ ->
config = Mix.Project.config()
if req = old_elixir_req(config) do
Mix.shell().error(
"warning: the dependency #{inspect(dep.app)} requires Elixir #{inspect(req)} " <>
"but you are running on v#{System.version()}"
)
end
try do
options = [
"--from-mix-deps-compile",
"--no-warnings-as-errors",
"--no-code-path-pruning",
"--ignore-module-conflict"
]
reenable_tasks()
res =
Mix.Task.run("compile", options)
reenable_tasks()
match?({:ok, _}, res)
catch
kind, reason ->
app = dep.app
Mix.shell().error(
"could not compile dependency #{inspect(app)}, \"mix compile\" failed. " <>
deps_compile_feedback(app)
)
:erlang.raise(kind, reason, __STACKTRACE__)
end
end)
end
# this is pretty wild, but works for now
# we need to figure out a "blessed" way of doing
# what we're trying to do here :/
# enabling all of the compilers for a given project
# is (for some reason) resulting in the process
# simply existing with no information
defp reenable_tasks do
["compile.elixir", "compile", "compile.all"]
|> Enum.each(&Mix.Task.reenable/1)
end
defp do_rebar3(%Mix.Dep{opts: opts} = dep, config) do
dep_path = opts[:dest]
build_path = opts[:build]
File.mkdir_p!(build_path)
# For Rebar3, we need to copy the source/ebin to the target/ebin
# before we run the command given that REBAR_BARE_COMPILER_OUTPUT_DIR
# writes directly to _build.
File.cp_r(Path.join(dep_path, "ebin"), Path.join(build_path, "ebin"))
# Now establish symlinks to the remaining sources
for dir <- ~w(include priv src) do
Mix.Utils.symlink_or_copy(Path.join(dep_path, dir), Path.join(build_path, dir))
end
# Build the rebar config and setup the command line
config_path = Path.join(build_path, "mix.rebar.config")
lib_path = Path.join(config[:deps_build_path], "lib/*/ebin")
File.write!(config_path, rebar_config(dep))
env = [
# REBAR_BARE_COMPILER_OUTPUT_DIR is only honored by rebar3 >= 3.14
{"REBAR_BARE_COMPILER_OUTPUT_DIR", build_path},
{"REBAR_CONFIG", config_path},
{"REBAR_PROFILE", "prod"},
{"TERM", "dumb"}
]
cmd = "#{rebar_cmd(dep)} bare compile --paths #{escape_path(lib_path)}"
do_command(dep, config, cmd, false, env)
# Check if we have any new symlinks after compilation
for dir <- ~w(include priv src),
File.exists?(Path.join(dep_path, dir)) and not File.exists?(Path.join(build_path, dir)) do
Mix.Utils.symlink_or_copy(Path.join(dep_path, dir), Path.join(build_path, dir))
end
Code.prepend_path(Path.join(build_path, "ebin"), cache: true)
true
end
defp escape_path(path) do
escape = if match?({:win32, _}, :os.type()), do: "^ ", else: "\\ "
String.replace(path, " ", escape)
end
defp rebar_config(dep) do
dep.extra
|> Mix.Rebar.dependency_config()
|> Mix.Rebar.serialize_config()
end
defp rebar_cmd(%Mix.Dep{manager: manager} = dep) do
Mix.Rebar.rebar_cmd(manager) || handle_rebar_not_found(dep)
end
defp handle_rebar_not_found(%Mix.Dep{app: app, manager: manager}) do
shell = Mix.shell()
shell.info(
"Could not find \"#{manager}\", which is needed to build dependency #{inspect(app)}"
)
install_question =
"Shall I install #{manager}? (if running non-interactively, " <>
"use \"mix local.rebar --force\")"
unless shell.yes?(install_question) do
error_message =
"Could not find \"#{manager}\" to compile " <>
"dependency #{inspect(app)}, please ensure \"#{manager}\" is available"
Mix.raise(error_message)
end
Mix.Tasks.Local.Rebar.run(["--force"])
Mix.Rebar.local_rebar_cmd(manager) || Mix.raise("\"#{manager}\" installation failed")
end
defp do_make(dep, config) do
command = make_command(dep)
do_command(dep, config, command, true, [{"IS_DEP", "1"}])
build_structure(dep, config)
true
end
defp make_command(dep) do
makefile_win? = makefile_win?(dep)
command =
case :os.type() do
{:win32, _} when makefile_win? ->
"nmake /F Makefile.win"
{:unix, type} when type in [:dragonfly, :freebsd, :netbsd, :openbsd] ->
"gmake"
_ ->
"make"
end
if erlang_mk?(dep) do
"#{command} clean && #{command}"
else
command
end
end
defp do_compile(%Mix.Dep{opts: opts} = dep, config) do
if command = opts[:compile] do
do_command(dep, config, command, true)
build_structure(dep, config)
true
else
false
end
end
defp do_command(dep, config, command, print_app?, env \\ []) do
%Mix.Dep{app: app, system_env: system_env, opts: opts} = dep
env = [{"ERL_LIBS", Path.join(config[:deps_build_path], "lib")} | system_env] ++ env
if Mix.shell().cmd(command, env: env, print_app: print_app?, cd: opts[:dest]) != 0 do
Mix.raise(
"Could not compile dependency #{inspect(app)}, \"#{command}\" command failed. " <>
deps_compile_feedback(app)
)
end
true
end
defp build_structure(%Mix.Dep{opts: opts}, config) do
config = Keyword.put(config, :deps_app_path, opts[:build])
Mix.Project.build_structure(config, symlink_ebin: true, source: opts[:dest])
Code.prepend_path(Path.join(opts[:build], "ebin"), cache: true)
end
defp old_elixir_req(config) do
req = config[:elixir]
if req && not Version.match?(System.version(), req) do
req
end
end
defp erlang_mk?(%Mix.Dep{opts: opts}) do
File.regular?(Path.join(opts[:dest], "erlang.mk"))
end
defp makefile_win?(%Mix.Dep{opts: opts}) do
File.regular?(Path.join(opts[:dest], "Makefile.win"))
end
defp filter_available_and_local_deps(deps) do
Enum.filter(deps, fn dep ->
Mix.Dep.available?(dep) or not dep.scm.fetchable?()
end)
end
defp deps_compile_feedback(app) do
"Errors may have been logged above. You can recompile this dependency with " <>
"\"mix deps.compile #{app} --force\", update it with \"mix deps.update #{app}\" or " <>
"clean it with \"mix deps.clean #{app}\""
end
end