:- lib(stoics_lib:mod_goal/3). :- lib(stoics_lib:en_list/3). :- lib(stoics_lib:io_streams/3). os_mill_defaults( Args, Defs ) :- ( memberchk(goal(MGoal),Args) -> mod_goal( _Mod, Goal, MGoal ), functor( Goal, Psfx, _ ) ; throw( internal_error(os_mill_no_goal) ) ), Defs = [ recreate(false), separator('_'), type(file), call_options([]), milled(_), dir(''), homonymous(false), call_module(user), not_created(error), postfix(Psfx), on_exists(true), outputs_to(user), outputs_as_tty(true) ]. /** os_mill( +FromOs, +Goal, ?Milled, +Opts ). Generate or recreate Milled from FromOs by calling Goal. If Milled is not given its name is generated by applying os_postfix/3 on FromOs with SepPsf as the postfix. Milled is usually a file, but it can also be a directory. By default, Goal is trusted to generate the correct format. If FromOs is of the form @(Callable), then =|call(Callable,Obj)|= is called to establish the location of OsObj which is used in place of FromOs. As of 0.3, when =|Type=dir|= and =|OutsTo|= is a filename, Milled is created as to house the file. Goal, should be aware of this- as it might be attempting to create it too. The Goal is called as call( Goal, RelFromOs, Milled, Co ). Opts * call_options(Co=[]) the argument is passed as the last argument to Goal. (Set to false for no options argument on the call.) * call_module(Mod=user) at which module to call Goal * debug(Dbg) whether to print debugs (see options_append/4) (default does nothing) * dir(Dir) directory for both Os and Milled (no default) * ext(Ext) if given, change the extension in Milled (ignored for dirs), when Milled is ground the extension is added * homonymous(Homon=false) use Goal's term name as the stem * milled(MillOs) returns the full milled OS name (file or directory) * not_created(Created=error) check Os was created at exit; if not take action: true, error, fail, debug * odir(ODir) directory for output (takes precendence over Dir) * on_exists(OnX=true) callable that is qurried if Milled already exists, eg to load an R object from a saved file called as call(OnX,Milled), if OnX has a module, then it is used as is, else Mod is : prepended. * outputs_to(OutsTo=user) if a different filename is given, it is used for redirecting both user_output and user_error. When =|Type=dir\= OutsTo is assumed to be a file within (Milled, is created in this case). * outputs_as_tty(OutsTty=true), only relevant when OutsTo is a filename. =|OutsTty=true|= makes the file a tty output (stream_property/2). * postfix(Psf=f(File) ) postfix for the new file name (see os_postfix/3) * recreate(R=false) whether to recreate OsEntry * separator(Sep='_') separator for Psf * type(Type=file) or dir for directory The predicate uses os_dir_stem_ext/2 to construct OS, so its options can be used in addition to the above. The default postfix (P) is taken to be the predicate name of Goal, minus a possible 'file_' prefix. == ?- assert( (true(A,B,C) :- write(args(A,B,C)), nl) ). ?- os_mill( abc.txt, true, Outf, [] ). args(abc.txt,abc_true.txt,[]) ERROR: os:os_mill/4: OS milled: abc_true.txt was not created (source was: abc.txt) ?- os_mill( abc.txt, true, Outf, not_created(fail) ). args(abc.txt,abc_true.txt,[]) false. ?- use_module(library(debug)). true. ?- [user]. |: go_from_here_to_there( Here, There, HTopts ) :- |: debug( testo, 'Here: ~w', [Here] ), |: debug( testo, 'There: ~w', [There] ), |: debug( testo, 'TheOpts: ~w', [HTopts] ). |: ^D% user://1 compiled 0.01 sec, 1 clauses true. ?- debug(testo). true. ?- Milts = [outputs_to('debug_outs.txt'),type(dir),debug(true)], os_mill( here, go_from_here_to_there, ex_os_milled, Milts ), write( milled(ex_os_milled) ), nl. % Creating non-existing mill entity: ex_os_milled, from: here % Calling os_mill: user:go_from_here_to_there/0 % Opened:'ex_os_milled/debug_outs.txt', at:(0x558dbb1d98c0) % Changing channels to: io_streams(user_input,(0x558dbb1d98c0),(0x558dbb1d98c0)) % Closing: (0x558dbb1d98c0) % Run output at: 'ex_os_milled/debug_outs.txt' milled(ex_os_milled) Milts = [outputs_to('debug_outs.txt'), type(dir), debug(true)]. ?- shell( 'cat ex_os_milled/debug_outs.txt'). % Calling: call(user:go_from_here_to_there,here,ex_os_milled,[]) % Here: here % There: ex_os_milled % TheOpts: [] % Caught: exit % Reverting streams to: io_streams((0x7f4311820780),(0x7f4311820880),(0x7f4311820980)) true. ?- == @author nicos angelopoulos @version 0.1 2014/10/15 @version 0.2 2016/06/28 @version 0.3 2020/09/16, added outputs_to() & outputs_as_tty(), with example @tbd double check non atomic File and Milled */ os_mill( InFile, Goal, Milled, Args ) :- ( (\+ var(InFile),InFile = @(FileGoal)) -> call(FileGoal,File); File = InFile ), en_list( Args, Argos, os_mill/4 ), options_append( os_mill, [goal(Goal)|Argos], Opts, process(debug) ), options( recreate(Rcr), Opts ), options( type(Type), Opts ), options( homonymous(Omon), Opts ), ( var(Milled) -> true; Nilled = Milled ), os_mill_destination( Omon, Nilled, Goal, File, DMilled, Opts ), options( dir(Dir), Opts ), os_path( Dir, File, DirFile ), os_name( File, Ftype ), os_cast( Ftype, DirFile, RelFromOs ), memberchk( milled(DMilled), Opts ), ( var(Milled) -> Milled = DMilled; true ), os_mill( Rcr, RelFromOs, Type, Goal, Milled, Opts ). /** os_mill_destination( +Homon, +Goal, +SrcFile, -Milled, -DMilled ). Create Milled and dirpended Milled (DMilled) from either manipulating SrcFile or by taking the term name from Goal, depending on Homon Boolean value (true: is homonymous to Goal). == ?- os_lib:os_mill_destination( false, Milled, true, abc.txt, DMill, [postfix(post)] ). Milled = DMill, DMill = abc_post.txt. ?- os_lib:os_mill_destination( false, Milled, true, abc.txt, DMill, [postfix(post),dir(sub)] ). Milled = DMill, DMill = 'sub/abc_post.txt'. ?- os_lib:os_mill_destination( true, xyz.tab, true, abc.txt, DMill, [postfix(post),dir(sub)] ). DMill = 'sub/xyz.tab'. ?- os_lib:os_mill_destination( true, Milled, true, abc.txt, DMill, [postfix(post),dir(sub)] ). Milled = true, DMill = 'sub/true'. == */ os_mill_destination( false, Milled, _Goal, File, DMilled, Opts ) :- os_ext( Ext, _, File, os_mill/4 ), append( [from(File)|Opts], [ext(Ext)], StemOpts ), % os_stem( Stem, Milled, [from(File)|Opts] ), ( var(Milled) -> options( postfix(Psfx), Opts ), os_postfix( Psfx, File, Posted ), os_stem( Stem, Posted, Opts ) ; os_stem( Stem, Milled, StemOpts ) ), append( [stem(Stem)|Opts], [ext(Ext)], DSEopts ), os_dir_stem_ext( DMilled, DSEopts ). os_mill_destination( true, Milled, Goal, File, DMilled, Opts ) :- Self = os_mill/4, ( var(Milled) -> mod_goal( _Mod, Call, Goal, os_lib:os_mill/4 ), functor(Call,Gname,_), ( memberchk(ext(Ext),Opts) -> os_ext(Ext,Gname,Milled,Self) ; Milled = Gname ) ; os_stem( Stem, Milled, [from(File)|Opts] ) ), os_ext( Ext, Stem, Milled, Self ), os_dir_stem_ext( DMilled, [stem(Stem),ext(Ext)|Opts] ). os_mill( false, File, Type, _Goal, Milled, Opts ) :- exists_milled( Type, Milled ), !, Using = 'Using existing milled ~w: ~w, apparently from: ~w ', debug( os_mill, Using, [Type,Milled,File] ), options( on_exists(OnX), Opts ), os_mill_exists( OnX, Milled, Opts ). os_mill( false, File, Type, Goal, Milled, Opts ) :- \+ exists_milled( Type, Milled ), Create = 'Creating non-existing mill entity: ~w, from: ~w', debug( os_mill, Create, [Milled,File] ), os_mill_call( File, Type, Goal, Milled, Opts ). os_mill( true, File, Type, Goal, Milled, Opts ) :- exists_milled( Type, Milled ), !, Create = 'Recreating mill ~w: ~w, from: ~w', debug( os_mill, Create, [Type,Milled,File] ), os_mill_delete_type( Type, Milled ), os_mill_call( File, Type, Goal, Milled, Opts ). os_mill( true, File, Type, Goal, Milled, Opts ) :- \+ exists_milled( Type, Milled ), Create = 'Creating non-existing mill entity: ~w, from: ~w', debug( os_mill, Create, [Milled,File] ), os_mill_call( File, Type, Goal, Milled, Opts ). os_mill_exists( true, _Milled, _Opts ) :- !. os_mill_exists( OnX, Milled, Opts ) :- os_mill_callable( OnX, Goal, Opts ), Calling = 'Calling on_exists option: ~w, on existing millled file ~w', debug( os_mill, Calling, [Goal,Milled] ), call( Goal, Milled ). os_mill_delete_type( file, Milled ) :- os_rm( Milled ). os_mill_delete_type( dir, Milled ) :- delete_directory_and_contents( Milled ). % this was old style can remove now that we use os_exists/2 exists_milled( file, Milled ) :- os_exists( Milled, type(flink) ), !. exists_milled( dir, Milled ) :- os_exists( Milled, dlink ). os_mill_call( File, Type, Goal, Milled, Opts ) :- options( call_options(CoPrv), Opts ), ( CoPrv == false -> CoInc = false; CoInc = true ), en_list( CoPrv, Copts ), os_mill_callable( Goal, Callable, Opts ), Callable = CallMod:Pid, functor( Pid, Patom, Parity ), debug( os_mill, 'Calling os_mill: ~w', [CallMod:Patom/Parity] ), options( outputs_to(OutsTo), Opts ), options( outputs_as_tty(OutTty), Opts ), os_mill_call_outputs( OutsTo, OutTty, CoInc, Callable, File, Type, Milled, Copts ), % os_mill_call_opts( CoInc, Callable, File, Milled, Copts ), holds( os_lib:os_exists(Milled), Exists ), options( not_created(Created), Opts ), os_mill_created( Exists, Created, Milled, File, Opts ). os_mill_call_outputs( user, _OutTty, CoInc, Callable, File, _Type, Milled, Copts ) :- !, os_mill_call_opts( CoInc, Callable, File, Milled, Copts ). os_mill_call_outputs( OutsFile, OutTty, CoInc, Callable, FromOs, Type, Milled, Copts ) :- ( Type == dir -> make_directory( Milled ), os_path( Milled, OutsFile, OutsDst ) ; OutsDst = OutsFile ), io_streams( In, Ou, Er ), setup_call_catcher_cleanup( ( open(OutsDst,write,MessAt), debug(os_mill,'Opened:~p, at:~w',[OutsDst,MessAt]), os_mill_set_stream(OutTty,MessAt), debug(os_mill,'Changing channels to: ~w', io_streams(user_input,MessAt,MessAt)), io_streams(user_input,MessAt,MessAt) ), os_mill_call_opts( CoInc, Callable, FromOs, Milled, Copts ), Ball, ( debug(os_mill, 'Caught: ~w', [Ball]), debug(os_mill, 'Reverting streams to: ~w', io_streams(In,Ou,Er)), io_streams( In, Ou, Er ), debug(os_mill, 'Closing: ~w', [MessAt]), close(MessAt) ) ), debug( os_mill, 'Run output at: ~p', OutsDst ). os_mill_set_stream( true, MessAt ) :- !, set_stream( MessAt, tty(true) ). % defaulty os_mill_set_stream( _, _MessAt ). os_mill_callable( Goal, Callable, Opts ) :- options( call_module(Mod), Opts ), ( Goal = _:_ -> Callable = Goal; Callable = Mod:Goal ). os_mill_call_opts( true, Callable, File, Milled, Copts ) :- debug( os_mill, 'Calling: ~w', [call(Callable,File,Milled,Copts)] ), call( Callable, File, Milled, Copts ). os_mill_call_opts( false, Callable, File, Milled, _Copts ) :- debug( os_mill, 'Calling: ~w', [call(Callable,File,Milled)] ), call( Callable, File, Milled ). os_mill_created( true, _Created, _Milled, _File, _Opts ). os_mill_created( false, Created, Milled, File, _Opts ) :- os_mill_created_not( Created, Milled, File ), !. os_mill_created( false, Created, _Milled, _File, _Opts ) :- Created \== fail, Mismatch = opt_mismatch(created,[false,error,fail,debug]), throw( pack_error(os,os_mill/4,Mismatch) ). os_mill_created_not( true, _Milled, _File ). % take no action (true = succeed) os_mill_created_not( error, Milled, File ) :- throw( pack_error(os,os_mill/4,os_created_not(Milled,File)) ). os_mill_created_not( fail, _Milled, _File ) :- fail. os_mill_created_not( debug, Milled, File ) :- debug( os_mill, 'Os milled was not created: ~p, (from: ~p)', [Milled,File] ).