@ -1,4 +1,6 @@
- module ( esScanner ) .
- include ( " erlSync.hrl " ) .
- behaviour ( gen_ipc ) .
- compile ( inline ) .
@ -8,14 +10,14 @@
- export ( [
start_link / 0 ,
rescan / 0 ,
info / 0 ,
swSyncNode / 1 ,
pause / 0 ,
unpause / 0 ,
setLog / 1 ,
getLog / 0 ,
curInfo / 0 ,
getOnsync / 0 ,
setOnsync / 1 ,
setLog / 1 ,
getLog / 0
swSyncNode / 1
] ) .
% % gen_ipc callbacks
@ -29,45 +31,30 @@
] ) .
- define ( SERVER , ? MODULE ) .
- define ( PRINT ( Var ) , io : format ( " DEBUG: ~p : ~p - ~p ~n ~n ~p ~n ~n " , [ ? MODULE , ? LINE , ? ? Var , Var ] ) ) .
- define ( LOG_OR_GROWL_ON ( Val ) , Val == true ; Val == all ; Val == skip_success ; is_list ( Val ) , Val =/= [ ] ) .
- define ( LOG_OR_GROWL_OFF ( Val ) , Val == false ; F == none ; F == [ ] ) .
- define ( gTimeout ( Type , Time ) , { { gTimeout , Type } , Time , Type } ) .
- define ( esCfgSync , esCfgSync ) .
- define ( Log , log ) .
- define ( moduleTime , moduleTime ) .
- define ( srcDirTime , srcDirTime ) .
- define ( srcFileTime , srcFileTime ) .
- define ( compareBeamTime , compareBeamTime ) .
- define ( compareSrcFileTime , compareSrcFileTime ) .
- define ( CfgList , [ { ? Log , all } , { ? moduleTime , 30000 } , { ? srcDirTime , 5000 } , { ? srcFileTime , 5000 } , { ? compareBeamTime , 3000 } , { ? compareSrcFileTime , 3000 } ] ) .
- type timestamp ( ) : : file : date_time ( ) | 0 .
- record ( state , {
modules = [ ] : : [ module ( ) ] ,
src Dirs = [ ] : : [ file : filename ( ) ] ,
hrl Dirs = [ ] : : [ file : filename ( ) ] ,
src Files = [ ] : : [ file : filename ( ) ] ,
hrl Files = [ ] : : [ file : filename ( ) ] ,
beamTimes = undefined : : [ { module ( ) , timestamp ( ) } ] | undefined ,
srcFileTimes = [ ] : : [ { file : filename ( ) , timestamp ( ) } ] ,
hrlFileTimes = [ ] : : [ { file : filename ( ) , timestamp ( ) } ] ,
onsyncFun = undefined ,
patching = false
modules = [ ] : : [ module ( ) ]
, hrlDirs = [ ] : : [ file : filename ( ) ]
, srcDirs = [ ] : : [ file : filename ( ) ]
, hrlFiles = [ ] : : [ file : filename ( ) ]
, srcFiles = [ ] : : [ file : filename ( ) ]
, beamTimes = undefined : : [ { module ( ) , timestamp ( ) } ] | undefined
, hrlFileTimes = undefined : : [ { file : filename ( ) , timestamp ( ) } ] | undefined
, srcFileTimes = undefined : : [ { file : filename ( ) , timestamp ( ) } ] | undefined
, onsyncFun = undefined
, swSyncNode = false
} ) .
% % * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * API start * * * * * * * * * * * * * * * * * * * * * * * * * * *
rescan ( ) - >
io : format ( " Scanning source files... ~n " ) ,
gen_ipc : cast ( ? SERVER , miCollMods ) ,
gen_ipc : cast ( ? SERVER , miCollSrcDirs ) ,
gen_ipc : cast ( ? SERVER , miCollSrcFiles ) ,
gen_ipc : cast ( ? SERVER , miCompareHrlFiles ) ,
gen_ipc : cast ( ? SERVER , miCompareSrcFiles ) ,
gen_ipc : cast ( ? SERVER , miCompareBeams ) ,
gen_ipc : cast ( ? SERVER , miCompareHrlFiles ) ,
esUtils : logSuccess ( " start Scanning source files... " ) ,
ok .
unpause ( ) - >
@ -75,24 +62,22 @@ unpause() ->
ok .
pause ( ) - >
esUtils : log_success ( " Pausing erlSync. Call sync:go() to restart ~n " ) ,
gen_ipc : cast ( ? SERVER , miPause ) ,
esUtils : logSuccess ( " Pausing erlSync. Call erlSync:run() to restart " ) ,
ok .
info ( ) - >
io : format ( " erlSync Info... ~n " ) ,
gen_ipc : call ( ? SERVER , miInfo ) ,
ok .
curInfo ( ) - >
gen_ipc : call ( ? SERVER , miCurInfo ) .
setLog ( T ) when ? LOG_OR_GROWL_O N ( T ) - >
setLog ( T ) when ? LOG_ON ( T ) - >
esUtils : setEnv ( log , T ) ,
loadCfg ( ) ,
esUtils : log_s uccess ( " Console Notifications Enabled ~n " ) ,
esUtils : logS uccess ( " Console Notifications Enabled " ) ,
ok ;
setLog ( F ) when ? LOG _OR_GROWL_OFF ( F ) - >
setLog ( _ ) - >
esUtils : setEnv ( log , none ) ,
loadCfg ( ) ,
esUtils : log_s uccess ( " Console Notifications Disabled ~n " ) ,
esUtils : logS uccess ( " Console Notifications Disabled " ) ,
ok .
getLog ( ) - >
@ -108,27 +93,29 @@ getOnsync() ->
setOnsync ( Fun ) - >
gen_ipc : call ( ? SERVER , { miSetOnsync , Fun } ) .
% % status running | pause
% % * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * API end * * * * * * * * * * * * * * * * * * * * * * * * * * *
start_link ( ) - >
gen_ipc : start_link ( { local , ? SERVER } , ? MODULE , [ ] , [ ] ) .
% % status : : running | pause
init ( [ ] ) - >
erlang : process_flag ( trap_exit , true ) ,
rescan ( ) ,
startup ( ) ,
loadCfg ( ) ,
case persistent_term : get ( ? esRecompileCnt , undefined ) of
undefined - >
IndexRef = atomics : new ( 1 , [ { signed , false } ] ) ,
persistent_term : put ( ? esRecompileCnt , IndexRef ) ;
_ - >
ignore
end ,
{ ok , running , #state { } } .
handleCall ( miGetOnsync , _ , State , _ From ) - >
OnSync = State #state.onsyncFun ,
handleCall ( miGetOnsync , _ , #state { onsyncFun = OnSync } = State , _ From ) - >
{ reply , OnSync , State } ;
handleCall ( { miSetOnsync , Fun } , _ , State , _ From ) - >
State2 = State #state { onsyncFun = Fun } ,
{ reply , ok , State2 } ;
handleCall ( miInfo , _ , State , _ Form ) - >
{ reply , ok , State #state { onsyncFun = Fun } } ;
handleCall ( miCurInfo , _ , State , _ Form ) - >
{ reply , { erlang : get ( ) , State } , State } ;
handleCall ( _ Request , _ , _ State , _ From ) - >
keepStatusState .
@ -136,119 +123,81 @@ handleCall(_Request, _, _State, _From) ->
handleCast ( miPause , _ , State ) - >
{ nextStatus , pause , State } ;
handleCast ( miUnpause , _ , State ) - >
rescan ( ) ,
{ nextStatus , running , State } ;
handleCast ( miCollMods , running , State ) - >
% Get a list of all loaded non - system modules .
AllModules = ( erlang : loaded ( ) -- esUtils : getSystemModules ( ) ) ,
% Delete excluded modules / applications
CollModules = collMods ( AllModules ) ,
% % Schedule the next interval . . .
LastCollMods = filterCollMods ( AllModules ) ,
Time = ? esCfgSync : getv ( ? moduleTime ) ,
{ keepStatus , State #state { modules = CollModules } , [ ? gTimeout ( miCollMods , Time ) ] } ;
handleCast ( miCollSrcDirs , running , State ) - >
{ keepStatus , State #state { modules = LastCollMods } , [ ? gTimeout ( miCollMods , Time ) ] } ;
handleCast ( miCollSrcDirs , running , #state { modules = Modules } = State ) - >
{ USortedSrcDirs , USortedHrlDirs } =
case application : get_env ( erlSync , srcDirs ) of
case ? esCfgSync : getv ( srcDirs ) of
undefined - >
collSrcDirs ( State , [ ] , [ ] ) ;
{ ok , { add, DirsAndOpts } } - >
collSrcDirs ( State , d irs( DirsAndOpts ) , [ ] ) ;
{ ok , { replace , DirsAndOpts } } - >
collSrcDirs ( State , [ ] , dirs ( DirsAndOpts ) )
collSrcDirs ( Modules , [ ] , [ ] ) ;
{ add , DirsAndOpts } - >
collSrcDirs ( Modules , addSrcD irs( DirsAndOpts ) , [ ] ) ;
{ only , DirsAndOpts } - >
collSrcDirs ( Modules , [ ] , a ddSrcD irs( DirsAndOpts ) )
end ,
Time = ? esCfgSync : getv ( ? srcDirTime ) ,
{ keepStatus , State #state { srcDirs = USortedSrcDirs , hrlDirs = USortedHrlDirs } , [ ? gTimeout ( miCollSrcDirs , Time ) ] } ;
handleCast ( miCollSrcFiles , running , State ) - >
% % For each source dir , get a list of source files . . .
FErl =
fun ( X , Acc ) - >
esUtils : wildcard ( X , " .* \\ .(erl|dtl|lfe|ex)$ " ) ++ Acc
handleCast ( miCollSrcFiles , running , #state { hrlDirs = HrlDirs , srcDirs = SrcDirs } = State ) - >
FSrc =
fun ( Dir , Acc ) - >
esUtils : wildcard ( Dir , " .* \\ .(erl|dtl|lfe|ex)$ " ) ++ Acc
end ,
ErlFiles = lists : usort ( lists : foldl ( FErl , [ ] , State #state.srcDirs ) ) ,
% % For each include dir , get a list of hrl files . . .
SrcFiles = lists : usort ( lists : foldl ( FSrc , [ ] , SrcDirs ) ) ,
FHrl =
fun ( X , Acc ) - >
esUtils : wildcard ( X , " .* \\ .hrl$ " ) ++ Acc
fun ( Dir , Acc ) - >
esUtils : wildcard ( Dir , " .* \\ .hrl$ " ) ++ Acc
end ,
HrlFiles = lists : usort ( lists : foldl ( FHrl , [ ] , State #state.hrlDirs ) ) ,
% % Schedule the next interval . . .
HrlFiles = lists : usort ( lists : foldl ( FHrl , [ ] , HrlDirs ) ) ,
Time = ? esCfgSync : getv ( ? srcFileTime ) ,
% % Return with updated files . . .
{ keepStatus , State #state { srcFiles = ErlFiles , hrlFiles = HrlFiles } , [ ? gTimeout ( miCollSrcFiles , Time ) ] } ;
handleCast ( miCompareBeams , running , #state { onsyncFun = OnsyncFun } = State ) - >
% % Create a list of beam file lastmod times , but filter out modules not having
% % a valid beam file reference .
F = fun ( X ) - >
case code : which ( X ) of
Beam when is_list ( Beam ) - >
case filelib : last_modified ( Beam ) of
0 - >
false ; % % file not found
LastMod - >
{ true , { X , LastMod } }
end ;
_ Other - >
false % % non_existing | cover_compiled | preloaded
end
end ,
NewBeamLastMod = lists : usort ( lists : filtermap ( F , State #state.modules ) ) ,
% % Compare to previous results , if there are changes , then reload
% % the beam . . .
reloadChangedMod ( State #state.beamTimes , NewBeamLastMod , State #state.patching , OnsyncFun , [ ] ) ,
{ keepStatus , State #state { srcFiles = SrcFiles , hrlFiles = HrlFiles } , [ ? gTimeout ( miCollSrcFiles , Time ) ] } ;
handleCast ( miCompareBeams , running , #state { modules = Modules , beamTimes = BeamTimes , onsyncFun = OnsyncFun , swSyncNode = SwSyncNode } = State ) - >
BeamTimeList = [ { Mod , LastMod } | | Mod < - Modules , LastMod < - [ modLastmod ( Mod ) ] , LastMod =/= 0 ] ,
NewBeamLastMod = lists : usort ( BeamTimeList ) ,
reloadChangedMod ( BeamTimes , NewBeamLastMod , SwSyncNode , OnsyncFun , [ ] ) ,
Time = ? esCfgSync : getv ( ? compareBeamTime ) ,
{ keepStatus , State #state { beamTimes = NewBeamLastMod } , [ ? gTimeout ( miCompareBeams , Time ) ] } ;
handleCast ( miCompareSrcFiles , running , State ) - >
handleCast ( miCompareSrcFiles , running , #state { srcFiles = SrcFiles , srcFileTimes = SrcFileTimes , swSyncNode = SwSyncNode } = State ) - >
atomics : put ( persistent_term : get ( ? esRecompileCnt ) , 1 , 0 ) ,
% % Create a list of file lastmod times . . .
F =
fun ( X ) - >
LastMod = filelib : last_modified ( X ) ,
{ X , LastMod }
end ,
NewSrcFileLastMod = lists : usort ( [ F ( X ) | | X < - State #state.srcFiles ] ) ,
SrcFileTimeList = [ { Src , LastMod } | | Src < - SrcFiles , LastMod < - [ filelib : last_modified ( Src ) ] , LastMod =/= 0 ] ,
NewSrcFileLastMod = lists : usort ( SrcFileTimeList ) ,
% % Compare to previous results , if there are changes , then recompile the file . . .
recompileChangeSrcFile ( State #state.srcFileTimes , NewSrcFileLastMod , State #state.patching ) ,
% % Schedule the next interval . . .
recompileChangeSrcFile ( SrcFileTimes , NewSrcFileLastMod , SwSyncNode ) ,
case atomics : get ( persistent_term : get ( ? esRecompileCnt ) , 1 ) > 0 of
true - >
gen_ipc : cast ( ? SERVER , miCompareBeams ) ;
_ - >
ignore
end ,
Time = ? esCfgSync : getv ( ? compareSrcFileTime ) ,
{ keepStatus , State #state { srcFileTimes = NewSrcFileLastMod } , [ ? gTimeout ( miCompareSrcFiles , Time ) ] } ;
handleCast ( miCompareHrlFiles , running , State ) - >
handleCast ( miCompareHrlFiles , running , #state { hrlFiles = HrlFiles , srcFiles = SrcFiles , hrlFileTimes = HrlFileTimes , swSyncNode = SwSyncNode } = State ) - >
atomics : put ( persistent_term : get ( ? esRecompileCnt ) , 1 , 0 ) ,
% % Create a list of file lastmod times . . .
F =
fun ( X ) - >
LastMod = filelib : last_modified ( X ) ,
{ X , LastMod }
end ,
NewHrlFileLastMod = lists : usort ( [ F ( X ) | | X < - State #state.hrlFiles ] ) ,
HrlFileTimeList = [ { Hrl , LastMod } | | Hrl < - HrlFiles , LastMod < - [ filelib : last_modified ( Hrl ) ] , LastMod =/= 0 ] ,
NewHrlFileLastMod = lists : usort ( HrlFileTimeList ) ,
% % Compare to previous results , if there are changes , then recompile src files that depends
recompileChangeHrlFile ( State #state.hrlFileTimes , NewHrlFileLastMod , State #state.srcFiles , State #state.patching ) ,
% % Schedule the next interval . . .
recompileChangeHrlFile ( HrlFileTimes , NewHrlFileLastMod , SrcFiles , SwSyncNode ) ,
case atomics : get ( persistent_term : get ( ? esRecompileCnt ) , 1 ) > 0 of
true - >
gen_ipc : cast ( ? SERVER , miCompareBeams ) ;
_ - >
ignore
end ,
Time = ? esCfgSync : getv ( ? compareSrcFileTime ) ,
{ keepStatus , State #state { hrlFileTimes = NewHrlFileLastMod } , [ ? gTimeout ( miCompareHrlFiles , Time ) ] } ;
handleCast ( { miSyncNode , IsSync } , _ , State ) - >
case IsSync of
true - >
{ keepStatus , State #state { patching = true } } ;
{ keepStatus , State #state { swSyncNode = true } } ;
_ - >
{ keepStatus , State #state { patching = false } }
{ keepStatus , State #state { swSyncNode = false } }
end ;
handleCast ( _ Msg , _ , _ State ) - >
keepStatusState .
@ -260,11 +209,15 @@ handleOnevent({gTimeout, _}, Msg, Status, State) ->
handleOnevent ( _ EventType , _ EventContent , _ Status , _ State ) - >
keepStatusState .
terminate ( _ Reason , _ Status , _ State ) - >
ok .
% % * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PRIVATE FUNCTIONS start * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
dirs ( DirsAndOpts ) - >
a ddSrcD irs( DirsAndOpts ) - >
[
begin
% % ensure module out path exists & in our code list
% % ensure module out path exists & in our code path list
case proplists : get_value ( outdir , Opts ) of
undefined - >
true ;
@ -274,59 +227,48 @@ dirs(DirsAndOpts) ->
end ,
setOptions ( Dir , Opts ) ,
Dir
end | | { Dir , Opts } < - DirsAndOpts ] .
terminate ( _ Reason , _ Status , _ State ) - >
ok .
end | | { Dir , Opts } < - DirsAndOpts
] .
% % % PRIVATE FUNCTIONS % % %
reloadChangedMod ( [ { Module , LastMod } | T1 ] , [ { Module , LastMod } | T2 ] , EnablePatching , OnsyncFun , Acc ) - >
% % Beam hasn ' t changed , do nothing . . .
reloadChangedMod ( T1 , T2 , EnablePatching , OnsyncFun , Acc ) ;
reloadChangedMod ( [ { Module , _ } | T1 ] , [ { Module , _ } | T2 ] , EnablePatching , OnsyncFun , Acc ) - >
% % Beam has changed , reload . . .
reloadChangedMod ( [ { Module , LastMod } | T1 ] , [ { Module , LastMod } | T2 ] , SwSyncNode , OnsyncFun , Acc ) - >
reloadChangedMod ( T1 , T2 , SwSyncNode , OnsyncFun , Acc ) ;
reloadChangedMod ( [ { Module , _ } | T1 ] , [ { Module , _ } | T2 ] , SwSyncNode , OnsyncFun , Acc ) - >
case code : get_object_code ( Module ) of
error - >
Msg = io_lib : format ( " Error loading object code for ~p ~n " , [ Module ] ) ,
esUtils : log_e rrors ( Msg ) ,
reloadChangedMod ( T1 , T2 , EnablePatching , OnsyncFun , Acc ) ;
Msg = io_lib : format ( " Error loading object code for ~p " , [ Module ] ) ,
esUtils : logE rrors ( Msg ) ,
reloadChangedMod ( T1 , T2 , SwSyncNode , OnsyncFun , Acc ) ;
{ Module , Binary , Filename } - >
code : load_binary ( Module , Filename , Binary ) ,
% % If patching is enabled , then reload the module across * all * connected
% % erlang VMs , and save the compiled beam to disk .
case EnablePatching of
case code : load_binary ( Module , Filename , Binary ) of
{ module , Module } - >
Msg = io_lib : format ( " Reloaded(Beam changed) Mod: ~s Success " , [ Module ] ) ,
esUtils : logSuccess ( Msg ) ;
{ error , What } - >
Msg = io_lib : format ( " Reloaded(Beam changed) Mod: ~s Errors Reason: ~p " , [ Module , What ] ) ,
esUtils : logErrors ( Msg )
end ,
case SwSyncNode of
true - >
{ ok , NumNodes } = syncLoadModOnAllNodes ( Module ) ,
Msg = io_lib : format ( " ~s : Reloaded on ~p nodes! (Beam changed.) ~n " , [ Module , NumNodes ] ) ,
esUtils : log_success ( Msg ) ;
{ ok , NumNodes , Nodes } = syncLoadModOnAllNodes ( Module ) ,
MsgNodes = io_lib : format ( " Reloaded(Beam changed) Mod: ~s on ~p nodes: ~p " , [ Module , NumNodes , Nodes ] ) ,
esUtils : logS uccess ( MsgNodes ) ;
false - >
% % Print a status message . . .
Msg = io_lib : format ( " ~s : Reloaded! (Beam changed.) ~n " , [ Module ] ) ,
esUtils : log_success ( Msg )
ignore
end ,
reloadChangedMod ( T1 , T2 , EnablePatching , OnsyncFun , [ Module | Acc ] )
reloadChangedMod ( T1 , T2 , SwSyncNode , OnsyncFun , [ Module | Acc ] )
end ;
reloadChangedMod ( [ { Module1 , _ LastMod1 } | T1 ] = OldLastMods , [ { Module2 , _ LastMod2 } | T2 ] = NewLastMods , EnablePatching , OnsyncFun , Acc ) - >
reloadChangedMod ( [ { Module1 , _ LastMod1 } | T1 ] = OldLastMods , [ { Module2 , _ LastMod2 } | T2 ] = NewLastMods , SwSyncNode , OnsyncFun , Acc ) - >
% % Lists are different , advance the smaller one . . .
case Module1 < Module2 of
true - >
reloadChangedMod ( T1 , NewLastMods , EnablePatching , OnsyncFun , Acc ) ;
reloadChangedMod ( T1 , NewLastMods , SwSyncNode , OnsyncFun , Acc ) ;
false - >
reloadChangedMod ( OldLastMods , T2 , EnablePatching , OnsyncFun , Acc )
reloadChangedMod ( OldLastMods , T2 , SwSyncNode , OnsyncFun , Acc )
end ;
reloadChangedMod ( A , B , _ EnablePatching , OnsyncFun , Acc ) when A =:= [ ] ; B =:= [ ] - >
% MsgAdd =
% case EnablePatching of
% true - > " on " ++ integer_to_list ( length ( get_nodes ( ) ) ) ++ " nodes. " ;
% false - > " . "
% end ,
% % Done .
reloadChangedMod ( A , B , _ SwSyncNode , OnsyncFun , Acc ) when A =:= [ ] ; B =:= [ ] - >
fireOnsync ( OnsyncFun , Acc ) ,
ok ;
reloadChangedMod ( undefined , _ Other , _ , _ , _ ) - >
% % First load , do nothing .
ok .
fireOnsync ( OnsyncFun , Modules ) - >
@ -348,58 +290,67 @@ getNodes() ->
lists : usort ( lists : flatten ( nodes ( ) ++ [ rpc : call ( X , erlang , nodes , [ ] ) | | X < - nodes ( ) ] ) ) -- [ node ( ) ] .
syncLoadModOnAllNodes ( Module ) - >
% % Get a list of nodes known by this node , plus all attached
% % nodes .
% % Get a list of nodes known by this node , plus all attached nodes .
Nodes = getNodes ( ) ,
io : format ( " [ ~s : ~p ] DEBUG - Nodes: ~p ~n " , [ ? MODULE , ? LINE , Nodes ] ) ,
NumNodes = length ( Nodes ) ,
{ Module , Binary , _ } = code : get_object_code ( Module ) ,
FSync =
fun ( Node ) - >
io : format ( " [ ~s : ~p ] DEBUG - Node: ~p ~n " , [ ? MODULE , ? LINE , Node ] ) ,
Msg = io_lib : format ( " Reloading ' ~s ' on ~s . ~n ", [ Module , Node ] ) ,
esUtils : log_s uccess ( Msg ) ,
io : format ( " [ ~s : ~p ] DEBUG - Node: ~p " , [ ? MODULE , ? LINE , Node ] ) ,
Msg = io_lib : format ( " Reloading ' ~s ' on ~s " , [ Module , Node ] ) ,
esUtils : logS uccess ( Msg ) ,
rpc : call ( Node , code , ensure_loaded , [ Module ] ) ,
case rpc : call ( Node , code , which , [ Module ] ) of
Filename when is_binary ( Filename ) orelse is_list ( Filename ) - >
% % File exists , overwrite and load into VM .
ok = rpc : call ( Node , file , write_file , [ Filename , Binary ] ) ,
rpc : call ( Node , code , purge , [ Module ] ) ,
{ module , Module } = rpc : call ( Node , code , load_file , [ Module ] ) ;
case rpc : call ( Node , code , load_file , [ Module ] ) of
{ module , Module } - >
Msg = io_lib : format ( " Reloaded(Beam changed) Mod: ~s and write Success on node: ~p " , [ Node , Module ] ) ,
esUtils : logSuccess ( Msg ) ;
{ error , What } - >
Msg = io_lib : format ( " Reloaded(Beam changed) Mod: ~s and write Errors on node: ~p Reason: ~p " , [ Module , Node , What ] ) ,
esUtils : logErrors ( Msg )
end ;
_ - >
% % File doesn ' t exist , just load into VM .
{ module , Module } = rpc : call ( Node , code , load_binary , [ Module , undefined , Binary ] )
case rpc : call ( Node , code , load_binary , [ Module , undefined , Binary ] ) of
{ module , Module } - >
Msg = io_lib : format ( " Reloaded(Beam changed) Mod: ~s Success on node: ~p " , [ Node , Module ] ) ,
esUtils : logSuccess ( Msg ) ;
{ error , What } - >
Msg = io_lib : format ( " Reloaded(Beam changed) Mod: ~s Errors on node: ~p Reason: ~p " , [ Module , Node , What ] ) ,
esUtils : logErrors ( Msg )
end
end
end ,
[ FSync ( X ) | | X < - Nodes ] ,
{ ok , NumNodes } .
recompileChangeSrcFile ( [ { File , LastMod } | T1 ] , [ { File , LastMod } | T2 ] , EnablePatching ) - >
% % Beam hasn ' t changed , do nothing . . .
recompileChangeSrcFile ( T1 , T2 , EnablePatching ) ;
recompileChangeSrcFile ( [ { File , _ } | T1 ] , [ { File , _ } | T2 ] , EnablePatching ) - >
% % File has changed , recompile . . .
recompileSrcFile ( File , EnablePatching ) ,
recompileChangeSrcFile ( T1 , T2 , EnablePatching ) ;
recompileChangeSrcFile ( [ { File1 , _ LastMod1 } | T1 ] = OldSrcFiles , [ { File2 , LastMod2 } | T2 ] = NewSrcFiles , EnablePatching ) - >
{ ok , NumNodes , Nodes } .
recompileChangeSrcFile ( [ { File , LastMod } | T1 ] , [ { File , LastMod } | T2 ] , SwSyncNode ) - >
recompileChangeSrcFile ( T1 , T2 , SwSyncNode ) ;
recompileChangeSrcFile ( [ { File , _ } | T1 ] , [ { File , _ } | T2 ] , SwSyncNode ) - >
recompileSrcFile ( File , SwSyncNode ) ,
recompileChangeSrcFile ( T1 , T2 , SwSyncNode ) ;
recompileChangeSrcFile ( [ { File1 , _ LastMod1 } | T1 ] = OldSrcFiles , [ { File2 , LastMod2 } | T2 ] = NewSrcFiles , SwSyncNode ) - >
% % Lists are different . . .
case File1 < File2 of
true - >
% % File was removed , do nothing . . .
recompileChangeSrcFile ( T1 , NewSrcFiles , EnablePatching ) ;
recompileChangeSrcFile ( T1 , NewSrcFiles , SwSyncNode ) ;
false - >
maybeRecompileSrcFile ( File2 , LastMod2 , EnablePatching ) ,
recompileChangeSrcFile ( OldSrcFiles , T2 , EnablePatching )
maybeRecompileSrcFile ( File2 , LastMod2 , SwSyncNode ) ,
recompileChangeSrcFile ( OldSrcFiles , T2 , SwSyncNode )
end ;
recompileChangeSrcFile ( [ ] , [ { File , LastMod } | T2 ] , EnablePatching ) - >
maybeRecompileSrcFile ( File , LastMod , EnablePatching ) ,
recompileChangeSrcFile ( [ ] , T2 , EnablePatching ) ;
recompileChangeSrcFile ( [ ] , [ { File , LastMod } | T2 ] , SwSyncNode ) - >
maybeRecompileSrcFile ( File , LastMod , SwSyncNode ) ,
recompileChangeSrcFile ( [ ] , T2 , SwSyncNode ) ;
recompileChangeSrcFile ( _ A , [ ] , _ ) - >
% % All remaining files , if any , were removed .
ok ;
recompileChangeSrcFile ( undefined , _ Other , _ ) - >
% % First load , do nothing .
ok .
erlydtlCompile ( SrcFile , Options ) - >
@ -429,20 +380,20 @@ lfe_compile(SrcFile, Options) ->
Compiler = lfe_comp ,
Compiler : file ( SrcFile , Options ) .
maybeRecompileSrcFile ( File , LastMod , EnablePatching ) - >
maybeRecompileSrcFile ( File , LastMod , SwSyncNode ) - >
Module = list_to_atom ( filename : basename ( File , " .erl " ) ) ,
case code : which ( Module ) of
BeamFile when is_list ( BeamFile ) - >
% % check with beam file
case filelib : last_modified ( BeamFile ) of
BeamLastMod when LastMod > BeamLastMod - >
recompileSrcFile ( File , EnablePatching ) ;
recompileSrcFile ( File , SwSyncNode ) ;
_ - >
ok
end ;
_ - >
% % File is new , recompile . . .
recompileSrcFile ( File , EnablePatching )
recompileSrcFile ( File , SwSyncNode )
end .
getCompileFunAndModuleName ( SrcFile ) - >
@ -463,12 +414,9 @@ getObjectCode(Module) ->
_ - > undefined
end .
reloadIfNecessary ( _ CompileFun , SrcFile , Module , Binary , Binary , _ Options , Warnings ) - >
% % Compiling didn 't change the beam code. Don' t reload . . .
printResults ( Module , SrcFile , [ ] , Warnings ) ,
{ ok , [ ] , Warnings } ;
reloadIfNecessary ( CompileFun , SrcFile , Module , _ OldBinary , _ Binary , Options , Warnings ) - >
reloadIfNecessary ( _ CompileFun , _ SrcFile , _ Module , Binary , Binary , _ Options ) - >
ok ;
reloadIfNecessary ( CompileFun , SrcFile , Module , _ OldBinary , _ Binary , Options ) - >
% % Compiling changed the beam code . Compile and reload .
CompileFun ( SrcFile , Options ) ,
% % Try to load the module . . .
@ -476,89 +424,66 @@ reloadIfNecessary(CompileFun, SrcFile, Module, _OldBinary, _Binary, Options, War
{ module , Module } - > ok ;
{ error , nofile } - > errorNoFile ( Module ) ;
{ error , embedded } - >
% % Module is not yet loaded , load it .
case code : load_file ( Module ) of
case code : load_file ( Module ) of % % Module is not yet loaded , load it .
{ module , Module } - > ok ;
{ error , nofile } - > errorNoFile ( Module )
end
end ,
gen_ipc : cast ( ? SERVER , miCompareBeams ) ,
% % Print the warnings . . .
printResults ( Module , SrcFile , [ ] , Warnings ) ,
{ ok , [ ] , Warnings } .
atomics : add ( persistent_term : get ( ? esRecompileCnt ) , 1 , 1 ) .
errorNoFile ( Module ) - >
Msg = io_lib : format ( " ~p :0: Couldn't load module: nofile~n " , [ Module ] ) ,
esUtils : log_w arnings ( [ Msg ] ) .
Msg = io_lib : format ( " ~p Couldn't load module: nofile " , [ Module ] ) ,
esUtils : logWarnings ( [ Msg ] ) .
recompileSrcFile ( SrcFile , _ EnablePatching ) - >
recompileSrcFile ( SrcFile , _ SwSyncNode ) - >
% % Get the module , src dir , and options . . .
case esUtils : getSrcDir ( SrcFile ) of
{ ok , SrcDir } - >
{ CompileFun , Module } = getCompileFunAndModuleName ( SrcFile ) ,
% % Get the old binary code . . .
OldBinary = getObjectCode ( Module ) ,
case getOptions ( SrcDir ) of
{ ok , Options } - >
case CompileFun ( SrcFile , [ binary , return | Options ] ) of
{ ok , Module , Binary , Warnings } - >
reloadIfNecessary ( CompileFun , SrcFile , Module , OldBinary , Binary , Options , Warnings ) ;
reloadIfNecessary ( CompileFun , SrcFile , Module , OldBinary , Binary , Options ) ,
printResults ( Module , SrcFile , [ ] , Warnings ) ,
{ ok , [ ] , Warnings } ;
{ ok , [ { ok , Module , Binary , Warnings } ] , Warnings2 } - >
reloadIfNecessary ( CompileFun , SrcFile , Module , OldBinary , Binary , Options , Warnings ++ Warnings2 ) ;
reloadIfNecessary ( CompileFun , SrcFile , Module , OldBinary , Binary , Options ) ,
printResults ( Module , SrcFile , [ ] , Warnings ++ Warnings2 ) ,
{ ok , [ ] , Warnings ++ Warnings2 } ;
{ ok , multiple , Results , Warnings } - >
Reloader =
fun ( { CompiledModule , Binary } ) - >
{ ok , _ , _ } = reloadIfNecessary ( CompileFun , SrcFile , CompiledModule , OldBinary , Binary , Options , Warnings )
end ,
lists : foreach ( Reloader , Results ) ,
[ reloadIfNecessary ( CompileFun , SrcFile , CompiledModule , OldBinary , Binary , Options ) | | { CompiledModule , Binary } < - Results ] ,
printResults ( Module , SrcFile , [ ] , Warnings ) ,
{ ok , [ ] , Warnings } ;
{ ok , OtherModule , _ Binary , Warnings } - >
Desc = io_lib : format ( " Module definition ( ~p ) differs from expected ( ~s ) " , [ OtherModule , filename : rootname ( filename : basename ( SrcFile ) ) ] ) ,
Errors = [ { SrcFile , { 0 , Module , Desc } } ] ,
printResults ( Module , SrcFile , Errors , Warnings ) ,
{ ok , Errors , Warnings } ;
{ error , Errors , Warnings } - >
% % Compiling failed . Print the warnings and errors . . .
printResults ( Module , SrcFile , Errors , Warnings ) ,
{ ok , Errors , Warnings }
end ;
undefined - >
Msg = io_lib : format ( " Unable to determine options for ~p " , [ SrcFile ] ) ,
esUtils : log_e rrors ( Msg )
esUtils : logE rrors ( Msg )
end ;
_ - >
Msg = io_lib : format ( " not find the file ~p " , [ SrcFile ] ) ,
esUtils : log_e rrors ( Msg )
esUtils : logE rrors ( Msg )
end .
printResults ( Module , SrcFile , [ ] , [ ] ) - >
Msg = io_lib : format ( " ~s :0: Recompiled. ~n " , [ SrcFile ] ) ,
case code : is_loaded ( Module ) of
{ file , _ } - >
ok ;
false - >
ignore
end ,
esUtils : log_success ( lists : flatten ( Msg ) ) ;
printResults ( _ Module , SrcFile , [ ] , [ ] ) - >
Msg = io_lib : format ( " ~s Recompiled " , [ SrcFile ] ) ,
esUtils : logSuccess ( lists : flatten ( Msg ) ) ;
printResults ( _ Module , SrcFile , [ ] , Warnings ) - >
Msg = [
formatErrors ( SrcFile , [ ] , Warnings ) ,
io_lib : format ( " ~s :0: Recompiled with ~p warnings ~n " , [ SrcFile , length ( Warnings ) ] )
] ,
esUtils : log_warnings ( Msg ) ;
Msg = [ formatErrors ( SrcFile , [ ] , Warnings ) , io_lib : format ( " ~s Recompiled with ~p warnings " , [ SrcFile , length ( Warnings ) ] ) ] ,
esUtils : logWarnings ( Msg ) ;
printResults ( _ Module , SrcFile , Errors , Warnings ) - >
Msg = [ formatErrors ( SrcFile , Errors , Warnings ) ] ,
esUtils : log_e rrors ( Msg ) .
esUtils : logErrors ( Msg ) .
% % @ private Print error messages in a pretty and user readable way .
@ -568,11 +493,12 @@ formatErrors(File, Errors, Warnings) ->
AllWarnings1 = lists : sort ( lists : flatten ( [ X | | { _ , X } < - Warnings ] ) ) ,
AllWarnings2 = [ { Line , " Warning " , Module , Description } | | { Line , Module , Description } < - AllWarnings1 ] ,
Everything = lists : sort ( AllErrors2 ++ AllWarnings2 ) ,
F = fun ( { Line , Prefix , Module , ErrorDescription } ) - >
Msg = formatError ( Module , ErrorDescription ) ,
io_lib : format ( " ~s : ~p : ~s : ~s ~n " , [ File , Line , Prefix , Msg ] )
end ,
[ F ( X ) | | X < - Everything ] .
FPck =
fun ( { Line , Prefix , Module , ErrorDescription } ) - >
Msg = formatError ( Module , ErrorDescription ) ,
io_lib : format ( " ~s : ~p : ~s : ~s " , [ File , Line , Prefix , Msg ] )
end ,
[ FPck ( X ) | | X < - Everything ] .
formatError ( Module , ErrorDescription ) - >
case erlang : function_exported ( Module , format_error , 1 ) of
@ -580,36 +506,32 @@ formatError(Module, ErrorDescription) ->
false - > io_lib : format ( " ~s " , [ ErrorDescription ] )
end .
recompileChangeHrlFile ( [ { File , LastMod } | T1 ] , [ { File , LastMod } | T2 ] , SrcFiles , Patching ) - >
% % Hrl hasn ' t changed , do nothing . . .
recompileChangeHrlFile ( T1 , T2 , SrcFiles , Patching ) ;
recompileChangeHrlFile ( [ { File , _ } | T1 ] , [ { File , _ } | T2 ] , SrcFiles , Patching ) - >
% % File has changed , recompile . . .
recompileChangeHrlFile ( [ { File , LastMod } | T1 ] , [ { File , LastMod } | T2 ] , SrcFiles , SwSyncNode ) - >
recompileChangeHrlFile ( T1 , T2 , SrcFiles , SwSyncNode ) ;
recompileChangeHrlFile ( [ { File , _ } | T1 ] , [ { File , _ } | T2 ] , SrcFiles , SwSyncNode ) - >
WhoInclude = whoInclude ( File , SrcFiles ) ,
[ recompileSrcFile ( SrcFile , Patching ) | | SrcFile < - WhoInclude ] ,
recompileChangeHrlFile ( T1 , T2 , SrcFiles , Patching ) ;
recompileChangeHrlFile ( [ { File1 , _ LastMod1 } | T1 ] = OldHrlFiles , [ { File2 , LastMod2 } | T2 ] = NewHrlFiles , SrcFiles , Patching ) - >
[ recompileSrcFile ( SrcFile , SwSyncNode ) | | SrcFile < - WhoInclude ] ,
recompileChangeHrlFile ( T1 , T2 , SrcFiles , SwSyncNode ) ;
recompileChangeHrlFile ( [ { File1 , _ LastMod1 } | T1 ] = OldHrlFiles , [ { File2 , LastMod2 } | T2 ] = NewHrlFiles , SrcFiles , SwSyncNode ) - >
% % Lists are different . . .
case File1 < File2 of
true - >
% % File was removed , do nothing . . .
warnDelHrlFiles ( File1 , SrcFiles ) ,
recompileChangeHrlFile ( T1 , NewHrlFiles , SrcFiles , Patching ) ;
recompileChangeHrlFile ( T1 , NewHrlFiles , SrcFiles , SwSyncNode ) ;
false - >
% % File is new , look for src that include it
WhoInclude = whoInclude ( File2 , SrcFiles ) ,
[ maybeRecompileSrcFile ( SrcFile , LastMod2 , Patching ) | | SrcFile < - WhoInclude ] ,
recompileChangeHrlFile ( OldHrlFiles , T2 , SrcFiles , Patching )
[ maybeRecompileSrcFile ( SrcFile , LastMod2 , SwSyncNode ) | | SrcFile < - WhoInclude ] ,
recompileChangeHrlFile ( OldHrlFiles , T2 , SrcFiles , SwSyncNode )
end ;
recompileChangeHrlFile ( [ ] , [ { File , LastMod } | T2 ] , SrcFiles , Patching ) - >
% % File is new , look for src that include it
recompileChangeHrlFile ( [ ] , [ { File , LastMod } | T2 ] , SrcFiles , SwSyncNode ) - >
WhoInclude = whoInclude ( File , SrcFiles ) ,
[ maybeRecompileSrcFile ( SrcFile , LastMod , Patching ) | | SrcFile < - WhoInclude ] ,
recompileChangeHrlFile ( [ ] , T2 , SrcFiles , Patching ) ;
recompileChangeHrlFile ( [ { File1 , _ LastMod1 } | T1 ] , [ ] , SrcFiles , Patching ) - >
% % Rest of file ( s ) removed , warn and process next
[ maybeRecompileSrcFile ( SrcFile , LastMod , SwSyncNode ) | | SrcFile < - WhoInclude ] ,
recompileChangeHrlFile ( [ ] , T2 , SrcFiles , SwSyncNode ) ;
recompileChangeHrlFile ( [ { File1 , _ LastMod1 } | T1 ] , [ ] , SrcFiles , SwSyncNode ) - >
warnDelHrlFiles ( File1 , SrcFiles ) ,
recompileChangeHrlFile ( T1 , [ ] , SrcFiles , Patching ) ;
recompileChangeHrlFile ( T1 , [ ] , SrcFiles , SwSyncNode ) ;
recompileChangeHrlFile ( [ ] , [ ] , _ , _ ) - >
% % Done
ok ;
@ -621,17 +543,18 @@ warnDelHrlFiles(HrlFile, SrcFiles) ->
WhoInclude = whoInclude ( HrlFile , SrcFiles ) ,
case WhoInclude of
[ ] - > ok ;
_ - > io : format (
" Warning. Deleted ~p file included in existing src files: ~p ~n " ,
[ filename : basename ( HrlFile ) , lists : map ( fun ( File ) - > filename : basename ( File ) end , WhoInclude ) ] )
_ - >
Msg = io_lib : format ( " Warning. Deleted ~p file included in existing src files: ~p " , [ filename : basename ( HrlFile ) , lists : map ( fun ( File ) - > filename : basename ( File ) end , WhoInclude ) ] ) ,
esUtils : logSuccess ( lists : flatten ( Msg ) )
end .
whoInclude ( HrlFile , SrcFiles ) - >
HrlFileBaseName = filename : basename ( HrlFile ) ,
Pred = fun ( SrcFile ) - >
{ ok , Forms } = epp_dodger : parse_file ( SrcFile ) ,
isInclude ( HrlFileBaseName , Forms )
end ,
Pred =
fun ( SrcFile ) - >
{ ok , Forms } = epp_dodger : parse_file ( SrcFile ) ,
isInclude ( HrlFileBaseName , Forms )
end ,
lists : filter ( Pred , SrcFiles ) .
isInclude ( _ HrlFile , [ ] ) - >
@ -645,27 +568,23 @@ isInclude(HrlFile, [{tree, attribute, _, {attribute, _, [{_, _, IncludeFile}]}}
isInclude ( HrlFile , [ _ SomeForm | Forms ] ) - >
isInclude ( HrlFile , Forms ) .
c ollMods( Modules ) - >
excludeMods ( whitelist Mods( Modules ) ) .
filterC ollMods( Modules ) - >
excludeMods ( only Mods( Modules ) ) .
whitelist Mods( Modules ) - >
case application : get_env ( erlSync , whitelist Mods) of
{ ok , [] } - >
only Mods( Modules ) - >
case ? esCfgSync : getv ( ? only Mods) of
[ ] - >
Modules ;
{ ok , WhitelistMods } - >
[ Mod | | Mod < - Modules , checkModIsMatch ( Mod , WhitelistMods ) == true ] ;
_ - >
Modules
OnlyMods - >
[ Mod | | Mod < - Modules , checkModIsMatch ( OnlyMods , Mod ) == true ]
end .
excludeMods ( Modules ) - >
case application : get_env ( erlSync , excludedMods ) of
{ ok , [] } - >
case ? esCfgSync : getv ( ? excludedMods ) of
[ ] - >
Modules ;
{ ok , ExcludedModules } - >
[ Mod | | Mod < - Modules , checkModIsMatch ( Mod , ExcludedModules ) == false ] ;
_ - >
Modules
ExcludedModules - >
[ Mod | | Mod < - Modules , checkModIsMatch ( ExcludedModules , Mod ) == false ]
end .
checkModIsMatch ( [ ] , _ Module ) - >
@ -685,23 +604,17 @@ checkModIsMatch([ModOrPattern | T], Module) ->
checkModIsMatch ( T , Module )
end .
collSrcDirs ( State , ExtraDirs , ReplaceDirs ) - >
% % Extract the compile / options / source / dir from each module .
F =
collSrcDirs ( Modules , AddDirs , OnlyDirs ) - >
FColl =
fun
( X , { SrcAcc , HrlAcc } = Acc ) - >
% % Get the dir . . .
case esUtils : getSrcDirFromMod ( X ) of
( Mod , { SrcAcc , HrlAcc } = Acc ) - >
case esUtils : getModSrcDir ( Mod ) of
{ ok , SrcDir } - >
case isReplaceDir ( SrcDir , ReplaceDirs ) of
case isOnlyDir ( OnlyDirs , SrcDir ) of
true - >
% % Get the options , storse under the dir . . .
{ ok , Options } = esUtils : getOptionsFromMod ( X ) ,
% % Store the options for later reference . . .
{ ok , Options } = esUtils : getModOptions ( Mod ) ,
HrlDir = proplists : get_all_values ( i , Options ) ,
setOptions ( SrcDir , Options ) ,
% % Return the dir . . .
{ [ SrcDir | SrcAcc ] , HrlDir ++ HrlAcc } ;
_ - >
Acc
@ -710,28 +623,34 @@ collSrcDirs(State, ExtraDirs, ReplaceDirs) ->
Acc
end
end ,
{ SrcDirs , HrlDirs } = lists : foldl ( F , { Extra Dirs, [ ] } , State #state.m odules) ,
{ SrcDirs , HrlDirs } = lists : foldl ( FColl , { Add Dirs, [ ] } , M odules) ,
USortedSrcDirs = lists : usort ( SrcDirs ) ,
USortedHrlDirs = lists : usort ( HrlDirs ) ,
% % InitialDirs = sync_utils : initial_src_dirs ( ) ,
% % Return with updated dirs . . .
{ USortedSrcDirs , USortedHrlDirs } .
isReplaceDir ( _ , [ ] ) - >
isOnlyDir ( [ ] , _ ) - >
true ;
isReplaceDir ( SrcDir , ReplaceDirs ) - >
lists : foldl (
fun
( Dir , false ) - >
case re : run ( SrcDir , Dir ) of
nomatch - > false ;
_ - > true
end ;
( _ , Acc ) - > Acc
end , false , ReplaceDirs ) .
% % * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * misc fun start * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
isOnlyDir ( ReplaceDirs , SrcDir ) - >
isMatchDir ( ReplaceDirs , SrcDir ) .
isMatchDir ( [ ] , _ SrcDir ) - >
false ;
isMatchDir ( [ SrcDir | _ ReplaceDirs ] , SrcDir ) - >
true ;
isMatchDir ( [ OneDir | ReplaceDirs ] , SrcDir ) - >
case re : run ( SrcDir , OneDir ) of
nomatch - > isMatchDir ( ReplaceDirs , SrcDir ) ;
_ - > true
end .
modLastmod ( Mod ) - >
case code : which ( Mod ) of
Beam when is_list ( Beam ) - >
filelib : last_modified ( Beam ) ;
_ Other - >
0 % % non_existing | cover_compiled | preloaded
end .
getOptions ( SrcDir ) - >
case erlang : get ( SrcDir ) of
undefined - >
@ -749,11 +668,8 @@ setOptions(SrcDir, Options) ->
erlang : put ( SrcDir , NewOptions )
end .
startup ( ) - >
io : format ( " Growl notifications disabled ~n " ) .
loadCfg ( ) - >
KVs = [ { Key , esUtils : getEnv ( Key , DefVal ) } | | { Key , DefVal } < - ? CfgList ] ,
esUtils : load ( ? esCfgSync , KVs ) .
% % * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * misc fun end * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
% % * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * PRIVATE FUNCTIONS end * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *