Skip to content

Commit 502421c

Browse files
committed
Fix env outdated deps compilation
We should only avoid cleaning for path dependencies. Fetch dependencies should go through the previous process. Closes #15467.
1 parent 47b762b commit 502421c

3 files changed

Lines changed: 77 additions & 15 deletions

File tree

lib/mix/lib/mix/dep.ex

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -486,20 +486,12 @@ defmodule Mix.Dep do
486486
@doc """
487487
Returns `true` if the dependency is compilable.
488488
"""
489+
def compilable?(%Mix.Dep{status: {:vsnlock, _}}), do: true
490+
def compilable?(%Mix.Dep{status: {:noappfile, {_, _}}}), do: true
491+
def compilable?(%Mix.Dep{status: {:scmlock, _}}), do: true
492+
def compilable?(%Mix.Dep{status: :compile}), do: true
489493
def compilable?(%Mix.Dep{status: :envoutdated}), do: true
490-
def compilable?(dep), do: force_compilable?(dep)
491-
492-
@doc """
493-
Returns `true` if the dependency is force compilable.
494-
495-
This is a subset of compilable. This is used in `deps.compile` to
496-
clean the build path before compiling.
497-
"""
498-
def force_compilable?(%Mix.Dep{status: {:vsnlock, _}}), do: true
499-
def force_compilable?(%Mix.Dep{status: {:noappfile, {_, _}}}), do: true
500-
def force_compilable?(%Mix.Dep{status: {:scmlock, _}}), do: true
501-
def force_compilable?(%Mix.Dep{status: :compile}), do: true
502-
def force_compilable?(_), do: false
494+
def compilable?(_), do: false
503495

504496
@doc """
505497
Formats a dependency for printing.

lib/mix/lib/mix/tasks/deps.compile.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ defmodule Mix.Tasks.Deps.Compile do
125125

126126
# If a dependency was marked as fetched or with an out of date lock
127127
# or missing the app file, we always compile it from scratch.
128-
if force? or Mix.Dep.force_compilable?(dep) do
128+
if force? or clean_before_compile?(dep) do
129129
File.rm_rf!(Path.join([Mix.Project.build_path(), "lib", Atom.to_string(dep.app)]))
130130
end
131131

@@ -402,6 +402,20 @@ defmodule Mix.Tasks.Deps.Compile do
402402
end
403403
end
404404

405+
# Most compilable statuses mean the dependency source, lock, app file, or build
406+
# metadata changed in a way that requires removing the old build before
407+
# compiling. :envoutdated is different: path dependencies can be incrementally
408+
# recompiled from their existing build, but fetched dependencies must be cleaned
409+
# first so stale BEAM files do not validate against the new compile environment
410+
# before the dependency has a chance to rebuild.
411+
defp clean_before_compile?(%Mix.Dep{status: :envoutdated, scm: scm}) do
412+
scm.fetchable?()
413+
end
414+
415+
defp clean_before_compile?(dep) do
416+
Mix.Dep.compilable?(dep)
417+
end
418+
405419
defp deps_compile_feedback(app) do
406420
if Mix.install?() do
407421
"Errors may have been logged above. You may run Mix.install/2 to try again or " <>

lib/mix/test/mix/tasks/deps_test.exs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ defmodule Mix.Tasks.DepsTest do
7373
end
7474
end
7575

76+
defmodule FetchableGitRepoDepApp do
77+
def project do
78+
[
79+
app: :git_sample,
80+
version: "0.1.0",
81+
deps: [
82+
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
83+
]
84+
]
85+
end
86+
end
87+
7688
## deps
7789

7890
test "prints list of dependencies and their status alphabetically" do
@@ -910,7 +922,51 @@ defmodule Mix.Tasks.DepsTest do
910922
assert Application.spec(:raw_repo, :vsn)
911923
end)
912924
after
913-
Application.delete_env(:raw_repo, :compile_env, persistent: true)
925+
Application.delete_env(:anyapp, :anything, persistent: true)
926+
end
927+
928+
test "recompiles fetchable dependencies when compile env changed" do
929+
in_fixture("deps_status", fn ->
930+
File.mkdir_p!("config")
931+
File.write!("config/config.exs", "import Config\n")
932+
933+
Mix.Project.push(FetchableGitRepoDepApp)
934+
Mix.Tasks.Deps.Get.run([])
935+
936+
File.write!("deps/git_repo/lib/git_repo.ex", """
937+
Application.compile_env(:anyapp, :anything)
938+
939+
defmodule GitRepo do
940+
def hello do
941+
"World"
942+
end
943+
end
944+
""")
945+
946+
Mix.Tasks.Loadconfig.load_compile("config/config.exs")
947+
Mix.Task.run("compile", [])
948+
assert Application.spec(:git_repo, :vsn)
949+
950+
File.write!("config/config.exs", """
951+
import Config
952+
config :anyapp, :anything, :anyvalue
953+
""")
954+
955+
Application.unload(:git_repo)
956+
Mix.ProjectStack.pop()
957+
Mix.Task.clear()
958+
Mix.Project.push(FetchableGitRepoDepApp)
959+
purge([GitRepo])
960+
Mix.Tasks.Loadconfig.load_compile("config/config.exs")
961+
962+
Mix.Task.run("compile", [])
963+
964+
assert_receive {:mix_shell, :info, ["Generated git_repo app"]}
965+
assert Application.spec(:git_repo, :vsn)
966+
end)
967+
after
968+
Application.delete_env(:anyapp, :anything, persistent: true)
969+
purge([GitRepo, GitRepo.MixProject])
914970
end
915971

916972
test "does not compile deps that have explicit option" do

0 commit comments

Comments
 (0)