@@ -83,13 +83,12 @@ defmodule Mix.Tasks.Compile.Erlang do
8383 end )
8484
8585 compile_path = Path . relative_to ( compile_path , File . cwd! ( ) )
86+ erls = scan_sources ( files , include_path , source_paths , compile_path , opts )
8687
87- { erls , tuples } =
88- Enum . unzip ( scan_sources ( files , include_path , source_paths , compile_path , opts ) )
88+ { sorted , parallel } = topsort_and_parallelize ( erls , compile_path )
89+ opts = [ parallel: MapSet . new ( parallel ) ] ++ opts
8990
90- opts = [ parallel: MapSet . new ( find_parallel ( erls ) ) ] ++ opts
91-
92- Erlang . compile_entries ( manifest ( ) , tuples , :erl , :beam , opts , fn input , _output ->
91+ Erlang . compile_entries ( manifest ( ) , sorted , :erl , :beam , opts , fn input , _output ->
9392 # We're purging the module because a previous compiler (for example, Phoenix)
9493 # might have already loaded the previous version of it.
9594 module = input |> Path . basename ( ".erl" ) |> String . to_atom ( )
@@ -147,7 +146,7 @@ defmodule Mix.Tasks.Compile.Erlang do
147146 ordered: false
148147 )
149148 |> Enum . flat_map ( fn
150- { :ok , { :ok , erl_file , target_tuple } } -> [ { erl_file , target_tuple } ]
149+ { :ok , { :ok , erl_file } } -> [ erl_file ]
151150 { :ok , :error } -> [ ]
152151 end )
153152 end
@@ -156,17 +155,15 @@ defmodule Mix.Tasks.Compile.Erlang do
156155 erl_file = % {
157156 file: file ,
158157 module: module_from_artifact ( file ) ,
159- behaviours: [ ] ,
160- compile: [ ] ,
158+ deps: [ ] ,
161159 includes: [ ] ,
162- invalid: false
160+ status: :ok
163161 }
164162
165163 case :epp . parse_file ( Erlang . to_erl_file ( file ) , include_paths , [ ] ) do
166164 { :ok , forms } ->
167165 erl_file = List . foldl ( tl ( forms ) , erl_file , & do_form ( file , & 1 , & 2 ) )
168- target_tuple = annotate_target ( erl_file , compile_path , opts [ :force ] )
169- { :ok , erl_file , target_tuple }
166+ { :ok , maybe_stale ( erl_file , compile_path , opts [ :force ] ) }
170167
171168 { :error , _error } ->
172169 :error
@@ -183,49 +180,71 @@ defmodule Mix.Tasks.Compile.Erlang do
183180 end
184181
185182 { :attribute , _ , :behaviour , behaviour } ->
186- % { erl | behaviours : [ behaviour | erl . behaviours ] }
183+ % { erl | deps : [ behaviour | erl . deps ] }
187184
188185 { :attribute , _ , :behavior , behaviour } ->
189- % { erl | behaviours : [ behaviour | erl . behaviours ] }
186+ % { erl | deps : [ behaviour | erl . deps ] }
190187
191188 { :attribute , _ , :compile , value } when is_list ( value ) ->
192- % { erl | compile: value ++ erl . compile }
189+ % { erl | deps: Enum . reduce ( value , erl . deps , & add_parse_transforms / 2 ) }
193190
194191 { :attribute , _ , :compile , value } ->
195- % { erl | compile: [ value | erl . compile ] }
192+ % { erl | deps: add_parse_transforms ( value , erl . deps ) }
196193
197194 _ ->
198195 erl
199196 end
200197 end
201198
202- defp find_parallel ( erls ) do
203- serial = MapSet . new ( find_dependencies ( erls ) )
199+ defp topsort_and_parallelize ( erls , compile_path ) do
200+ graph = :digraph . new ( )
204201
205- erls
206- |> Enum . reject ( & ( & 1 . module in serial ) )
207- |> Enum . map ( & & 1 . file )
208- end
202+ for % { module: module , status: status , file: file } <- erls do
203+ :digraph . add_vertex ( graph , module , { file , status } )
204+ end
205+
206+ for % { module: module , deps: deps } <- erls , dep <- deps do
207+ # It may error if the behaviour/parse transform is not in the project,
208+ # which is expected
209+ :digraph . add_edge ( graph , module , dep )
210+ end
209211
210- defp find_dependencies ( erls ) do
211- Enum . flat_map ( erls , fn erl ->
212- transforms =
213- Enum . flat_map ( erl . compile , fn
214- { :parse_transform , transform } -> [ transform ]
215- _ -> [ ]
212+ if vertices = :digraph_utils . topsort ( graph ) do
213+ sorted =
214+ Enum . reduce ( vertices , [ ] , fn module , acc ->
215+ { _ , { file , status } } = :digraph . vertex ( graph , module )
216+ [ { status , file , Path . join ( compile_path , "#{ module } .beam" ) } | acc ]
216217 end )
217218
218- transforms ++ erl . behaviours
219+ parallel =
220+ for % { file: file , module: module } <- erls ,
221+ :digraph . in_neighbours ( graph , module ) == [ ] ,
222+ do: file
223+
224+ { sorted , parallel }
225+ else
226+ Mix . raise (
227+ "Could not compile Erlang. " <>
228+ "The following modules form a cycle: " <>
229+ Enum . join ( Mix.Utils . find_cycle! ( graph ) , ", " )
230+ )
231+ end
232+ end
233+
234+ defp add_parse_transforms ( compile , deps ) do
235+ Enum . reduce ( compile , deps , fn
236+ { :parse_transform , transform } , deps -> [ transform | deps ]
237+ _ , deps -> deps
219238 end )
220239 end
221240
222- defp annotate_target ( erl , compile_path , force ) do
241+ defp maybe_stale ( erl , compile_path , force ) do
223242 beam = Path . join ( compile_path , "#{ erl . module } .beam" )
224243
225244 if force || Mix.Utils . stale? ( [ erl . file | erl . includes ] , [ beam ] ) do
226- { :stale , erl . file , beam }
245+ % { erl | status: :stale }
227246 else
228- { :ok , erl . file , beam }
247+ erl
229248 end
230249 end
231250
0 commit comments