Skip to content

Commit a5e53b7

Browse files
committed
Do not add optional deps for skipped deps, closes #13410
1 parent cbe0132 commit a5e53b7

File tree

2 files changed

+40
-13
lines changed

2 files changed

+40
-13
lines changed

lib/mix/lib/mix/dep/converger.ex

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ defmodule Mix.Dep.Converger do
159159

160160
defp init_all(main, apps, rest, lock, callback, locked?, env_target, cache) do
161161
state = %{locked?: locked?, env_target: env_target, cache: cache, callback: callback}
162-
{deps, _optional, rest, lock} = all(main, [], [], apps, [], rest, lock, state)
162+
{deps, _kept, _optional, rest, lock} = all(main, [], [], [], apps, [], rest, lock, state)
163163
deps = Enum.reverse(deps)
164164
# When traversing dependencies, we keep skipped ones to
165165
# find conflicts. We remove them now after traversal.
@@ -206,18 +206,19 @@ defmodule Mix.Dep.Converger do
206206
# Now, since "d" was specified in a parent project, no
207207
# exception is going to be raised since d is considered
208208
# to be the authoritative source.
209-
defp all([dep | t], acc, upper, breadths, optional, rest, lock, state) do
209+
defp all([dep | t], acc, kept, upper, breadths, optional, rest, lock, state) do
210210
case match_deps(acc, upper, dep, state.env_target) do
211211
{:replace, dep, acc} ->
212-
all([dep | t], acc, upper, breadths, optional, rest, lock, state)
212+
all([dep | t], acc, kept, upper, breadths, optional, rest, lock, state)
213213

214214
{:match, acc} ->
215-
all(t, acc, upper, breadths, optional, rest, lock, state)
215+
all(t, acc, kept, upper, breadths, optional, rest, lock, state)
216216

217217
:skip ->
218218
# We still keep skipped dependencies around to detect conflicts.
219-
# They must be rejected after every all iteration.
220-
all(t, [dep | acc], upper, breadths, optional, rest, lock, state)
219+
# They must be rejected after every all iteration but they are not
220+
# included in the list of kept dependencies.
221+
all(t, [dep | acc], kept, upper, breadths, optional, rest, lock, state)
221222

222223
:nomatch ->
223224
{%{app: app, deps: deps, opts: opts} = dep, rest, lock} =
@@ -239,21 +240,21 @@ defmodule Mix.Dep.Converger do
239240
# Something that we previously ruled out as an optional dependency is
240241
# no longer a dependency. Add it back for traversal.
241242
{no_longer_optional, optional} = Enum.split_with(optional, &(&1.app == app))
243+
t = no_longer_optional ++ t
242244

243-
{acc, optional, rest, lock} =
244-
all(no_longer_optional ++ t, [dep | acc], upper, breadths, optional, rest, lock, state)
245+
{acc, kept, optional, rest, lock} =
246+
all(t, [dep | acc], [dep.app | kept], upper, breadths, optional, rest, lock, state)
245247

246248
# Now traverse all parent dependencies and see if we have any optional dependency.
247-
{discarded, deps} =
248-
split_non_fulfilled_optional(deps, Enum.map(acc, & &1.app), opts[:from_umbrella])
249+
{discarded, deps} = split_non_fulfilled_optional(deps, kept, opts[:from_umbrella])
249250

250251
new_breadths = Enum.map(deps, & &1.app) ++ breadths
251-
all(deps, acc, breadths, new_breadths, discarded ++ optional, rest, lock, state)
252+
all(deps, acc, kept, breadths, new_breadths, discarded ++ optional, rest, lock, state)
252253
end
253254
end
254255

255-
defp all([], acc, _upper, _current, optional, rest, lock, _state) do
256-
{acc, optional, rest, lock}
256+
defp all([], acc, kept, _upper, _current, optional, rest, lock, _state) do
257+
{acc, kept, optional, rest, lock}
257258
end
258259

259260
defp put_lock(%Mix.Dep{app: app} = dep, lock) do

lib/mix/test/mix/dep_test.exs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,32 @@ defmodule Mix.DepTest do
742742
end)
743743
end
744744

745+
test "does not conflict with optional deps on nested deps (reverse order)" do
746+
Process.put(:custom_deps_git_repo_opts, optional: true)
747+
748+
# deps_repo wants all git_repo, git_repo is restricted to only test
749+
deps = [
750+
{:git_repo, "0.2.0", git: MixTest.Case.fixture_path("git_repo"), only: :test},
751+
{:deps_repo, "0.1.0", path: "custom/deps_repo"}
752+
]
753+
754+
with_deps(deps, fn ->
755+
in_fixture("deps_status", fn ->
756+
loaded = Mix.Dep.Converger.converge([])
757+
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
758+
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
759+
760+
loaded = Mix.Dep.Converger.converge(env: :dev)
761+
assert [:deps_repo] = Enum.map(loaded, & &1.app)
762+
assert [noappfile: {_, _}] = Enum.map(loaded, & &1.status)
763+
764+
loaded = Mix.Dep.Converger.converge(env: :test)
765+
assert [:git_repo, :deps_repo] = Enum.map(loaded, & &1.app)
766+
assert [unavailable: _, noappfile: {_, _}] = Enum.map(loaded, & &1.status)
767+
end)
768+
end)
769+
end
770+
745771
test "does not conflict on valid subsets on nested deps" do
746772
# deps_repo wants git_repo for prod, git_repo is restricted to only prod and test
747773
deps = [

0 commit comments

Comments
 (0)