diff options
Diffstat (limited to 'lib/mix')
-rw-r--r-- | lib/mix/lib/mix/compilers/elixir.ex | 23 | ||||
-rw-r--r-- | lib/mix/lib/mix/tasks/compile.elixir.ex | 22 | ||||
-rw-r--r-- | lib/mix/test/mix/tasks/compile.elixir_test.exs | 6 |
3 files changed, 34 insertions, 17 deletions
diff --git a/lib/mix/lib/mix/compilers/elixir.ex b/lib/mix/lib/mix/compilers/elixir.ex index 004331018..8accc7cba 100644 --- a/lib/mix/lib/mix/compilers/elixir.ex +++ b/lib/mix/lib/mix/compilers/elixir.ex @@ -1,7 +1,7 @@ defmodule Mix.Compilers.Elixir do @moduledoc false - @manifest_vsn 17 + @manifest_vsn 18 @checkpoint_vsn 2 import Record @@ -393,7 +393,7 @@ defmodule Mix.Compilers.Elixir do Enum.any?(modules, &Map.has_key?(modules_to_recompile, &1)) or Enum.any?(external, &stale_external?(&1, modified, sources_stats)) or (last_mtime > modified and - (missing_beam_file?(dest, modules) or digest != digest(source))), + (missing_beam_file?(dest, modules) or digest != digest_file!(source))), do: source changed = new_paths ++ changed @@ -420,10 +420,10 @@ defmodule Mix.Compilers.Elixir do {modules, exports, changed, sources_stats} end - defp stale_external?({external, existed?}, modified, sources_stats) do + defp stale_external?({external, digest}, modified, sources_stats) do case sources_stats do - %{^external => {0, 0}} -> existed? - %{^external => {mtime, _}} -> mtime > modified + %{^external => {0, 0}} -> digest != nil + %{^external => {mtime, _}} -> mtime > modified and digest != digest_file!(external) end end @@ -437,9 +437,11 @@ defmodule Mix.Compilers.Elixir do end) end - defp digest(file) do - contents = File.read!(file) + defp digest_file!(file) do + file |> File.read!() |> digest_contents() + end + defp digest_contents(contents) do case :erlang.system_info(:wordsize) do 8 -> :crypto.hash(:blake2b, contents) _ -> :crypto.hash(:blake2s, contents) @@ -1065,7 +1067,7 @@ defmodule Mix.Compilers.Elixir do source( source, # We preserve the digest if the file is recompiled but not changed - digest: source(source, :digest) || digest(file), + digest: source(source, :digest) || digest_file!(file), compile_references: compile_references, export_references: export_references, runtime_references: runtime_references, @@ -1149,7 +1151,10 @@ defmodule Mix.Compilers.Elixir do defp process_external_resources(external, cwd) do for file <- external do - {Path.relative_to(file, cwd), File.exists?(file)} + case File.read(file) do + {:ok, binary} -> {Path.relative_to(file, cwd), digest_contents(binary)} + {:error, _} -> {Path.relative_to(file, cwd), nil} + end end end end diff --git a/lib/mix/lib/mix/tasks/compile.elixir.ex b/lib/mix/lib/mix/tasks/compile.elixir.ex index 87776af56..a8d1ac05e 100644 --- a/lib/mix/lib/mix/tasks/compile.elixir.ex +++ b/lib/mix/lib/mix/tasks/compile.elixir.ex @@ -9,19 +9,27 @@ defmodule Mix.Tasks.Compile.Elixir do Elixir is smart enough to recompile only files that have changed and their dependencies. This means if `lib/a.ex` is invoking - a function defined over `lib/b.ex`, whenever `lib/b.ex` changes, - `lib/a.ex` is also recompiled. + a function defined over `lib/b.ex` at compile time, whenever + `lib/b.ex` changes, `lib/a.ex` is also recompiled. - Note it is important to recompile a file's dependencies as - there are often compile time dependencies between them. + Note Elixir considers a file as changed if its source file has + changed on disk since the last compilation AND its contents are + no longer the same. + + ## `@external_resource` + + If a module depends on external files, those can be annotated + with the `@external_resource` module attribute. If these files + change, the Elixir module is automatically recompiled. ## `__mix_recompile__?/0` A module may export a `__mix_recompile__?/0` function that can cause the module to be recompiled using custom rules. For example, - `@external_resource` already adds a compile-time dependency on an - external file, however, to depend on a _dynamic_ list of files we - can do: + to recompile whenever a file is changed in a given directory, you + can use a combination of `@external_resource` for existing files + and a `__mix_recompile__?/0` check to verify when new entries are + added to the directory itself: defmodule MyModule do paths = Path.wildcard("*.txt") diff --git a/lib/mix/test/mix/tasks/compile.elixir_test.exs b/lib/mix/test/mix/tasks/compile.elixir_test.exs index c2e32f7d3..e18cefc00 100644 --- a/lib/mix/test/mix/tasks/compile.elixir_test.exs +++ b/lib/mix/test/mix/tasks/compile.elixir_test.exs @@ -983,7 +983,11 @@ defmodule Mix.Tasks.Compile.ElixirTest do Mix.shell().flush purge([A, B]) - # Update local existing resource + # Update local existing resource timestamp is not enough + File.touch!("lib/a.eex", {{2038, 1, 1}, {0, 0, 0}}) + assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:noop, []} + + File.write!("lib/a.eex", [File.read!("lib/a.eex"), ?\n]) File.touch!("lib/a.eex", {{2038, 1, 1}, {0, 0, 0}}) assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []} assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]} |