summaryrefslogtreecommitdiff
path: root/lib/mix/lib/mix
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mix/lib/mix')
-rw-r--r--lib/mix/lib/mix/compilers/elixir.ex23
-rw-r--r--lib/mix/lib/mix/tasks/compile.elixir.ex22
2 files changed, 29 insertions, 16 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")