Skip to content

Commit

Permalink
Do not always load applications during convergence, closes #12682
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Sep 12, 2023
1 parent 34010cf commit d46fa4b
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 117 deletions.
37 changes: 16 additions & 21 deletions lib/mix/lib/mix/dep.ex
Expand Up @@ -114,12 +114,12 @@ defmodule Mix.Dep do
write_cached_deps(top, {env, target}, load_and_cache(config, top, bottom, env, target))

_ ->
converge(env: env, target: target)
converge_and_load(env: env, target: target)
end
end

defp load_and_cache(_config, top, top, env, target) do
converge(env: env, target: target)
converge_and_load(env: env, target: target)
end

defp load_and_cache(config, _top, bottom, _env, _target) do
Expand Down Expand Up @@ -152,6 +152,20 @@ defmodule Mix.Dep do
end)
end

defp converge_and_load(opts) do
for %{app: app, opts: opts} = dep <- Mix.Dep.Converger.converge(opts) do
case Keyword.pop(opts, :app_properties) do
{nil, _opts} ->
dep

{app_properties, opts} ->
# We don't raise because child dependencies may be missing if manually cleaned
:application.load({:application, app, app_properties})
%{dep | opts: opts}
end
end
end

defp read_cached_deps(project, env_target) do
case Mix.State.read_cache({:cached_deps, project}) do
{^env_target, deps} -> deps
Expand All @@ -174,25 +188,6 @@ defmodule Mix.Dep do
end
end

@doc """
Returns loaded dependencies recursively on the given environment.
If no environment is passed, dependencies are loaded across all
environments. The result is not cached.
## Exceptions
This function raises an exception if any of the dependencies
provided in the project are in the wrong format.
"""
def load_on_environment(opts) do
converge(opts)
end

defp converge(opts) do
Mix.Dep.Converger.converge(nil, nil, opts, &{&1, &2, &3}) |> elem(0)
end

@doc """
Filters the given dependencies by name.
Expand Down
40 changes: 16 additions & 24 deletions lib/mix/lib/mix/dep/converger.ex
Expand Up @@ -60,6 +60,18 @@ defmodule Mix.Dep.Converger do
)
end

@doc """
Converge without lock and accumulator updates.
Note the dependencies returned from converge are not yet loaded.
The relevant app keys are found under `dep.opts[:app_properties]`.
See `Mix.Dep.Loader.children/1` for options.
"""
def converge(opts \\ []) do
converge(nil, nil, opts, &{&1, &2, &3}) |> elem(0)
end

@doc """
Converges all dependencies from the current project,
including nested dependencies.
Expand All @@ -68,35 +80,15 @@ defmodule Mix.Dep.Converger do
must return an updated dependency in case some processing
is done.
Note the dependencies returned from converge are not yet loaded.
The relevant app keys are found under `dep.opts[:app_properties]`.
See `Mix.Dep.Loader.children/1` for options.
"""
def converge(acc, lock, opts, callback) do
{deps, acc, lock} = all(acc, lock, opts, callback)
if remote = Mix.RemoteConverger.get(), do: remote.post_converge()

deps =
for %{app: app, opts: opts} = dep <- topological_sort(deps) do
case Keyword.pop(opts, :app_properties) do
{nil, _opts} ->
dep

{app_properties, opts} ->
case :application.load({:application, app, app_properties}) do
:ok ->
:ok

{:error, {:already_loaded, _}} ->
:ok

{:error, error} ->
Mix.raise("Could not load application #{inspect(app)}: #{inspect(error)}")
end

%{dep | opts: opts}
end
end

{deps, acc, lock}
{topological_sort(deps), acc, lock}
end

defp all(acc, lock, opts, callback) do
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.clean.ex
Expand Up @@ -41,7 +41,7 @@ defmodule Mix.Tasks.Deps.Clean do
value = opts[switch],
do: {key, :"#{value}"}

loaded_deps = Mix.Dep.load_on_environment(loaded_opts)
loaded_deps = Mix.Dep.Converger.converge(loaded_opts)

apps_to_clean =
cond do
Expand Down
3 changes: 0 additions & 3 deletions lib/mix/lib/mix/tasks/deps.compile.ex
Expand Up @@ -57,9 +57,6 @@ defmodule Mix.Tasks.Deps.Compile do

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
# or local dependency.
compile(filter_available_and_local_deps(deps), opts)

{opts, tail, _} ->
Expand Down
4 changes: 2 additions & 2 deletions lib/mix/lib/mix/tasks/deps.ex
@@ -1,7 +1,7 @@
defmodule Mix.Tasks.Deps do
use Mix.Task

import Mix.Dep, only: [load_on_environment: 1, format_dep: 1, format_status: 1, check_lock: 1]
import Mix.Dep, only: [format_dep: 1, format_status: 1, check_lock: 1]

@shortdoc "Lists dependencies and their status"

Expand Down Expand Up @@ -167,7 +167,7 @@ defmodule Mix.Tasks.Deps do

shell = Mix.shell()

load_on_environment(loaded_opts)
Mix.Dep.Converger.converge(loaded_opts)
|> Enum.sort_by(& &1.app)
|> Enum.each(fn dep ->
%Mix.Dep{scm: scm, manager: manager} = dep
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.tree.ex
Expand Up @@ -44,7 +44,7 @@ defmodule Mix.Tasks.Deps.Tree do
value = opts[switch],
do: {key, :"#{value}"}

deps = Mix.Dep.load_on_environment(deps_opts)
deps = Mix.Dep.Converger.converge(deps_opts)

root =
case args do
Expand Down
2 changes: 1 addition & 1 deletion lib/mix/lib/mix/tasks/deps.unlock.ex
Expand Up @@ -83,7 +83,7 @@ defmodule Mix.Tasks.Deps.Unlock do
end

defp unused_apps(lock) do
apps = Mix.Dep.load_on_environment([]) |> Enum.map(& &1.app)
apps = Mix.Dep.Converger.converge([]) |> Enum.map(& &1.app)

lock
|> Map.drop(apps)
Expand Down

0 comments on commit d46fa4b

Please sign in to comment.