1 % (c) 2009-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(system_call,[system_call/4, system_call_with_options/5,
6 system_call/5, system_call_with_options/6,
7 system_call_keep_open/7,
8 system_call_keep_open_no_stdin/6,
9 system_call_keep_open_no_pipes/4,
10 get_writable_compiled_filename/3,
11 get_writable_compiled_filename/4, generate_temporary_cspm_file/2,
12 get_temporary_filename/2,
13 get_command_path/2,
14 safe_process_create/3,
15 safe_process_create_with_diagnostics/4,
16 system_call_with_diagnostics/5, system_call_with_diagnostics/6,
17 system_call_failure_diagnostics/2]).
18 :- use_module(module_information).
19 :- module_info(group,tools).
20 :- module_info(description,'Tools for calling underlying system procedures or binaries (java, dot, krt,...).').
21
22 :- use_module(library(process)).
23 :- use_module(library(file_systems)).
24 :- use_module(error_manager).
25
26 :- use_module(tools,[ajoin/2]).
27 :- use_module(tools_meta,[safe_on_exception/3]).
28 :- use_module(preferences,[get_preference/2, eclipse_preference/2, path_preference_for_command/2]).
29
30 safe_process_create(Command,Args,ProcessCreateOptions) :-
31 safe_on_exception(E,
32 process:process_create(Command, Args, ProcessCreateOptions),
33 ( ajoin(['Could not execute command "',Command,'" due to exception: '],Msg),
34 add_error(system_call,Msg,E),
35 fail)).
36 % for existence error we could check if Command has .exe extension on Windows
37
38
39 % a version where we do not return a stream for stdin
40 system_call_keep_open_no_stdin(Command,Args,Process,STDOut,STDErr,Env) :-
41 safe_process_create(Command,Args,
42 [process(Process),
43 stdout(pipe(STDOut,[encoding(utf8)])),
44 stderr(pipe(STDErr,[encoding(utf8)])),
45 environment(Env)]).
46
47 system_call_keep_open(Command,Args,Process,STDIn,STDOut,STDErr,Env) :-
48 safe_process_create(Command,Args,
49 [process(Process),
50 stdout(pipe(STDOut,[encoding(utf8)])),
51 stdin(pipe(STDIn,[encoding(utf8)])),
52 stderr(pipe(STDErr,[encoding(utf8)])),
53 environment(Env)]).
54
55 system_call_keep_open_no_pipes(Command,Args,Process,Env) :-
56 safe_process_create(Command, Args,
57 [process(Process),environment(Env)]).
58
59 system_call(Command,Args,ErrorTextAsCodeList,ExitCode) :-
60 system_call_with_options(Command,Args,[],ErrorTextAsCodeList,ExitCode).
61
62 system_call_with_options(Command,Args,Options,ErrorTextAsCodeList,ExitCode) :-
63 safe_process_create(Command, Args,
64 [process(Process),stderr(pipe(JStderr,[encoding(utf8)]))|Options]),
65
66 read_all(JStderr,Command,stderr,ErrorTextAsCodeList),
67 my_process_wait(Process,ExitCode).
68
69 system_call(Command,Args,OutputTextAsCodeList,ErrorTextAsCodeList,ExitCode) :-
70 system_call_with_options(Command,Args,[],OutputTextAsCodeList,ErrorTextAsCodeList,ExitCode).
71
72 system_call_with_options(Command,Args,Options,OutputTextAsCodeList,ErrorTextAsCodeList,ExitCode) :-
73 %StreamOptions = [encoding('UTF-8')], % we could use pipe(JStdout,StreamOptions), ... below
74 safe_process_create(Command, Args,
75 [process(Process),
76 stdout(pipe(JStdout,[encoding(utf8)])),stderr(pipe(JStderr,[encoding(utf8)]))|Options]),
77 read_all(JStdout,Command,stdout,OutputTextAsCodeList),
78 read_all(JStderr,Command,stderr,ErrorTextAsCodeList),
79 my_process_wait(Process,ExitCode).
80
81 my_process_wait(Process,ExitCode) :- (ExitCode == no_process_wait -> true ; process_wait(Process,ExitCode)).
82
83 get_command_path(CmdName,CmdPath) :-
84 ? absolute_file_name(path(CmdName),
85 CmdPath,
86 [access(exist),extensions(['.exe','']),solutions(all),file_errors(fail)]),!.
87 get_command_path(CmdName,_) :-
88 add_error_fail(get_command_path,'Could not get path to command: ',CmdName).
89
90
91 :- use_module(library(lists)).
92
93 % read all characters from a stream
94 read_all(S,Command,Pipe,Text) :-
95 call_cleanup(read_all1(S,Command,Pipe,Text),
96 close(S)).
97 read_all1(S,Command,Pipe,Text) :-
98 catch(read_all2(S,Lines), error(_,E), ( % E could be system_error('SPIO_E_ENCODING_INVALID')
99 ajoin(['Error reading ',Pipe,' for "',Command,'" due to exception: '],Msg),
100 add_error(system_call,Msg,E),
101 fail
102 )),
103 append(Lines,Text).
104 read_all2(S,Text) :-
105 read_line(S,Line),
106 ( Line==end_of_file ->
107 Text=[[]]
108 ;
109 Text = [Line, [0'\n] | Rest],
110 read_all2(S,Rest)).
111
112 :- use_module(tools,[get_tail_filename/2]).
113 generate_temporary_cspm_file(CSPFile,CSPFileNameTemp):-
114 get_writable_compiled_filename(CSPFile,'.tmp',CSPFileNameTemp).
115
116 :- use_module(debug,[debug_println/2]).
117 get_writable_compiled_filename(SourceFile,Extension,WritableFile) :-
118 get_writable_compiled_filename(SourceFile,Extension,WritableFile,_).
119 get_writable_compiled_filename(SourceFile,Extension,WritableFile,IsTemporaryFile) :-
120 atom_concat(SourceFile,Extension,WFile),
121 debug_println(9,generating_writable_file(SourceFile,Extension)),
122 (file_exists(WFile),
123 file_property(WFile,writable)
124 -> debug_println(9,file_writable(WFile)), IsTemporaryFile=false,
125 WritableFile=WFile
126 ; get_tail_filename(WFile,TFile),
127 safe_on_exception(_E,
128 (open(WFile,write,S1,[]),close(S1),WritableFile=WFile,
129 IsTemporaryFile=false),
130 (IsTemporaryFile=true,
131 get_temporary_filename(TFile,WritableFile)) )
132 ).
133
134 :- use_module(tools, [open_temp_file/3]).
135 get_temporary_filename(PreferredFileName,WritableFile) :-
136 debug_println(9,trying_to_get_temporary_filename(PreferredFileName)),
137 open_temp_file(PreferredFileName, WritableFile, S1),
138 debug_println(9,temporary_file(WritableFile)),
139 close(S1).
140
141
142
143 % call this when safe_process_create, system_call fails
144 system_call_failure_diagnostics(CommandName,_Command) :-
145 path_preference_for_command(CommandName,Pref),
146 get_preference(Pref,CurPrefValue),
147 (eclipse_preference(EPref,Pref) -> true ; EPref=Pref),
148 ajoin(['Be sure that the path preference ',EPref,' is set correctly, current value: '],Msg),
149 add_warning(system_call,Msg,CurPrefValue),
150 fail.
151 system_call_failure_diagnostics(CommandName,_Command) :-
152 command_requires_package(CommandName,Package,URL,ProBURL),
153 ajoin(['Be sure that ',Package,' (',URL,
154 ') is installed. More details can be found in the ProB Wiki: '],Msg),
155 add_warning(system_call,Msg,ProBURL),
156 fail.
157 % TOODO: check if there is a install_lib_component feature available in pathes_lib
158
159 command_requires_package(dot,'GraphViz','http://www.graphviz.org',
160 'https://prob.hhu.de/w/index.php?title=Download#Graphviz_Requirements').
161 command_requires_package(sfdp,Package,URL,ProBURL) :- command_requires_package(dot,Package,URL,ProBURL).
162 command_requires_package(java,'Java','https://www.java.com/',
163 'https://prob.hhu.de/w/index.php?title=Download#Java_Requirements_for_B_Parser').
164 command_requires_package(krt,'Atelier-B','http://www.atelierb.eu',
165 'https://prob.hhu.de/w/index.php?title=Using_ProB_with_Atelier_B').
166
167 safe_process_create_with_diagnostics(CommandName,CommandPath,Args,Options) :-
168 (safe_process_create(CommandPath,Args,Options) -> true
169 ; system_call_failure_diagnostics(CommandName,_Command)).
170 system_call_with_diagnostics(CommandName,CommandPath,AllArgs,Text,ErrText,JExit) :-
171 (system_call(CommandPath,AllArgs,Text,ErrText,JExit) -> true
172 ; system_call_failure_diagnostics(CommandName,_Command)).
173 system_call_with_diagnostics(CommandName,CommandPath,AllArgs,Text,JExit) :-
174 (system_call(CommandPath,AllArgs,Text,JExit) -> true
175 ; system_call_failure_diagnostics(CommandName,_Command)).