Skip to content

Commit b8689da

Browse files
author
José Valim
committed
Small refactoring on Mix tasks
1 parent a05592f commit b8689da

File tree

6 files changed

+125
-158
lines changed

6 files changed

+125
-158
lines changed

lib/mix/lib/mix/project.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,17 @@ defmodule Mix.Project do
129129

130130
defp default_config do
131131
[ compile_path: "ebin",
132-
elixirc_exts: [:ex],
133132
default_env: [test: :test],
134133
default_task: "run",
135134
deps_path: "deps",
136-
erlc_paths: ["src"],
137-
erlc_include_path: "include",
138-
erlc_options: [:debug_info],
135+
elixirc_exts: [:ex],
136+
elixirc_paths: ["lib"],
137+
elixirc_watch_exts: [:ex, :eex, :exs],
139138
lockfile: "mix.lock",
140139
prepare_task: "app.start",
141-
elixirc_paths: ["lib"],
142-
elixirc_watch_exts: [:ex, :eex, :exs] ]
140+
erlc_paths: ["src"],
141+
erlc_include_path: "include",
142+
erlc_options: [:debug_info] ]
143143
end
144144

145145
defp get_project_config(nil), do: []

lib/mix/lib/mix/tasks/clean.ex

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
defmodule Mix.Tasks.Clean do
22
use Mix.Task
3+
alias Mix.Tasks.Compile.Erlang
34

45
@shortdoc "Clean generated application files"
56

@@ -15,10 +16,11 @@ defmodule Mix.Tasks.Clean do
1516
{ opts, _ } = OptionParser.parse(args)
1617
File.rm_rf Mix.project[:compile_path] || "ebin"
1718

18-
generated_source = lc source_path inlist Mix.project[:erlc_paths] do
19-
Mix.Utils.check_files(source_path, [:yrl, :xrl], source_path, :erl)
20-
end |> List.flatten
21-
lc {_, output} inlist generated_source, do: File.rm_rf output
19+
Enum.each Mix.project[:erlc_paths], fn(source_path) ->
20+
pairs = Erlang.extract_stale_pairs(source_path, [:yrl, :xrl], source_path, :erl, true)
21+
Enum.each pairs, fn { _, output } -> File.rm_rf output end
22+
end
23+
2224
if opts[:all], do: Mix.Task.run("deps.clean")
2325
end
2426
end
Lines changed: 81 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
defmodule Mix.Tasks.Compile.Erlang do
2-
32
alias :epp, as: Epp
43
alias :digraph, as: Graph
54
alias :digraph_utils, as: GraphUtils
6-
alias :code, as: Code
7-
alias :compile, as: Compiler
85
alias Mix.Utils
6+
97
use Mix.Task
108

119
@hidden true
@@ -29,7 +27,7 @@ defmodule Mix.Tasks.Compile.Erlang do
2927
3028
## Command line options
3129
32-
* `--force` - forces compilation regardless of module times;
30+
* `--force` - forces compilation regardless of module times
3331
3432
## Configuration
3533
@@ -47,19 +45,11 @@ defmodule Mix.Tasks.Compile.Erlang do
4745
4846
[`erlc_include_path`: "other"]
4947
50-
* `:erlc_options` - compilation options that applies
51-
to Erlang's compiler.
52-
This options are setted:
53-
54-
:outdir to a configured :compile_path
55-
:i to a configured :include_path
56-
:report
57-
58-
and :debug_info in project configuration
48+
* `:erlc_options` - compilation options that applies to Erlang's
49+
compiler. `:debug_info` is enabled by default.
5950
60-
There are many other available options here:
51+
There are many available options here:
6152
http://www.erlang.org/doc/man/compile.html#file-2
62-
6353
"""
6454

6555
defrecord Erl, file: nil, module: nil, behaviours: [], compile: [],
@@ -68,23 +58,26 @@ defmodule Mix.Tasks.Compile.Erlang do
6858
def run(args) do
6959
{ opts, _ } = OptionParser.parse(args, switches: [force: :boolean])
7060

71-
project = Mix.project
61+
project = Mix.project
7262
source_paths = project[:erlc_paths]
7363
files = Mix.Utils.extract_files(source_paths, [:erl])
7464
compile_path = to_erl_file project[:compile_path]
7565
include_path = to_erl_file project[:erlc_include_path]
7666

77-
erlc_options = [{:outdir, compile_path}, {:i, include_path}, :report
78-
| project[:erlc_options] || []]
79-
erlc_options = Enum.map erlc_options, fn(opt) ->
80-
case opt do
81-
{ :i, dir } -> { :i, to_erl_file(dir) }
82-
_ -> opt
83-
end
67+
erlc_options = project[:erlc_options] || []
68+
erlc_options = erlc_options ++ [{:outdir, compile_path}, {:i, include_path}, :report]
69+
erlc_options = Enum.map erlc_options, fn
70+
{ kind, dir } when kind in [:i, :outdit] ->
71+
{ kind, to_erl_file(dir) }
72+
opt ->
73+
opt
8474
end
8575

86-
files = files |> scan_sources(include_path, source_paths) |> sort_dependency
87-
unless opts[:force], do: files = Enum.filter(files, check_file(compile_path, &1))
76+
files = files |> scan_sources(include_path, source_paths) |> sort_dependencies
77+
78+
unless opts[:force] do
79+
files = Enum.filter(files, requires_compilation?(compile_path, &1))
80+
end
8881

8982
if files == [] do
9083
:noop
@@ -99,67 +92,69 @@ defmodule Mix.Tasks.Compile.Erlang do
9992
end
10093

10194
defp scan_sources(files, include_path, source_paths) do
102-
include_pathes = [include_path | source_paths]
103-
List.foldl(files, [], fn(file, acc) -> scan_source(acc, file, include_pathes) end) |> Enum.reverse
95+
include_paths = [include_path | source_paths]
96+
Enum.reduce(files, [], scan_source(&2, &1, include_paths)) |> Enum.reverse
10497
end
10598

106-
defp scan_source(acc, file, include_pathes) do
107-
erl_file = Erl[mtime: Utils.last_modified(file),
108-
file: file,
109-
module: Path.basename(file, ".erl")]
110-
case Epp.parse_file(to_erl_file(file), include_pathes, []) do
111-
{:ok, forms} ->
112-
[List.foldl(tl(forms), erl_file, fn(f, acc) -> do_form(file, f, acc) end) | acc]
113-
{:error, _error} ->
99+
defp scan_source(acc, file, include_paths) do
100+
erl_file = Erl[file: file, module: Path.basename(file, ".erl")]
101+
102+
case Epp.parse_file(to_erl_file(file), include_paths, []) do
103+
{ :ok, forms } ->
104+
[List.foldl(tl(forms), erl_file, do_form(file, &1, &2)) | acc]
105+
{ :error, _error } ->
114106
acc
115107
end
116108
end
117109

118-
defp do_form(file, form, erl) do
110+
defp do_form(file, form, Erl[] = erl) do
119111
case form do
120112
{:attribute, _, :file, {include_file, _}} when file != include_file ->
121-
erl.update(includes: [include_file | erl.includes])
113+
if File.regular?(include_file) do
114+
erl.update_includes [include_file|&1]
115+
else
116+
erl
117+
end
122118
{:attribute, _, :behaviour, behaviour} ->
123-
erl.update(behaviour: [behaviour | erl.behaviours])
119+
erl.update_behaviours [behaviour|&1]
124120
{:attribute, _, :compile, value} ->
125-
erl.update(compile: [value | erl.compile])
121+
erl.update_compile [value|&1]
126122
_ ->
127123
erl
128124
end
129125
end
130126

131-
defp sort_dependency(erls) do
127+
defp sort_dependencies(erls) do
132128
graph = Graph.new
129+
133130
lc erl inlist erls do
134131
Graph.add_vertex(graph, erl.module, erl)
135132
end
133+
136134
lc erl inlist erls do
137135
lc b inlist erl.behaviours, do: Graph.add_edge(graph, b, erl.module)
138-
lc a inlist erl.compile do
139-
case a do
140-
{:parse_transform, transform} -> Graph.add_edge(graph, transform, erl.module);
136+
lc c inlist erl.compile do
137+
case c do
138+
{:parse_transform, transform} -> Graph.add_edge(graph, transform, erl.module)
141139
_ -> :ok
142140
end
143141
end
144142
end
143+
145144
result =
146145
case GraphUtils.topsort(graph) do
147-
:false -> erls;
148-
mods ->
146+
false -> erls
147+
mods ->
149148
lc m inlist mods, do: elem(Graph.vertex(graph, m), 1)
150149
end
150+
151151
Graph.delete(graph)
152152
result
153153
end
154154

155-
defp check_file(compile_path, erl) do
156-
beam = Path.join(compile_path, "#{erl.module}#{Code.objfile_extension}")
157-
case File.regular?(beam) do
158-
:false -> :true
159-
:true ->
160-
beammtime = Utils.last_modified(beam)
161-
(beammtime <= erl.mtime) or Utils.check_mtime(beammtime, erl.includes)
162-
end
155+
defp requires_compilation?(compile_path, erl) do
156+
beam = Path.join(compile_path, "#{erl.module}#{:code.objfile_extension}")
157+
Utils.stale?([erl.file|erl.includes], [beam])
163158
end
164159

165160
defp compile_files(files, compile_path, erlc_options) do
@@ -172,17 +167,45 @@ defmodule Mix.Tasks.Compile.Erlang do
172167
interpret_result file, :compile.file(file, erlc_options), ".erl"
173168
end
174169

170+
## Helpers shared accross erlang compilers
171+
172+
@doc """
173+
Extract stale pairs considering the set of directories
174+
and filename extensions. It first looks up the `dir1`
175+
for files with `ext1` extensions and then recursively
176+
try to find matching pairs in `dir2` with `ext2`
177+
extension.
178+
"""
179+
def extract_stale_pairs(dir1, ext1, dir2, ext2, force) do
180+
files = Mix.Utils.extract_files([dir1], List.wrap(ext1))
181+
Enum.reduce files, [], fn(file, acc) ->
182+
compiled_file = Path.rootname(file) |> Path.basename
183+
compiled_file = Path.join(dir2, compiled_file <> "." <> to_binary(ext2))
184+
if force or Mix.Utils.stale?([file], [compiled_file]) do
185+
[{file, compiled_file} | acc]
186+
else
187+
acc
188+
end
189+
end
190+
end
191+
192+
@doc """
193+
Interprets compilation results and prints them to the console.
194+
"""
175195
def interpret_result(file, result, ext // "") do
176196
case result do
177-
{ :ok, _} ->
178-
Mix.shell.info "Compiled #{file}#{ext}"
179-
:error ->
197+
{ :ok, _ } ->
198+
Mix.shell.info "Compiled #{file}#{ext}"
199+
:error ->
180200
:ok
181201
end
182202
end
183203

184-
def to_erl_file file do
204+
@doc """
205+
Converts the given file to a format accepted by
206+
Erlang compilation tools.
207+
"""
208+
def to_erl_file(file) do
185209
to_char_list(file)
186210
end
187-
188211
end

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

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
defmodule Mix.Tasks.Compile.Leex do
2-
3-
alias :compile, as: Compiler
42
alias :leex, as: Leex
5-
alias Mix.Utils
63
alias Mix.Tasks.Compile.Erlang
4+
75
use Mix.Task
86

97
@hidden true
@@ -30,35 +28,20 @@ defmodule Mix.Tasks.Compile.Leex do
3028
[erlc_paths: ["src", "other"]]
3129
3230
* `:leex_options` - compilation options that applies
33-
to Leex's compiler.
34-
This options are setted:
35-
36-
:scannerfile
37-
{:report, true}
38-
39-
There are many other available options here:
40-
http://www.erlang.org/doc/man/leex.html#file-2
31+
to Leex's compiler. There are many available options
32+
here: http://www.erlang.org/doc/man/leex.html#file-2
4133
4234
"""
43-
4435
def run(args) do
4536
{ opts, _ } = OptionParser.parse(args, switches: [force: :boolean])
4637

47-
project = Mix.project
38+
project = Mix.project
4839
source_paths = project[:erlc_paths]
4940

50-
checkfun = if opts[:force] do
51-
fn(_, _) -> true end
52-
else
53-
fn(f1, f2) ->
54-
mtime = Utils.last_modified(f2)
55-
Utils.check_mtime(mtime, [f1])
56-
end
57-
end
58-
5941
files = lc source_path inlist source_paths do
60-
Utils.check_files(source_path, :xrl, source_path, :erl, checkfun)
42+
Erlang.extract_stale_pairs(source_path, :xrl, source_path, :erl, opts[:force])
6143
end |> List.flatten
44+
6245
if files == [] do
6346
:noop
6447
else
@@ -67,13 +50,12 @@ defmodule Mix.Tasks.Compile.Leex do
6750
end
6851
end
6952

70-
def compile_files(files, options) do
53+
defp compile_files(files, options) do
7154
lc {input, output} inlist files do
72-
Erlang.interpret_result(input, Leex.file(Erlang.to_erl_file(input),
73-
[{:scannerfile, Erlang.to_erl_file(output)},
74-
{:report, true} | options]))
55+
options = options ++ [scannerfile: Erlang.to_erl_file(output), report: true]
56+
Erlang.interpret_result(input,
57+
Leex.file(Erlang.to_erl_file(input), options))
7558
end
7659
end
77-
7860
end
7961

0 commit comments

Comments
 (0)