Skip to content

Commit 4d55fa6

Browse files
josevalimJosé Valim
authored andcommitted
Accordingly mark top level optional dependencies from child deps (#8035)
Signed-off-by: José Valim <[email protected]>
1 parent b9d6f4f commit 4d55fa6

File tree

2 files changed

+98
-3
lines changed

2 files changed

+98
-3
lines changed

lib/mix/lib/mix/dep.ex

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,42 @@ defmodule Mix.Dep do
117117
end
118118
end
119119

120+
# optional and runtime only matter at the top level.
121+
# Any non-top level dependency that is optional and
122+
# is still available means it has been fulfilled.
123+
@child_keep_opts [:optional, :runtime]
124+
120125
defp load_and_cache(_config, top, top, env) do
121126
converge(env: env)
122127
end
123128

124129
defp load_and_cache(config, _top, bottom, _env) do
125130
{_, deps} = Mix.ProjectStack.read_cache({:cached_deps, bottom})
126131
app = Keyword.fetch!(config, :app)
127-
top_level = for dep <- deps, dep.app == app, child <- dep.deps, do: child.app
128-
129132
seen = populate_seen(MapSet.new(), [app])
130133
children = get_deps(deps, tl(Enum.uniq(get_children(deps, seen, [app]))))
131-
Enum.map(children, &%{&1 | top_level: &1.app in top_level})
134+
135+
top_level =
136+
for dep <- deps,
137+
dep.app == app,
138+
child <- dep.deps,
139+
do: {child.app, Keyword.take(child.opts, @child_keep_opts)},
140+
into: %{}
141+
142+
Enum.map(children, fn %{app: app} = dep ->
143+
case top_level do
144+
%{^app => child_opts} ->
145+
opts =
146+
dep.opts
147+
|> Keyword.drop(@child_keep_opts)
148+
|> Keyword.merge(child_opts)
149+
150+
%{dep | top_level: true, opts: opts}
151+
152+
%{} ->
153+
%{dep | top_level: false}
154+
end
155+
end)
132156
end
133157

134158
defp read_cached_deps(project, env) do

lib/mix/test/mix/dep_test.exs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,77 @@ defmodule Mix.DepTest do
509509
end)
510510
end
511511

512+
test "nested deps with optional matching" do
513+
Process.put(:custom_deps_git_repo_opts, optional: true)
514+
515+
# deps_repo brings git_repo but it is optional
516+
deps = [
517+
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
518+
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
519+
]
520+
521+
with_deps(deps, fn ->
522+
in_fixture("deps_status", fn ->
523+
File.mkdir_p!("custom/deps_repo/lib")
524+
525+
File.write!("custom/deps_repo/lib/a.ex", """
526+
# Check that the child dependency is top_level and optional
527+
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
528+
true = Keyword.fetch!(opts, :optional)
529+
""")
530+
531+
Mix.Tasks.Deps.Get.run([])
532+
Mix.Tasks.Deps.Compile.run([])
533+
end)
534+
end)
535+
end
536+
537+
test "nested deps with runtime override on parent" do
538+
Process.put(:custom_deps_git_repo_opts, runtime: false)
539+
540+
deps = [
541+
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
542+
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
543+
]
544+
545+
with_deps(deps, fn ->
546+
in_fixture("deps_status", fn ->
547+
File.mkdir_p!("custom/deps_repo/lib")
548+
549+
File.write!("custom/deps_repo/lib/a.ex", """
550+
# Check that the child dependency is top_level and optional
551+
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
552+
false = Keyword.fetch!(opts, :runtime)
553+
""")
554+
555+
Mix.Tasks.Deps.Get.run([])
556+
Mix.Tasks.Deps.Compile.run([])
557+
end)
558+
end)
559+
end
560+
561+
test "nested deps with runtime override on child" do
562+
deps = [
563+
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
564+
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo"), runtime: false}
565+
]
566+
567+
with_deps(deps, fn ->
568+
in_fixture("deps_status", fn ->
569+
File.mkdir_p!("custom/deps_repo/lib")
570+
571+
File.write!("custom/deps_repo/lib/a.ex", """
572+
# Check that the child dependency is top_level and optional
573+
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
574+
false = Keyword.has_key?(opts, :runtime)
575+
""")
576+
577+
Mix.Tasks.Deps.Get.run([])
578+
Mix.Tasks.Deps.Compile.run([])
579+
end)
580+
end)
581+
end
582+
512583
test "nested deps with overrides" do
513584
# deps_repo brings git_repo but it is overriden
514585
deps = [

0 commit comments

Comments
 (0)