@ -30,18 +30,35 @@
ok | { ok , [ string ( ) ] } | { ok , [ string ( ) ] , [ string ( ) ] } .
- callback clean ( [ file : filename ( ) ] , rebar_app_info : t ( ) ) - > _ .
- define ( DAG_VSN , 2 ) .
- define ( DAG_ROOT , " source " ) .
- define ( DAG_EXT , " .dag " ) .
- type dag_v ( ) : : { digraph : vertex ( ) , term ( ) } | 'false' .
- type dag_e ( ) : : { digraph : vertex ( ) , digraph : vertex ( ) } .
- type dag ( ) : : { list ( dag_v ( ) ) , list ( dag_e ( ) ) , list ( string ( ) ) } .
- record ( dag , { vsn = ? DAG_VSN : : pos_integer ( ) ,
info = { [ ] , [ ] , [ ] } : : dag ( ) } ) .
- define ( RE_PREFIX , " ^(?! \\ ._) " ) .
compile_all ( Compilers , AppInfo ) - >
- spec compile_all ( [ { module ( ) , digraph : graph ( ) } , . . . ] , rebar_app_info : t ( ) ) - > ok
; ( [ module ( ) , . . . ] , rebar_app_info : t ( ) ) - > ok .
compile_all ( DAGs , AppInfo ) when is_tuple ( hd ( DAGs ) ) - > % > 3 . 13 . 0
prepare_compiler_env ( AppInfo ) ,
lists : foreach ( fun ( { Compiler , G } ) - >
run ( G , Compiler , AppInfo ) ,
% % TODO : disable default recursivity in extra_src_dirs compiling to
% % prevent compiling sample modules in _ SUITE_data / directories
% % in CT .
ExtraApps = annotate_extras ( AppInfo ) ,
[ run ( G , Compiler , ExtraAppInfo ) | | ExtraAppInfo < - ExtraApps ] ,
ok
end ,
DAGs ) ;
compile_all ( Compilers , AppInfo ) - > % =< 3 . 13 . 0 interface ; plugins use this !
% % Support the old - style API by re - declaring a local DAG for the
% % compile steps needed .
lists : foreach ( fun ( Compiler ) - >
OutDir = rebar_app_info : out_dir ( AppInfo ) ,
G = rebar_compiler_dag : init ( OutDir , Compiler , undefined , [ ] ) ,
compile_all ( [ { Compiler , G } ] , AppInfo ) ,
rebar_compiler_dag : maybe_store ( G , OutDir , Compiler , undefined , [ ] ) ,
rebar_compiler_dag : terminate ( G )
end , Compilers ) .
prepare_compiler_env ( AppInfo ) - >
EbinDir = rebar_utils : to_list ( rebar_app_info : ebin_dir ( AppInfo ) ) ,
% % Make sure that outdir is on the path
ok = rebar_file_utils : ensure_dir ( EbinDir ) ,
@ -51,15 +68,9 @@ compile_all(Compilers, AppInfo) ->
% % called here for clarity as it ' s required by both opts_changed / 2
% % and erl_compiler_opts_set / 0 in needed_files
_ = code : ensure_loaded ( compile ) ,
lists : foreach ( fun ( CompilerMod ) - >
run ( CompilerMod , AppInfo , undefined ) ,
run_on_extra_src_dirs ( CompilerMod , AppInfo ,
fun ( Mod , App ) - > run ( Mod , App , " extra " ) end )
end , Compilers ) ,
ok .
run ( CompilerMod , AppInfo , Label ) - >
run ( G , CompilerMod , AppInfo ) - >
#{ src_dirs : = SrcDirs ,
include_dirs : = InclDirs ,
src_ext : = SrcExt ,
@ -72,12 +83,14 @@ run(CompilerMod, AppInfo, Label) ->
AbsInclDirs = [ filename : join ( BaseDir , InclDir ) | | InclDir < - InclDirs ] ,
FoundFiles = find_source_files ( BaseDir , SrcExt , SrcDirs , BaseOpts ) ,
OutDir = rebar_app_info : out_dir ( AppInfo ) ,
AbsSrcDirs = [ filename : join ( BaseDir , SrcDir ) | | SrcDir < - SrcDirs ] ,
G = init_dag ( CompilerMod , AbsInclDirs , AbsSrcDirs , FoundFiles , OutDir , EbinDir , Label ) ,
{ { FirstFiles , FirstFileOpts } , { RestFiles , Opts } } = CompilerMod : needed_files ( G , FoundFiles ,
Mappings , AppInfo ) ,
true = digraph : delete ( G ) ,
InDirs = lists : usort ( AbsInclDirs ++ AbsSrcDirs ) ,
rebar_compiler_dag : prune ( G , AbsSrcDirs , EbinDir , FoundFiles ) ,
rebar_compiler_dag : update ( G , CompilerMod , InDirs , FoundFiles ) ,
{ { FirstFiles , FirstFileOpts } ,
{ RestFiles , Opts } } = CompilerMod : needed_files ( G , FoundFiles , Mappings , AppInfo ) ,
compile_each ( FirstFiles , FirstFileOpts , BaseOpts , Mappings , CompilerMod ) ,
case RestFiles of
@ -167,20 +180,19 @@ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod) ->
clean ( Compilers , AppInfo ) - >
lists : foreach ( fun ( CompilerMod ) - >
clean_ ( CompilerMod , AppInfo , undefined ) ,
run_on_extra_src_dirs ( CompilerMod , AppInfo ,
fun ( Mod , App ) - > clean_ ( Mod , App , " extra " ) end )
Extras = annotate_extras ( AppInfo ) ,
[ clean_ ( CompilerMod , ExtraApp , " extra " ) | | ExtraApp < - Extras ]
end , Compilers ) .
clean_ ( CompilerMod , AppInfo , Label ) - >
clean_ ( CompilerMod , AppInfo , _ Label ) - >
#{ src_dirs : = SrcDirs ,
src_ext : = SrcExt } = CompilerMod : context ( AppInfo ) ,
BaseDir = rebar_app_info : dir ( AppInfo ) ,
Opts = rebar_app_info : opts ( AppInfo ) ,
EbinDir = rebar_app_info : ebin_dir ( AppInfo ) ,
FoundFiles = find_source_files ( BaseDir , SrcExt , SrcDirs , Opts ) ,
CompilerMod : clean ( FoundFiles , AppInfo ) ,
rebar_file_utils : rm_rf ( dag_file ( CompilerMod , EbinDir , Label ) ) .
ok .
- spec needs_compile ( filename : all ( ) , extension ( ) , [ { extension ( ) , file : dirname ( ) } ] ) - > boolean ( ) .
needs_compile ( Source , OutExt , Mappings ) - >
@ -190,30 +202,23 @@ needs_compile(Source, OutExt, Mappings) ->
Target = filename : join ( OutDir , BaseName ++ OutExt ) ,
filelib : last_modified ( Source ) > filelib : last_modified ( Target ) .
run_on_extra_src_dirs ( CompilerMod , AppInfo , Fun ) - >
annotate_extras ( AppInfo ) - >
ExtraDirs = rebar_dir : extra_src_dirs ( rebar_app_info : opts ( AppInfo ) , [ ] ) ,
run_on_extra_src_dirs ( ExtraDirs , CompilerMod , AppInfo , Fun ) .
run_on_extra_src_dirs ( [ ] , _ CompilerMod , _ AppInfo , _ Fun ) - >
ok ;
run_on_extra_src_dirs ( [ Dir | Rest ] , CompilerMod , AppInfo , Fun ) - >
case filelib : is_dir ( filename : join ( rebar_app_info : dir ( AppInfo ) , Dir ) ) of
true - >
OldSrcDirs = rebar_app_info : get ( AppInfo , src_dirs , [ " src " ] ) ,
AppDir = rebar_app_info : dir ( AppInfo ) ,
EbinDir = filename : join ( rebar_app_info : out_dir ( AppInfo ) , Dir ) ,
AppInfo1 = rebar_app_info : ebin_dir ( AppInfo , EbinDir ) ,
AppInfo2 = rebar_app_info : set ( AppInfo1 , src_dirs , [ Dir ] ) ,
AppInfo3 = rebar_app_info : set ( AppInfo2 , extra_src_dirs , OldSrcDirs ) ,
AppInfo4 = add_to_includes ( % give access to . hrl in app ' s src /
AppInfo3 ,
[ filename : join ( [ AppDir , D ] ) | | D < - OldSrcDirs ]
) ,
Fun ( CompilerMod , AppInfo4 ) ;
_ - >
ok
OldSrcDirs = rebar_app_info : get ( AppInfo , src_dirs , [ " src " ] ) ,
AppDir = rebar_app_info : dir ( AppInfo ) ,
lists : map ( fun ( Dir ) - >
EbinDir = filename : join ( rebar_app_info : out_dir ( AppInfo ) , Dir ) ,
AppInfo1 = rebar_app_info : ebin_dir ( AppInfo , EbinDir ) ,
AppInfo2 = rebar_app_info : set ( AppInfo1 , src_dirs , [ Dir ] ) ,
AppInfo3 = rebar_app_info : set ( AppInfo2 , extra_src_dirs , OldSrcDirs ) ,
add_to_includes ( % give access to . hrl in app ' s src /
AppInfo3 ,
[ filename : join ( [ AppDir , D ] ) | | D < - OldSrcDirs ]
)
end ,
run_on_extra_src_dirs ( Rest , CompilerMod , AppInfo , Fun ) .
[ ExtraDir | | ExtraDir < - ExtraDirs ,
filelib : is_dir ( filename : join ( AppDir , ExtraDir ) ) ]
) .
% % These functions are here for the ultimate goal of getting rid of
% % rebar_base_compiler . This can ' t be done because of existing plugins .
@ -233,7 +238,7 @@ format_error_source(Path, Opts) ->
report ( Messages ) - >
rebar_base_compiler : report ( Messages ) .
% % private functions
% % % private functions
find_source_files ( BaseDir , SrcExt , SrcDirs , Opts ) - >
SourceExtRe = " ^(?! \\ ._).* \\ " ++ SrcExt ++ [ $$ ] ,
@ -242,160 +247,6 @@ find_source_files(BaseDir, SrcExt, SrcDirs, Opts) ->
rebar_utils : find_files_in_dirs ( [ filename : join ( BaseDir , SrcDir ) ] , SourceExtRe , Recursive )
end , SrcDirs ) .
% % @ private generate the name for the DAG based on the compiler module and
% % a custom label , both of which are used to prevent various compiler runs
% % from clobbering each other . The label ` undefined ' is kept for a default
% % run of the compiler , to keep in line with previous versions of the file .
dag_file ( CompilerMod , Dir , undefined ) - >
filename : join ( [ rebar_dir : local_cache_dir ( Dir ) , CompilerMod ,
? DAG_ROOT ++ ? DAG_EXT ] ) ;
dag_file ( CompilerMod , Dir , Label ) - >
filename : join ( [ rebar_dir : local_cache_dir ( Dir ) , CompilerMod ,
? DAG_ROOT ++ " _ " ++ Label ++ ? DAG_EXT ] ) .
% % private graph functions
% % Get dependency graph of given Erls files and their dependencies ( header files ,
% % parse transforms , behaviours etc . ) located in their directories or given
% % InclDirs . Note that last modification times stored in vertices already respect
% % dependencies induced by given graph G .
init_dag ( Compiler , InclDirs , SrcDirs , Erls , Dir , EbinDir , Label ) - >
G = digraph : new ( [ acyclic ] ) ,
try restore_dag ( Compiler , G , InclDirs , Dir , Label )
catch
_ : _ - >
? WARN ( " Failed to restore ~ ts file. Discarding it. ~n " , [ dag_file ( Compiler , Dir , Label ) ] ) ,
file : delete ( dag_file ( Compiler , Dir , Label ) )
end ,
Dirs = lists : usort ( InclDirs ++ SrcDirs ) ,
% % A source file may have been renamed or deleted . Remove it from the graph
% % and remove any beam file for that source if it exists .
Modified = maybe_rm_beams_and_edges ( G , EbinDir , Erls ) ,
Modified1 = lists : foldl ( update_dag_fun ( G , Compiler , Dirs ) , Modified , Erls ) ,
if Modified1 - > store_dag ( Compiler , G , InclDirs , Dir , Label ) ;
not Modified1 - > ok
end ,
G .
maybe_rm_beams_and_edges ( G , Dir , Files ) - >
Vertices = digraph : vertices ( G ) ,
case lists : filter ( fun ( File ) - >
case filename : extension ( File ) =:= " .erl " of
true - >
maybe_rm_beam_and_edge ( G , Dir , File ) ;
false - >
false
end
end , lists : sort ( Vertices ) -- lists : sort ( Files ) ) of
[ ] - >
false ;
_ - >
true
end .
maybe_rm_beam_and_edge ( G , OutDir , Source ) - >
% % This is NOT a double check it is the only check that the source file is actually gone
case filelib : is_regular ( Source ) of
true - >
% % Actually exists , don ' t delete
false ;
false - >
Target = target_base ( OutDir , Source ) ++ " .beam " ,
? DEBUG ( " Source ~ ts is gone, deleting previous beam file if it exists ~ ts " , [ Source , Target ] ) ,
file : delete ( Target ) ,
digraph : del_vertex ( G , Source ) ,
true
end .
target_base ( OutDir , Source ) - >
filename : join ( OutDir , filename : basename ( Source , " .erl " ) ) .
restore_dag ( Compiler , G , InclDirs , Dir , Label ) - >
case file : read_file ( dag_file ( Compiler , Dir , Label ) ) of
{ ok , Data } - >
% Since externally passed InclDirs can influence dependency graph ( see
% modify_dag ) , we have to check here that they didn ' t change .
#dag { vsn = ? DAG_VSN , info = { Vs , Es , InclDirs } } =
binary_to_term ( Data ) ,
lists : foreach (
fun ( { V , LastUpdated } ) - >
digraph : add_vertex ( G , V , LastUpdated )
end , Vs ) ,
lists : foreach (
fun ( { _ , V1 , V2 , _ } ) - >
digraph : add_edge ( G , V1 , V2 )
end , Es ) ;
{ error , _ } - >
ok
end .
store_dag ( Compiler , G , InclDirs , Dir , Label ) - >
Vs = lists : map ( fun ( V ) - > digraph : vertex ( G , V ) end , digraph : vertices ( G ) ) ,
Es = lists : map ( fun ( E ) - > digraph : edge ( G , E ) end , digraph : edges ( G ) ) ,
File = dag_file ( Compiler , Dir , Label ) ,
ok = filelib : ensure_dir ( File ) ,
Data = term_to_binary ( #dag { info = { Vs , Es , InclDirs } } , [ { compressed , 2 } ] ) ,
file : write_file ( File , Data ) .
update_dag ( G , Compiler , Dirs , Source ) - >
case digraph : vertex ( G , Source ) of
{ _ , LastUpdated } - >
case filelib : last_modified ( Source ) of
0 - >
% % The file doesn ' t exist anymore ,
% % erase it from the graph .
% % All the edges will be erased automatically .
digraph : del_vertex ( G , Source ) ,
modified ;
LastModified when LastUpdated < LastModified - >
modify_dag ( G , Compiler , Source , LastModified , filename : dirname ( Source ) , Dirs ) ;
_ - >
Modified = lists : foldl (
update_dag_fun ( G , Compiler , Dirs ) ,
false , digraph : out_neighbours ( G , Source ) ) ,
MaxModified = update_max_modified_deps ( G , Source ) ,
case Modified orelse MaxModified > LastUpdated of
true - > modified ;
false - > unmodified
end
end ;
false - >
modify_dag ( G , Compiler , Source , filelib : last_modified ( Source ) , filename : dirname ( Source ) , Dirs )
end .
modify_dag ( G , Compiler , Source , LastModified , SourceDir , Dirs ) - >
AbsIncls = Compiler : dependencies ( Source , SourceDir , Dirs ) ,
digraph : add_vertex ( G , Source , LastModified ) ,
digraph : del_edges ( G , digraph : out_edges ( G , Source ) ) ,
lists : foreach (
fun ( Incl ) - >
update_dag ( G , Compiler , Dirs , Incl ) ,
digraph : add_edge ( G , Source , Incl )
end , AbsIncls ) ,
modified .
update_dag_fun ( G , Compiler , Dirs ) - >
fun ( Erl , Modified ) - >
case update_dag ( G , Compiler , Dirs , Erl ) of
modified - > true ;
unmodified - > Modified
end
end .
update_max_modified_deps ( G , Source ) - >
MaxModified =
lists : foldl ( fun ( File , Acc ) - >
case digraph : vertex ( G , File ) of
{ _ , MaxModified } when MaxModified > Acc - >
MaxModified ;
_ - >
Acc
end
end , 0 , [ Source | digraph : out_neighbours ( G , Source ) ] ) ,
digraph : add_vertex ( G , Source , MaxModified ) ,
MaxModified .
add_to_includes ( AppInfo , Dirs ) - >
Opts = rebar_app_info : opts ( AppInfo ) ,
List = rebar_opts : get ( Opts , erl_opts , [ ] ) ,