Skip to content

Commit cc69ab0

Browse files
author
José Valim
committed
Use read timestamp in both Erlang and Elixir compilers
We fetch the time from before we read files so any future change to files are still picked up by the compiler. This timestamp is used when writing beams and the manifest.
1 parent f957d2f commit cc69ab0

File tree

5 files changed

+51
-51
lines changed

5 files changed

+51
-51
lines changed

lib/mix/lib/mix/compilers/elixir.ex

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ defmodule Mix.Compilers.Elixir do
2828
have changed at runtime.
2929
"""
3030
def compile(manifest, srcs, dest, force, opts) do
31+
# We fetch the time from before we read files so any future
32+
# change to files are still picked up by the compiler. This
33+
# timestamp is used when writing beams and the manifest.
34+
timestamp = :calendar.universal_time()
3135
all = Mix.Utils.extract_files(srcs, [:ex])
36+
3237
{all_modules, all_sources} = parse_manifest(manifest, dest)
3338
modified = Mix.Utils.last_modified(manifest)
3439

@@ -70,10 +75,10 @@ defmodule Mix.Compilers.Elixir do
7075

7176
cond do
7277
stale != [] ->
73-
compile_manifest(manifest, modules, sources, stale, dest, opts)
78+
compile_manifest(manifest, modules, sources, stale, dest, timestamp, opts)
7479
:ok
7580
removed != [] ->
76-
write_manifest(manifest, modules, sources, dest)
81+
write_manifest(manifest, modules, sources, dest, timestamp)
7782
:ok
7883
true ->
7984
:noop
@@ -126,7 +131,7 @@ defmodule Mix.Compilers.Elixir do
126131
end
127132
end
128133

129-
defp compile_manifest(manifest, modules, sources, stale, dest, opts) do
134+
defp compile_manifest(manifest, modules, sources, stale, dest, timestamp, opts) do
130135
Mix.Utils.compiling_n(length(stale), :ex)
131136

132137
config = Mix.Project.config()
@@ -156,7 +161,7 @@ defmodule Mix.Compilers.Elixir do
156161
long_compilation_threshold: long_compilation_threshold,
157162
dest: dest] ++ extra
158163
Agent.cast pid, fn {modules, sources} ->
159-
write_manifest(manifest, modules, sources, dest)
164+
write_manifest(manifest, modules, sources, dest, timestamp)
160165
{modules, sources}
161166
end
162167
after
@@ -381,19 +386,20 @@ defmodule Mix.Compilers.Elixir do
381386
end)
382387
end
383388

384-
defp write_manifest(manifest, [], [], _compile_path) do
389+
defp write_manifest(manifest, [], [], _compile_path, _timestamp) do
385390
File.rm(manifest)
386391
:ok
387392
end
388393

389-
defp write_manifest(manifest, modules, sources, compile_path) do
394+
defp write_manifest(manifest, modules, sources, compile_path, timestamp) do
390395
File.mkdir_p!(Path.dirname(manifest))
391396

392397
modules =
393398
for module(beam: beam, binary: binary) = module <- modules do
394399
if binary do
395400
beam_path = Path.join(compile_path, beam)
396401
File.write!(beam_path, binary)
402+
File.touch!(beam_path, timestamp)
397403
end
398404
module(module, binary: nil)
399405
end
@@ -403,6 +409,7 @@ defmodule Mix.Compilers.Elixir do
403409
|> :erlang.term_to_binary(compressed: 9)
404410

405411
File.write!(manifest, manifest_data)
412+
File.touch!(manifest, timestamp)
406413

407414
# Since Elixir is a dependency itself, we need to touch the lock
408415
# so the current Elixir version, used to compile the files above,

lib/mix/lib/mix/compilers/erlang.ex

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ defmodule Mix.Compilers.Erlang do
1818
manifest = Path.join Mix.Project.manifest_path, ".compile.lfe"
1919
dest = Mix.Project.compile_path
2020
21-
compile manifest, [{"src", dest}], :lfe, :beam, opts[:force], fn
21+
compile manifest, [{"src", dest}], :lfe, :beam, opts, fn
2222
input, output ->
2323
:lfe_comp.file(to_erl_file(input),
2424
[output_dir: Path.dirname(output)])
@@ -42,11 +42,17 @@ defmodule Mix.Compilers.Erlang do
4242
of error. An error is raised at the end if any of the
4343
files failed to compile.
4444
"""
45-
def compile(manifest, mappings, src_ext, dest_ext, force, callback) do
46-
files = for {src, dest} <- mappings do
47-
extract_targets(src, src_ext, dest, dest_ext, force)
48-
end |> Enum.concat
49-
compile(manifest, files, src_ext, callback)
45+
def compile(manifest, mappings, src_ext, dest_ext, force, callback) when is_boolean(force) do
46+
compile(manifest, mappings, src_ext, dest_ext, [force: force], callback)
47+
end
48+
49+
def compile(manifest, mappings, src_ext, dest_ext, opts, callback) do
50+
force = opts[:force]
51+
files =
52+
for {src, dest} <- mappings do
53+
extract_targets(src, src_ext, dest, dest_ext, force)
54+
end |> Enum.concat
55+
compile(manifest, files, src_ext, opts, callback)
5056
end
5157

5258
@doc """
@@ -58,14 +64,15 @@ defmodule Mix.Compilers.Erlang do
5864
must be given. A src/dest pair where destination is `nil` is considered
5965
to be up to date and won't be (re-)compiled.
6066
"""
61-
def compile(manifest, mappings, callback) do
62-
compile(manifest, mappings, :erl, callback)
67+
def compile(manifest, mappings, opts \\ [], callback) do
68+
compile(manifest, mappings, :erl, opts, callback)
6369
end
6470

65-
defp compile(manifest, mappings, ext, callback) do
71+
defp compile(manifest, mappings, ext, opts, callback) do
6672
stale = for {:stale, src, dest} <- mappings, do: {src, dest}
6773

6874
# Get the previous entries from the manifest
75+
timestamp = :calendar.universal_time()
6976
entries = read_manifest(manifest)
7077

7178
# Files to remove are the ones in the manifest
@@ -87,15 +94,24 @@ defmodule Mix.Compilers.Erlang do
8794

8895
# Remove manifest entries with no source
8996
Enum.each(removed, &File.rm/1)
97+
verbose = opts[:verbose]
9098

9199
# Compile stale files and print the results
92-
results = for {input, output} <- stale do
93-
callback.(input, output)
94-
end
100+
results =
101+
for {input, output} <- stale do
102+
result = callback.(input, output)
103+
104+
with {:ok, _} <- result do
105+
File.touch!(output, timestamp)
106+
verbose && Mix.shell.info "Compiled #{input}"
107+
end
108+
109+
result
110+
end
95111

96112
# Write final entries to manifest
97113
entries = (entries -- removed) ++ Enum.map(stale, &elem(&1, 1))
98-
write_manifest(manifest, :lists.usort(entries))
114+
write_manifest(manifest, :lists.usort(entries), timestamp)
99115

100116
# Raise if any error, return :ok otherwise
101117
if :error in results do
@@ -162,8 +178,9 @@ defmodule Mix.Compilers.Erlang do
162178
end
163179
end
164180

165-
defp write_manifest(file, entries) do
181+
defp write_manifest(file, entries, timestamp) do
166182
Path.dirname(file) |> File.mkdir_p!
167183
File.write!(file, Enum.join(entries, "\n"))
184+
File.touch!(file, timestamp)
168185
end
169186
end

lib/mix/lib/mix/tasks/compile.erlang.ex

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ defmodule Mix.Tasks.Compile.Erlang do
8282
|> sort_dependencies
8383
|> Enum.map(&annotate_target(&1, compile_path, opts[:force]))
8484

85-
Mix.Compilers.Erlang.compile(manifest(), tuples, fn
85+
Mix.Compilers.Erlang.compile(manifest(), tuples, opts, fn
8686
input, _output ->
8787
# We're purging the module because a previous compiler (e.g. Phoenix)
8888
# might have already loaded the previous version of it.
@@ -91,15 +91,7 @@ defmodule Mix.Tasks.Compile.Erlang do
9191
:code.delete(module)
9292

9393
file = to_erl_file(Path.rootname(input, ".erl"))
94-
case :compile.file(file, erlc_options) do
95-
{:ok, _} = ok ->
96-
if opts[:verbose] do
97-
Mix.shell.info "Compiled #{input}"
98-
end
99-
ok
100-
:error ->
101-
:error
102-
end
94+
:compile.file(file, erlc_options)
10395
end)
10496
end
10597

@@ -181,7 +173,7 @@ defmodule Mix.Tasks.Compile.Erlang do
181173
end
182174

183175
defp annotate_target(erl, compile_path, force) do
184-
beam = Path.join(compile_path, "#{erl.module}#{:code.objfile_extension}")
176+
beam = Path.join(compile_path, "#{erl.module}.beam")
185177

186178
if force || Mix.Utils.stale?([erl.file | erl.includes], [beam]) do
187179
{:stale, erl.file, beam}

lib/mix/lib/mix/tasks/compile.leex.ex

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,11 @@ defmodule Mix.Tasks.Compile.Leex do
4949
mappings = Enum.zip(source_paths, source_paths)
5050
options = project[:leex_options] || []
5151

52-
Erlang.compile(manifest(), mappings, :xrl, :erl, opts[:force], fn
52+
Erlang.compile(manifest(), mappings, :xrl, :erl, opts, fn
5353
input, output ->
5454
Erlang.ensure_application!(:parsetools, input)
5555
options = options ++ @forced_opts ++ [scannerfile: Erlang.to_erl_file(output)]
56-
case :leex.file(Erlang.to_erl_file(input), options) do
57-
{:ok, _} = ok ->
58-
if opts[:verbose] do
59-
Mix.shell.info "Compiled #{input}"
60-
end
61-
ok
62-
:error ->
63-
:error
64-
end
56+
:leex.file(Erlang.to_erl_file(input), options)
6557
end)
6658
end
6759

lib/mix/lib/mix/tasks/compile.yecc.ex

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,11 @@ defmodule Mix.Tasks.Compile.Yecc do
4949
mappings = Enum.zip(source_paths, source_paths)
5050
options = project[:yecc_options] || []
5151

52-
Erlang.compile(manifest(), mappings, :yrl, :erl, opts[:force], fn
52+
Erlang.compile(manifest(), mappings, :yrl, :erl, opts, fn
5353
input, output ->
5454
Erlang.ensure_application!(:parsetools, input)
5555
options = options ++ @forced_opts ++ [parserfile: Erlang.to_erl_file(output)]
56-
case :yecc.file(Erlang.to_erl_file(input), options) do
57-
{:ok, _} = ok ->
58-
if opts[:verbose] do
59-
Mix.shell.info "Compiled #{input}"
60-
end
61-
ok
62-
:error ->
63-
:error
64-
end
56+
:yecc.file(Erlang.to_erl_file(input), options)
6557
end)
6658
end
6759

0 commit comments

Comments
 (0)