7
7
8
8
-include (" rebar.hrl" ).
9
9
10
- -define (DAG_VSN , 3 ).
10
+ -define (DAG_VSN , 4 ).
11
11
-define (DAG_ROOT , " source" ).
12
12
-define (DAG_EXT , " .dag" ).
13
13
14
- -type dag_v () :: {digraph :vertex (), term ()} | 'false' .
15
- -type dag_e () :: {digraph :vertex (), digraph :vertex ()}.
16
- -type critical_meta () :: term (). % if this changes, the DAG is invalid
17
- -type dag_rec () :: {list (dag_v ()), list (dag_e ()), critical_meta ()}.
18
- -type dag () :: digraph :graph ().
14
+ -type critical_meta () :: term ().
15
+
19
16
-record (dag , {vsn = ? DAG_VSN :: pos_integer (),
20
- info = {[], [], []} :: dag_rec ()}).
17
+ meta :: critical_meta (),
18
+ vtab :: notable | [tuple ()],
19
+ etab :: notable | [tuple ()],
20
+ ntab :: notable | [tuple ()]}).
21
+
22
+ -type dag () :: digraph :graph ().
21
23
22
24
% % @doc You should initialize one DAG per compiler module.
23
25
% % `CritMeta' is any contextual information that, if it is found to change,
@@ -105,13 +107,46 @@ filter_prefix(G, [{App, Out} | AppTail] = AppPaths, [File | FTail]) ->
105
107
filter_prefix (G , AppPaths , FTail )
106
108
end .
107
109
110
+ finalise_populate_sources (_G , _InDirs , Waiting ) when Waiting =:= #{} ->
111
+ ok ;
112
+ finalise_populate_sources (G , InDirs , Waiting ) ->
113
+ % % wait for all deps to complete
114
+ receive
115
+ {deps , Pid , AbsIncls } ->
116
+ {Status , Source } = maps :get (Pid , Waiting ),
117
+ % % the file hasn't been visited yet; set it to existing, but with
118
+ % % a last modified value that's null so it gets updated to something new.
119
+ [digraph :add_vertex (G , Src , 0 ) || Src <- AbsIncls ,
120
+ digraph :vertex (G , Src ) =:= false ],
121
+ % % drop edges from deps that aren't included!
122
+ [digraph :del_edge (G , Edge ) || Status == old ,
123
+ Edge <- digraph :out_edges (G , Source ),
124
+ {_ , _Src , Path , _Label } <- [digraph :edge (G , Edge )],
125
+ not lists :member (Path , AbsIncls )],
126
+ % % Add the rest
127
+ [digraph :add_edge (G , Source , Incl ) || Incl <- AbsIncls ],
128
+ % % mark the digraph dirty when there is any change in
129
+ % % dependencies, for any application in the project
130
+ mark_dirty (G ),
131
+ finalise_populate_sources (G , InDirs , Waiting );
132
+ {'DOWN' , _MRef , process , Pid , normal } ->
133
+ finalise_populate_sources (G , InDirs , maps :remove (Pid , Waiting ));
134
+ {'DOWN' , _MRef , process , Pid , Reason } ->
135
+ {_Status , Source } = maps :get (Pid , Waiting ),
136
+ ? ERROR (" Failed to get dependencies for ~s~n~p " , [Source , Reason ]),
137
+ ? FAIL
138
+ end .
139
+
108
140
% % @doc this function scans all the source files found and looks into
109
141
% % all the `InDirs' for deps (other source files, or files that aren't source
110
142
% % but still returned by the compiler module) that are related
111
143
% % to them.
112
- populate_sources (_G , _Compiler , _InDirs , [], _DepOpts ) ->
113
- ok ;
114
- populate_sources (G , Compiler , InDirs , [Source |Erls ], DepOpts ) ->
144
+ populate_sources (G , Compiler , InDirs , Sources , DepOpts ) ->
145
+ populate_sources (G , Compiler , InDirs , Sources , DepOpts , #{}).
146
+
147
+ populate_sources (G , _Compiler , InDirs , [], _DepOpts , Waiting ) ->
148
+ finalise_populate_sources (G , InDirs , Waiting );
149
+ populate_sources (G , Compiler , InDirs , [Source |Erls ], DepOpts , Waiting ) ->
115
150
case digraph :vertex (G , Source ) of
116
151
{_ , LastUpdated } ->
117
152
case filelib :last_modified (Source ) of
@@ -120,21 +155,20 @@ populate_sources(G, Compiler, InDirs, [Source|Erls], DepOpts) ->
120
155
% % from the graph.
121
156
digraph :del_vertex (G , Source ),
122
157
mark_dirty (G ),
123
- populate_sources (G , Compiler , InDirs , Erls , DepOpts );
158
+ populate_sources (G , Compiler , InDirs , Erls , DepOpts , Waiting );
124
159
LastModified when LastUpdated < LastModified ->
125
160
digraph :add_vertex (G , Source , LastModified ),
126
- prepopulate_deps (G , Compiler , InDirs , Source , DepOpts , old ),
127
- mark_dirty ( G );
161
+ Worker = prepopulate_deps (Compiler , InDirs , Source , DepOpts , self () ),
162
+ populate_sources ( G , Compiler , InDirs , Erls , DepOpts , Waiting #{ Worker => { old , Source }} );
128
163
_ -> % unchanged
129
- ok
164
+ populate_sources ( G , Compiler , InDirs , Erls , DepOpts , Waiting )
130
165
end ;
131
166
false ->
132
167
LastModified = filelib :last_modified (Source ),
133
168
digraph :add_vertex (G , Source , LastModified ),
134
- prepopulate_deps (G , Compiler , InDirs , Source , DepOpts , new ),
135
- mark_dirty (G )
136
- end ,
137
- populate_sources (G , Compiler , InDirs , Erls , DepOpts ).
169
+ Worker = prepopulate_deps (Compiler , InDirs , Source , DepOpts , self ()),
170
+ populate_sources (G , Compiler , InDirs , Erls , DepOpts , Waiting #{Worker => {new , Source }})
171
+ end .
138
172
139
173
% % @doc Scan all files in the digraph that are seen as dependencies, but are
140
174
% % neither source files nor artifacts (i.e. header files that don't produce
@@ -228,19 +262,23 @@ restore_dag(G, File, CritMeta) ->
228
262
{ok , Data } ->
229
263
% % The CritMeta value is checked and if it doesn't match, we fail
230
264
% % the whole restore operation.
231
- # dag {vsn = ? DAG_VSN , info = {Vs , Es , CritMeta }} = binary_to_term (Data ),
232
- [digraph :add_vertex (G , V , LastUpdated ) || {V , LastUpdated } <- Vs ],
233
- [digraph :add_edge (G , V1 , V2 , Label ) || {_ , V1 , V2 , Label } <- Es ],
265
+ # dag {vsn = ? DAG_VSN , meta = CritMeta , vtab = VTab ,
266
+ etab = ETab , ntab = NTab } = binary_to_term (Data ),
267
+ {digraph , VT , ET , NT , false } = G ,
268
+ true = ets :insert_new (VT , VTab ),
269
+ true = ets :insert_new (ET , ETab ),
270
+ true = ets :delete_all_objects (NT ),
271
+ true = ets :insert (NT , NTab ),
234
272
ok ;
235
273
{error , _Err } ->
236
274
ok
237
275
end .
238
276
239
277
store_dag (G , File , CritMeta ) ->
240
278
ok = filelib :ensure_dir (File ),
241
- Vs = lists : map ( fun ( V ) -> digraph : vertex ( G , V ) end , digraph : vertices ( G )) ,
242
- Es = lists : map ( fun ( E ) -> digraph : edge ( G , E ) end , digraph : edges ( G ) ),
243
- Data = term_to_binary ( # dag { info = { Vs , Es , CritMeta } }, [{compressed , 2 }]),
279
+ { digraph , VT , ET , NT , false } = G ,
280
+ Data = term_to_binary ( # dag { meta = CritMeta , vtab = ets : tab2list ( VT ),
281
+ etab = ets : tab2list ( ET ), ntab = ets : select ( NT , [{ '_' ,[],[ '$_' ]}]) }, [{compressed , 2 }]),
244
282
file :write_file (File , Data ).
245
283
246
284
% % Drop a file from the digraph if it doesn't exist, and if so,
@@ -285,26 +323,20 @@ maybe_rm_vertex(G, Source) ->
285
323
% % mark its timestamp to 0, which means we have no info on it.
286
324
% % Source files will be covered at a later point in their own scan, and
287
325
% % non-source files are going to be covered by `populate_deps/3'.
288
- prepopulate_deps (G , Compiler , InDirs , Source , DepOpts , Status ) ->
289
- SourceDir = filename :dirname (Source ),
290
- AbsIncls = case erlang :function_exported (Compiler , dependencies , 4 ) of
291
- false ->
292
- Compiler :dependencies (Source , SourceDir , InDirs );
293
- true ->
294
- Compiler :dependencies (Source , SourceDir , InDirs , DepOpts )
295
- end ,
296
- % % the file hasn't been visited yet; set it to existing, but with
297
- % % a last modified value that's null so it gets updated to something new.
298
- [digraph :add_vertex (G , Src , 0 ) || Src <- AbsIncls ,
299
- digraph :vertex (G , Src ) =:= false ],
300
- % % drop edges from deps that aren't included!
301
- [digraph :del_edge (G , Edge ) || Status == old ,
302
- Edge <- digraph :out_edges (G , Source ),
303
- {_ , _Src , Path , _Label } <- [digraph :edge (G , Edge )],
304
- not lists :member (Path , AbsIncls )],
305
- % % Add the rest
306
- [digraph :add_edge (G , Source , Incl ) || Incl <- AbsIncls ],
307
- ok .
326
+ prepopulate_deps (Compiler , InDirs , Source , DepOpts , Control ) ->
327
+ {Worker , _MRef } = spawn_monitor (
328
+ fun () ->
329
+ SourceDir = filename :dirname (Source ),
330
+ AbsIncls = case erlang :function_exported (Compiler , dependencies , 4 ) of
331
+ false ->
332
+ Compiler :dependencies (Source , SourceDir , InDirs );
333
+ true ->
334
+ Compiler :dependencies (Source , SourceDir , InDirs , DepOpts )
335
+ end ,
336
+ Control ! {deps , self (), AbsIncls }
337
+ end
338
+ ),
339
+ Worker .
308
340
309
341
% % check that a dep file is up to date
310
342
refresh_dep (_G , {artifact , _ }) ->
0 commit comments