11defmodule 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-
188211end
0 commit comments