Skip to content

Run formatter on deps.compile #6894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 109 additions & 66 deletions lib/mix/lib/mix/tasks/deps.compile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,59 +30,68 @@ defmodule Mix.Tasks.Deps.Compile do
`b` is included in the compilation step, pass `--include-children`.
"""

import Mix.Dep, only: [loaded: 1, available?: 1, loaded_by_name: 2,
make?: 1, mix?: 1]
import Mix.Dep, only: [loaded: 1, available?: 1, loaded_by_name: 2, make?: 1, mix?: 1]

@switches [include_children: :boolean, force: :boolean]

@spec run(OptionParser.argv) :: :ok
@spec run(OptionParser.argv()) :: :ok
def run(args) do
unless "--no-archives-check" in args do
Mix.Task.run "archive.check", args
Mix.Task.run("archive.check", args)
end

Mix.Project.get!
Mix.Project.get!()

case OptionParser.parse(args, switches: @switches) do
{opts, [], _} ->
# Because this command may be invoked explicitly with
# deps.compile, we simply try to compile any available
# dependency.
compile(Enum.filter(loaded(env: Mix.env), &available?/1), opts)
compile(Enum.filter(loaded(env: Mix.env()), &available?/1), opts)

{opts, tail, _} ->
compile(loaded_by_name(tail, [env: Mix.env] ++ opts), opts)
compile(loaded_by_name(tail, [env: Mix.env()] ++ opts), opts)
end
end

@doc false
def compile(deps, options \\ []) do
shell = Mix.shell
config = Mix.Project.deps_config
shell = Mix.shell()
config = Mix.Project.deps_config()

Mix.Task.run "deps.precompile"
Mix.Task.run("deps.precompile")

compiled =
Enum.map(deps, fn %Mix.Dep{app: app, status: status, opts: opts, scm: scm} = dep ->
check_unavailable!(app, status)

maybe_clean(app, options)

compiled? = cond do
not is_nil(opts[:compile]) ->
do_compile dep, config
mix?(dep) ->
do_mix dep, config
make?(dep) ->
do_make dep, config
dep.manager == :rebar ->
do_rebar 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
compiled? =
cond do
not is_nil(opts[:compile]) ->
do_compile(dep, config)

mix?(dep) ->
do_mix(dep, config)

make?(dep) ->
do_make(dep, config)

dep.manager == :rebar ->
do_rebar(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

unless mix?(dep), do: build_structure(dep, config)
# We should touch fetchable dependencies even if they
Expand All @@ -92,12 +101,12 @@ defmodule Mix.Tasks.Deps.Compile do
compiled? and fetchable?
end)

if true in compiled, do: Mix.Dep.Lock.touch_manifest, else: :ok
if true in compiled, do: Mix.Dep.Lock.touch_manifest(), else: :ok
end

defp maybe_clean(app, opts) do
if Keyword.get(opts, :force, false) do
File.rm_rf! Path.join [Mix.Project.build_path, "lib", Atom.to_string(app)]
File.rm_rf!(Path.join([Mix.Project.build_path(), "lib", Atom.to_string(app)]))
end
end

Expand All @@ -112,83 +121,109 @@ defmodule Mix.Tasks.Deps.Compile do
end

defp check_unavailable!(app, {:unavailable, _}) do
Mix.raise "Cannot compile dependency #{inspect app} because " <>
"it isn't available, run \"mix deps.get\" first"
Mix.raise(
"Cannot compile dependency #{inspect(app)} because " <>
"it isn't available, run \"mix deps.get\" first"
)
end

defp check_unavailable!(_, _) do
:ok
end

defp do_mix(dep, _config) do
Mix.Dep.in_dependency dep, fn _ ->
if req = old_elixir_req(Mix.Project.config) do
Mix.shell.error "warning: the dependency #{inspect dep.app} requires Elixir #{inspect req} " <>
"but you are running on v#{System.version}"
Mix.Dep.in_dependency(dep, fn _ ->
if req = old_elixir_req(Mix.Project.config()) do
Mix.shell().error(
"warning: the dependency #{inspect(dep.app)} requires Elixir #{inspect(req)} " <>
"but you are running on v#{System.version()}"
)
end

# Force recompilation on compile status
if dep.status == :compile do
Mix.Dep.Lock.touch_manifest
Mix.Dep.Lock.touch_manifest()
end

try do
res = Mix.Task.run("compile", ["--no-deps", "--no-archives-check",
"--no-elixir-version-check", "--no-warnings-as-errors"])
options = [
"--no-deps",
"--no-archives-check",
"--no-elixir-version-check",
"--no-warnings-as-errors"
]

res = Mix.Task.run("compile", options)

match?({:ok, _}, res)
catch
kind, reason ->
stacktrace = System.stacktrace
stacktrace = System.stacktrace()
app = dep.app
Mix.shell.error "could not compile dependency #{inspect app}, \"mix compile\" failed. " <>
"You can recompile this dependency with \"mix deps.compile #{app}\", update it " <>
"with \"mix deps.update #{app}\" or clean it with \"mix deps.clean #{app}\""

Mix.shell().error(
"could not compile dependency #{inspect(app)}, \"mix compile\" failed. " <>
"You can recompile this dependency with \"mix deps.compile #{app}\", update it " <>
"with \"mix deps.update #{app}\" or clean it with \"mix deps.clean #{app}\""
)

:erlang.raise(kind, reason, stacktrace)
end
end
end)
end

defp do_rebar(dep, config) do
lib_path = Path.join(config[:env_path], "lib")
cmd = "#{rebar_cmd(dep)} compile skip_deps=true deps_dir=#{inspect lib_path}"
do_command dep, config, cmd, false
cmd = "#{rebar_cmd(dep)} compile skip_deps=true deps_dir=#{inspect(lib_path)}"
do_command(dep, config, cmd, false)
end

defp do_rebar3(%Mix.Dep{opts: opts} = dep, config) do
dep_path = opts[:build]
dep_path = opts[:build]
config_path = Path.join(dep_path, "mix.rebar.config")
lib_path = Path.join(config[:env_path], "lib/*/ebin")
lib_path = Path.join(config[:env_path], "lib/*/ebin")

env = [{"REBAR_CONFIG", config_path}, {"TERM", "dumb"}]
cmd = "#{rebar_cmd(dep)} bare compile --paths #{inspect lib_path}"
cmd = "#{rebar_cmd(dep)} bare compile --paths #{inspect(lib_path)}"

File.mkdir_p!(dep_path)
File.write!(config_path, rebar_config(dep))
do_command dep, config, cmd, false, env
do_command(dep, config, cmd, false, env)
end

defp rebar_config(dep) do
dep.extra
|> Mix.Rebar.dependency_config
|> Mix.Rebar.serialize_config
|> 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}"
shell.info "I can install a local copy which is just used by Mix"
shell = Mix.shell()

shell.info(
"Could not find \"#{manager}\", which is needed to build dependency #{inspect(app)}"
)

unless shell.yes?("Shall I install #{manager}? (if running non-interactively, use \"mix local.rebar --force\")") do
Mix.raise "Could not find \"#{manager}\" to compile " <>
"dependency #{inspect app}, please ensure \"#{manager}\" is available"
shell.info("I can install a local copy which is just used by Mix")

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([]) && Mix.Rebar.local_rebar_cmd(manager)) ||
Mix.raise "\"#{manager}\" installation failed"
Mix.raise("\"#{manager}\" installation failed")
end

defp do_make(dep, config) do
Expand All @@ -200,11 +235,13 @@ defmodule Mix.Tasks.Deps.Compile do
makefile_win? = makefile_win?(dep)

command =
case :os.type do
case :os.type() do
{:win32, _} when makefile_win? ->
"nmake /F Makefile.win"

{:unix, type} when type in [:freebsd, :openbsd] ->
"gmake"

_ ->
"make"
end
Expand All @@ -227,34 +264,40 @@ defmodule Mix.Tasks.Deps.Compile do
defp do_command(%Mix.Dep{app: app, opts: opts}, config, command, print_app?, env \\ []) do
File.cd!(opts[:dest], fn ->
env = [{"ERL_LIBS", Path.join(config[:env_path], "lib")}] ++ env

if Mix.Shell.cmd(command, env: env, into: %Mix.Shell{print_app?: print_app?}) != 0 do
Mix.raise "Could not compile dependency #{inspect app}, \"#{command}\" command failed. " <>
"You can recompile this dependency with \"mix deps.compile #{app}\", update it " <>
"with \"mix deps.update #{app}\" or clean it with \"mix deps.clean #{app}\""
Mix.raise(
"Could not compile dependency #{inspect(app)}, \"#{command}\" command failed. " <>
"You can recompile this dependency with \"mix deps.compile #{app}\", update it " <>
"with \"mix deps.update #{app}\" or clean it with \"mix deps.clean #{app}\""
)
end
end)

true
end

defp build_structure(%Mix.Dep{opts: opts} = dep, config) do
build_path = Path.dirname(opts[:build])
Enum.each Mix.Dep.source_paths(dep), fn {source, base} ->

Enum.each(Mix.Dep.source_paths(dep), fn {source, base} ->
app = Path.join(build_path, base)
build_structure(source, app, config)
Code.prepend_path(Path.join(app, "ebin"))
end
end)
end

defp build_structure(dest, build, config) do
File.cd! dest, fn ->
File.cd!(dest, fn ->
config = Keyword.put(config, :app_path, build)
Mix.Project.build_structure(config, symlink_ebin: true)
end
end)
end

defp old_elixir_req(config) do
req = config[:elixir]
if req && not Version.match?(System.version, req) do

if req && not Version.match?(System.version(), req) do
req
end
end
Expand Down