summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@dashbit.co>2021-12-10 09:49:47 +0100
committerJosé Valim <jose.valim@dashbit.co>2021-12-10 09:49:47 +0100
commit382c6d2e0e083b15cd02c0eaddd6c187082a600f (patch)
tree0a38e4e8ec176cf0e75b45aa7c5733f537f9a8ff
parent99006eb081cc1cce2426c80732a20a9168d24473 (diff)
downloadelixir-382c6d2e0e083b15cd02c0eaddd6c187082a600f.tar.gz
Track environment changes from compile_env
-rw-r--r--lib/elixir/lib/application.ex12
-rw-r--r--lib/mix/lib/mix/compilers/elixir.ex22
-rw-r--r--lib/mix/test/mix/tasks/compile.elixir_test.exs86
3 files changed, 109 insertions, 11 deletions
diff --git a/lib/elixir/lib/application.ex b/lib/elixir/lib/application.ex
index be2e87414..b1b1a0c89 100644
--- a/lib/elixir/lib/application.ex
+++ b/lib/elixir/lib/application.ex
@@ -53,9 +53,15 @@ defmodule Application do
See the "Configuration" section in the `Mix` module for more information.
You can also change the application environment dynamically by using functions
- such as `put_env/3` and `delete_env/2`. However, as a rule of thumb, each application
- is responsible for its own environment. Please do not use the functions in this
- module for directly accessing or modifying the environment of other applications.
+ such as `put_env/3` and `delete_env/2`.
+
+ > Note: Each application is responsible for its own environment. Do not
+ > use the functions in this module for directly accessing or modifying
+ > the environment of other applications. Whenever you change the application
+ > environment, Elixir's build tool will only recompile the files that
+ > belong to that application. So if you read the application environment
+ > of another application, there is a chance you will be depending on
+ > outdated configuration, as your file won't be recompiled as it changes.
### Compile-time environment
diff --git a/lib/mix/lib/mix/compilers/elixir.ex b/lib/mix/lib/mix/compilers/elixir.ex
index 15ad7a75b..fe3ca6e3b 100644
--- a/lib/mix/lib/mix/compilers/elixir.ex
+++ b/lib/mix/lib/mix/compilers/elixir.ex
@@ -75,9 +75,8 @@ defmodule Mix.Compilers.Elixir do
new_lock = Enum.sort(Mix.Dep.Lock.read())
new_config = Enum.sort(Mix.Tasks.Loadconfig.read_compile())
- apps = []
- apps = merge_appset(old_lock, new_lock, apps)
- apps = merge_appset(old_config, new_config, apps)
+ config_apps = merge_appset(old_config, new_config, [])
+ apps = merge_appset(old_lock, new_lock, config_apps)
if Mix.Project.config()[:app] in apps do
{true, stale, new_lock, new_config}
@@ -95,7 +94,14 @@ defmodule Mix.Compilers.Elixir do
end
end)
- {false, stale ++ apps_stale, new_lock, new_config}
+ compile_env_stale =
+ for source(compile_env: compile_env, modules: modules) <- all_sources,
+ Enum.any?(config_apps, &List.keymember?(compile_env, &1, 0)),
+ module <- modules,
+ do: module
+
+ stale = (stale ++ compile_env_stale) ++ apps_stale
+ {false, stale, new_lock, new_config}
end
true ->
@@ -288,8 +294,8 @@ defmodule Mix.Compilers.Elixir do
dest
) do
modules_to_recompile =
- for module(module: module, recompile?: true) <- all_modules,
- recompile_module?(module),
+ for module(module: module, recompile?: recompile?) <- all_modules,
+ recompile_module?(module, recompile?) or Map.has_key?(stale_local_mods, module),
do: module
{checkpoint_stale, checkpoint_modules} = parse_checkpoint(manifest)
@@ -495,8 +501,8 @@ defmodule Mix.Compilers.Elixir do
:ok
end
- defp recompile_module?(module) do
- Code.ensure_loaded?(module) and
+ defp recompile_module?(module, recompile?) do
+ recompile? and Code.ensure_loaded?(module) and
function_exported?(module, :__mix_recompile__?, 0) and
module.__mix_recompile__?()
end
diff --git a/lib/mix/test/mix/tasks/compile.elixir_test.exs b/lib/mix/test/mix/tasks/compile.elixir_test.exs
index 8566fbab9..bf9fcc86e 100644
--- a/lib/mix/test/mix/tasks/compile.elixir_test.exs
+++ b/lib/mix/test/mix/tasks/compile.elixir_test.exs
@@ -324,6 +324,92 @@ defmodule Mix.Tasks.Compile.ElixirTest do
Application.put_env(:logger, :compile_time_purge_matching, [])
end
+ test "recompiles files when config changes via compile_env" do
+ in_fixture("no_mixfile", fn ->
+ Mix.Project.push(MixTest.Case.Sample)
+ File.mkdir_p!("config")
+
+ File.write!("lib/a.ex", """
+ defmodule A do
+ _ = Application.compile_env(:logger, :level)
+ end
+ """)
+
+ assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
+ assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
+ assert_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
+
+ recompile = fn ->
+ Mix.ProjectStack.pop()
+ Mix.Project.push(MixTest.Case.Sample)
+ Mix.Tasks.Loadconfig.load_compile("config/config.exs")
+ Mix.Tasks.Compile.Elixir.run(["--verbose"])
+ end
+
+ # Adding config recompiles
+ File.write!("config/config.exs", """
+ import Config
+ config :logger, :level, :debug
+ """)
+
+ File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
+ assert recompile.() == {:ok, []}
+ assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
+ refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
+ assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
+
+ # Changing config recompiles
+ File.write!("config/config.exs", """
+ import Config
+ config :logger, :level, :info
+ """)
+
+ File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
+ assert recompile.() == {:ok, []}
+ assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
+ refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
+ assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
+
+ # Removing config recompiles
+ File.write!("config/config.exs", """
+ import Config
+ """)
+
+ File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
+ assert recompile.() == {:ok, []}
+ assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
+ refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
+ assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
+
+ # Changing self fully recompiles
+ File.write!("config/config.exs", """
+ import Config
+ config :sample, :foo, :bar
+ """)
+
+ File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
+ assert recompile.() == {:ok, []}
+ assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
+ assert_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
+ assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
+
+ # Changing an unknown dependency returns :ok but does not recompile
+ File.write!("config/config.exs", """
+ import Config
+ config :sample, :foo, :bar
+ config :unknown, :unknown, :unknown
+ """)
+
+ # We use ensure_touched because an outdated manifest would recompile anyway.
+ ensure_touched("config/config.exs", "_build/dev/lib/sample/.mix/compile.elixir")
+ assert recompile.() == {:ok, []}
+ refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
+ refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
+ end)
+ after
+ Application.delete_env(:sample, :foo, persistent: true)
+ end
+
test "recompiles files when lock changes" do
in_fixture("no_mixfile", fn ->
Mix.Project.push(MixTest.Case.Sample)