1
1
defmodule Mix.Tasks.Compile.Erlang do
2
-
3
2
alias :epp , as: Epp
4
3
alias :digraph , as: Graph
5
4
alias :digraph_utils , as: GraphUtils
6
- alias :code , as: Code
7
- alias :compile , as: Compiler
8
5
alias Mix.Utils
6
+
9
7
use Mix.Task
10
8
11
9
@ hidden true
@@ -29,7 +27,7 @@ defmodule Mix.Tasks.Compile.Erlang do
29
27
30
28
## Command line options
31
29
32
- * `--force` - forces compilation regardless of module times;
30
+ * `--force` - forces compilation regardless of module times
33
31
34
32
## Configuration
35
33
@@ -47,19 +45,11 @@ defmodule Mix.Tasks.Compile.Erlang do
47
45
48
46
[`erlc_include_path`: "other"]
49
47
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.
59
50
60
- There are many other available options here:
51
+ There are many available options here:
61
52
http://www.erlang.org/doc/man/compile.html#file-2
62
-
63
53
"""
64
54
65
55
defrecord Erl , file: nil , module: nil , behaviours: [ ] , compile: [ ] ,
@@ -68,23 +58,26 @@ defmodule Mix.Tasks.Compile.Erlang do
68
58
def run ( args ) do
69
59
{ opts , _ } = OptionParser . parse ( args , switches: [ force: :boolean ] )
70
60
71
- project = Mix . project
61
+ project = Mix . project
72
62
source_paths = project [ :erlc_paths ]
73
63
files = Mix.Utils . extract_files ( source_paths , [ :erl ] )
74
64
compile_path = to_erl_file project [ :compile_path ]
75
65
include_path = to_erl_file project [ :erlc_include_path ]
76
66
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
84
74
end
85
75
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
88
81
89
82
if files == [ ] do
90
83
:noop
@@ -99,67 +92,69 @@ defmodule Mix.Tasks.Compile.Erlang do
99
92
end
100
93
101
94
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
104
97
end
105
98
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 } ->
114
106
acc
115
107
end
116
108
end
117
109
118
- defp do_form ( file , form , erl ) do
110
+ defp do_form ( file , form , Erl [ ] = erl ) do
119
111
case form do
120
112
{ :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
122
118
{ :attribute , _ , :behaviour , behaviour } ->
123
- erl . update ( behaviour: [ behaviour | erl . behaviours ] )
119
+ erl . update_behaviours [ behaviour | & 1 ]
124
120
{ :attribute , _ , :compile , value } ->
125
- erl . update ( compile: [ value | erl . compile ] )
121
+ erl . update_compile [ value | & 1 ]
126
122
_ ->
127
123
erl
128
124
end
129
125
end
130
126
131
- defp sort_dependency ( erls ) do
127
+ defp sort_dependencies ( erls ) do
132
128
graph = Graph . new
129
+
133
130
lc erl inlist erls do
134
131
Graph . add_vertex ( graph , erl . module , erl )
135
132
end
133
+
136
134
lc erl inlist erls do
137
135
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 )
141
139
_ -> :ok
142
140
end
143
141
end
144
142
end
143
+
145
144
result =
146
145
case GraphUtils . topsort ( graph ) do
147
- : false -> erls ;
148
- mods ->
146
+ false -> erls
147
+ mods ->
149
148
lc m inlist mods , do: elem ( Graph . vertex ( graph , m ) , 1 )
150
149
end
150
+
151
151
Graph . delete ( graph )
152
152
result
153
153
end
154
154
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 ] )
163
158
end
164
159
165
160
defp compile_files ( files , compile_path , erlc_options ) do
@@ -172,17 +167,45 @@ defmodule Mix.Tasks.Compile.Erlang do
172
167
interpret_result file , :compile . file ( file , erlc_options ) , ".erl"
173
168
end
174
169
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
+ """
175
195
def interpret_result ( file , result , ext // "" ) do
176
196
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 ->
180
200
:ok
181
201
end
182
202
end
183
203
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
185
209
to_char_list ( file )
186
210
end
187
-
188
211
end
0 commit comments