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(pathes,[
6 set_search_pathes/0, % install run-time search pathes
7 runtime_application_path/1
8 % note: also interesting: get_prob_application_type/1 in preferences
9 ]).
10
11 :- use_module(module_information).
12
13 :- module_info(group,infrastructure).
14 :- module_info(description,'This module contains code to compute compile-time and runtime-pathes.').
15
16 :- use_module(library(system),[environ/2]).
17 :- use_module(library(file_systems),[file_exists/1,
18 current_directory/1]).
19
20 :- set_prolog_flag(double_quotes, codes).
21
22 :- multifile user:file_search_path/2.
23 :- dynamic user:file_search_path/2.
24 % portray_file_search_path :- user:file_search_path(A,B), B \= library(_), portray_clause(file_search_path(A,B)),fail.
25
26 % some pathes may be set by environment variables during compilation,
27 :- dynamic compile_time_env_path/2.
28 lookup_env_path(Pathname, Varname) :-
29 ( environ(Varname,Value) ->
30 print('Hard-wired path for alias '),print(Pathname),print(': '),print(Value),nl,
31 assertz(compile_time_env_path(Pathname,Value))
32 ;
33 true).
34 % compile-time pathes
35 :- lookup_env_path(prob_comp_home,'PROB_COMP_HOME'). % hard-wired version of the runtime_application_path
36 % This is used on systems where it absolutely clear where the
37 % ProB's application directory will be, namely Debian/Ubuntu packages
38 % run-time pathes
39 :- lookup_env_path(prob_home,'PROB_HOME').
40 :- lookup_env_path(examples,'PROB_EXAMPLES'). % hard-wired pathes, see above (about Debian/Ubuntu packages)
41 :- lookup_env_path(prob_lib,'PROB_LIB'). % hard-wired pathes, see above (about Debian/Ubuntu packages)
42 :- lookup_env_path(prob_tcl,'PROB_TCL'). % hard-wired pathes, see above (about Debian/Ubuntu packages)
43
44 % removes an (optional) trailing /src or /tests directory from the path,
45 % the result will not end with a slash
46 remove_src_dir(Orig,Dir) :-
47 atom_codes(Orig,COrig),
48 ( append(CDir,"/src/",COrig) -> true
49 ; append(CDir,"/src",COrig) -> true
50 ; append(CDir,"/src/proz",COrig) -> true
51 ; append(CDir,"/src/proz/",COrig) -> true
52 ; append(CDir,"/tests/",COrig) -> true
53 ; append(CDir,"/tests",COrig) -> true
54 ; append(CDir,"/",COrig) -> true
55 ; COrig=CDir),
56 atom_codes(Dir,CDir).
57
58 % returns the path to the application directory (at run-time)
59 runtime_application_path(Dir) :-
60 ( environ('PROB_HOME',Dir) -> % the user has set the environment variable PROB_HOME and thus overrides any other setting
61 true % the Rodin plugin used this mechanism
62 ? ; application_path2(Dir),is_correct_prob_home_path(Dir) ->
63 true
64 ; application_path2(D),absolute_file_name(D,D1),parent_dir(D1,Dir,_),
65 is_correct_prob_home_path(Dir) ->
66 true
67 ; compile_time_env_path(prob_comp_home,Dir) -> % e.g. Debian/Ubuntu systems: the path was hard-wired at compile-time
68 true
69 ; % usually a run from source code, where we the current directory is prob/src
70 current_directory(Current), remove_src_dir(Current,Dir)).
71 application_path2(Dir) :-
72 current_prolog_flag(system_type,development), % other value of system_type flag is runtime
73 % try and find location of src/module_information.pl
74 current_module(module_information,MFile),parent_dir(MFile,SrcDir,_),
75 remove_src_dir(SrcDir,Dir).
76 application_path2(Dir) :-
77 \+(current_prolog_flag(system_type,development)),
78 environ('SP_APP_DIR',Dir). /* /usr/local/bin/sicstus on development systems */
79 application_path2(Dir) :-
80 \+(current_prolog_flag(system_type,development)),
81 environ('SP_STARTUP_DIR',Dir).
82
83 % check if the path points to a directory with a lib subdirectory and the probcliparser.jar file
84 is_correct_prob_home_path(D) :- % print(trying_as_prob_home_path(D)),nl,nl,
85 (atom_concat(D,'/lib/probcliparser.jar',LibFile)
86 ; % Note: ProB for Rodin no longer contains a probcliparser.jar file, try this:
87 atom_concat(D,'/stdlib/LibraryProB.def',LibFile)
88 ),
89 file_exists(LibFile).
90
91 parent_dir(FullFile,Dir,Basename) :-
92 atom_chars(FullFile,CFullFile), % "path/to/file"
93 reverse(CFullFile,RCFullFile), % "elif/ot/htap"
94 append(RCFile,['/'|RCDir],RCFullFile),!, % "elif" "/ot/htap"
95 reverse(RCFile,CFile),reverse(['/'|RCDir],CDir), % "file" "path/to/"
96 atom_chars(Basename,CFile),atom_chars(Dir,CDir).
97
98 % avoid importing library lists for plspec; for some reason this generates errors
99 % :- use_module(library(lists),[reverse/2]).
100 reverse(List, Reversed) :-
101 rev(List, [], Reversed).
102 rev([], Reversed, Reversed).
103 rev([Head|Tail], Sofar, Reversed) :-
104 rev(Tail, [Head|Sofar], Reversed).
105
106 % we do not use our standard test case schema here because we do not
107 % want to introduce a dependency to the test framework at this level
108 parent_dir_test1 :-
109 parent_dir('/aaaa/bbb/cc/d.app',D,B),D=='/aaaa/bbb/cc/',B=='d.app'.
110 parent_dir_test2 :-
111 parent_dir('/aaaa/bbb/cc/',D,B),D=='/aaaa/bbb/cc/',B==''.
112 parent_dir_test :-
113 parent_dir_test1, parent_dir_test2.
114 :- ( parent_dir_test -> true
115 ; write(user_error,'testcase for pathes:parent_dir/2 failed, aborted.\n'),
116 halt(1)).
117
118 % returns the path to the application directory (compile-time)
119 compiletime_application_path(Dir) :-
120 compile_time_env_path(prob_comp_home,Dir),!.
121 compiletime_application_path(Dir) :-
122 runtime_application_path(Dir).
123
124 check_if_hard_wired(Alias, _Prefix, _Dir, Full) :-
125 compile_time_env_path(Alias,Full),!.
126 check_if_hard_wired(_Alias, Prefix, Dir, Full) :-
127 atom_concat(Prefix,Dir,Full).
128
129 set_search_path(Alias, Prefix, Dir) :-
130 check_if_hard_wired(Alias, Prefix, Dir, Full),
131 %% print(setting_path(Alias,Full)),nl, %%
132 ( catch( user:file_search_path(Alias,Full), _, fail)
133 -> true % Alias already set to same value
134 ; catch( retractall(user:file_search_path(Alias,_)), _, true), % remove any old defintion
135 assertz(user:file_search_path(Alias,Full)) % , print(setting_search_path(Alias,Full)),nl
136 ).
137
138 % pathes that can be used by use_module(...), but
139 % must not be used in run-time code
140 set_compile_time_search_pathes :-
141 compiletime_application_path(App),
142 %format('~n ProB compile time application path: ~w~n~n',[App]),
143 set_search_path(extension, App, '/extensions'),
144 set_search_path(probsrc, App, '/src'),
145 set_search_path(chrsrc, App, '/src/chr'),
146 set_search_path(cbcsrc, App, '/src/cbc'),
147 set_search_path(probcspsrc, App, '/src/cia'),
148 set_search_path(probltlsrc, App, '/src/ltl'),
149 set_search_path(probporsrc, App, '/src/por'),
150 %set_search_path(probpgesrc, App, '/src/pge'),
151 set_search_path(prozsrc, App, '/src/proz'),
152 set_search_path(kodkodsrc, App, '/src/kodkod'),
153 set_search_path(symbolic_model_checker, App, '/src/symbolic_model_checker'),
154 set_search_path(wdsrc, App, '/src/well_def'),
155 %set_search_path(plugins, App, '/plugins'),
156 %set_search_path(abstract_domains, App, '/plugins/absint/domains'),
157 set_search_path(tclsrc, App, '/tcl'),
158 %set_search_path(probpromela,App,'/src/promela'),
159 set_search_path(pltablesrc, App, '/src/pltables'),
160 set_search_path(prob_rewrite_rules, App, '/src/rewrite_rules'),
161 set_search_path(smtlib_solver, App, '/src/smtlib_solver'),
162 set_search_path(smt_solvers_interface, App, '/src/smt_solvers_interface'),
163 set_search_path(disproversrc, App, '/src/disprover'),
164 set_search_path(cdclt_solver, App, '/src/cdclt_solver'),
165 set_search_path(code2vec, App, '/src/code2vec'),
166 set_search_path(synthesis, App, '/src/synthesis'),
167 set_search_path(visbsrc, App, '/src/visb'),
168 set_search_path(dotsrc, App, '/src/dot'),
169 set_search_path(rulesdslsrc, App, '/src/rules_dsl'),
170 set_search_path(seqproversrc, App, '/src/sequent_prover'),
171 set_search_path(covsrc, App, '/src/coverage'),
172 set_search_path(tcltkuisrc, App, '/src/tcltk'),
173 set_search_path(extrasrc, App, '/src/extra'),
174 set_search_path(symsrc, App, '/src/symmetry'),
175 set_search_path(library_plspec, App, '/extensions/plspec_libraries'). % patched libraries for plspec
176 :- set_compile_time_search_pathes.
177
178 :- if(environ(prob_release,true)).
179 :- else.
180 % These paths will not be defined in release mode:
181 set_compile_time_dev_search_pathes :-
182 compiletime_application_path(App),
183 set_search_path(testsrc, App, '/tests'). % should never be used from main sources!
184 :- set_compile_time_dev_search_pathes.
185 :- endif.
186
187 :- use_module(extension('dll_path/dll_path'), [set_dll_directory/1]).
188
189 :- dynamic user:library_directory/1.
190
191 % pathes that can be used in run-time code; should not be called at compile time
192 set_search_pathes :-
193 runtime_application_path(App),
194 %print(runtime_application_path(App)),nl,
195 set_search_path(prob_home, App, ''),
196 set_search_path(examples, App, '/../probprivate/public_examples'),
197 set_search_path(prob_lib, App, '/lib'),
198 set_search_path(prob_tcl, App, '/tcl'),
199
200 check_if_hard_wired(prob_lib, App, '/lib', LibDir),
201 ( catch(user:library_directory(LibDir), _, fail) -> true % ,print(already_set(LibDir)),nl
202 ;
203 catch( retractall(user:library_directory(_)), _, true), % remove any old defintion
204 assertz(user:library_directory(LibDir))),
205 (current_prolog_flag(system_type,development)
206 -> true
207 ; set_compiled_prob_pathes(App)
208 ),
209 % Add the lib directory to the Windows DLL search path.
210 set_dll_directory(LibDir).
211
212 set_compiled_prob_pathes(_App) :-
213 % print('Adding to library directories:'),nl,
214 % assertz(user:library_directory('/usr/local/sicstus4.6/bin/sp-4.6.0/sicstus-4.6.0/library/')).
215 true.
216
217
218 % Term expansion for automatically replacing uses of library(avl)
219 % with our custom SICStus-like implementation avl_custom(avl).
220 % Mainly for compatibility with SWI-Prolog,
221 % because ProB heavily relies on SICStus library(avl) and its term representation.
222 :- use_module(tools_portability, [exists_source/1, supported_term_expansion_style/1]).
223 :- if((environ(prob_avl_custom,true) ; \+ exists_source(library(avl)))).
224
225 expand_avl_custom((:- use_module(library(avl))), Layout1, Term2, Layout2) :-
226 !,
227 Term2 = (:- use_module(probsrc(avl_custom))),
228 Layout2 = Layout1.
229 expand_avl_custom((:- use_module(library(avl), Imports)), Layout1, Term2, Layout2) :-
230 !,
231 Term2 = (:- use_module(probsrc(avl_custom), Imports)),
232 Layout2 = Layout1.
233
234 :- if(supported_term_expansion_style(sicstus)).
235 :- multifile user:term_expansion/6.
236 user:term_expansion(Term1, Layout1, Ids, Term2, Layout2, [patch_avl_custom|Ids]) :-
237 nonvar(Term1), nonmember(patch_avl_custom,Ids),
238 expand_avl_custom(Term1, Layout1, Term2, Layout2).
239 :- elif(supported_term_expansion_style(swi_layout)).
240 :- multifile user:term_expansion/4.
241 user:term_expansion(Term1, Layout1, Term2, Layout2) :-
242 nonvar(Term1),
243 expand_avl_custom(Term1, Layout1, Term2, Layout2).
244 :- else.
245 :- multifile user:term_expansion/2.
246 user:term_expansion(Term1, Term2) :-
247 nonvar(Term1),
248 expand_avl_custom(Term1, [], Term2, _).
249 :- endif.
250
251 :- endif.
252
253
254 % TERM EXPANDER for ProB EXTENSIONS
255 % ---------------------------------
256
257 :- if(environ(prob_core_only,true)).
258
259 % keep track of which optional extensions of ProB are available
260 % and patch use_modules to unloaded extensions
261 % Note: only use_modules with explicit import lists are patched !
262
263 :- use_module(pathes_extensions_db, [compile_time_unavailable_extension/2,
264 module_path_implements_extension/3]).
265
266 % true if a given path should be pruned by the term expander:
267 prune_use_module(Path,Extension,Reason) :-
268 module_path_implements_extension(Path,_,Extension),
269 compile_time_unavailable_extension(Extension,Reason).
270
271 % utility to export dependencies to a dot graph
272 ext_dot_graph(F) :- open(F,write,S,[encoding(utf8)]),
273 format(S,'digraph ~w {~n',[extensions]),
274 format(S,'graph [nodesep=1.5, ranksep=1.5, rankdir=LR];~n',[]),
275 ( depends_edge(From,To,Label,Color),
276 format(S,' ~w -> ~w [label="~w",color="~w"]~n',[From,To,Label,Color]),
277 fail ; true
278 ),
279 format(S,'}~n',[]),
280 close(S).
281 % ?- pathes:ext_dot_graph('~/Desktop/out.dot').
282 % dot -Tpdf <~/Desktop/out.dot > ~/Desktop/out.pdf ; open ~/Desktop/out.pdf
283 :- dynamic module_extension/2, dependency/6.
284 depends_edge(FromExtension,ToExtension,Label,Col) :-
285 pathes_extensions_db:prob_extension(FromExtension),
286 pathes_extensions_db:prob_extension(ToExtension), ToExtension \= FromExtension,
287 (dependency(FromExtension,_CurModule,_,_,ToExtension,Av) ->
288 %Label = CurModule,
289 findall(MM,dependency(FromExtension,MM,_,_,ToExtension,Av),Ls), sort(Ls,SLs),
290 tools:ajoin_with_sep(SLs,',',Label),
291 (Av=available -> Col=green ; Col=red)).
292 % utility to generate a dot graph of dependency; TODO: make useful
293
294 :- if(1=2). % change to 1=1 to store dependencies and e.g. call print_modules
295 store_dependency(Path) :-
296 module_path_implements_extension(Path,Module,ToExtension),
297 % store the extension associated to the module:
298 (module_extension(Module,ToExtension) -> true
299 ; %format(' ~w --> ~w~n',[Module,ToExtension]),
300 assertz(module_extension(Module,ToExtension))
301 ),
302 % examine context of current module performing the import:
303 (prolog_load_context(module,CurModule)
304 -> (get_module_info(CurModule,group,Group) -> true ; Group=unknown),
305 (module_extension(CurModule,FromExtension) -> true ; FromExtension=core)
306 ; CurModule=user,Group=user),
307 !,
308 (compile_time_unavailable_extension(ToExtension, _) -> Av=not_available ; Av=available),
309 assertz(dependency(FromExtension,CurModule,Group,Path,ToExtension,Av)).
310 :- endif.
311 store_dependency(_).
312
313 %:- use_module(probsrc(tools),[latex_escape_atom/2]).
314 print_modules :-
315 version:version_str(Vers), version:revision(Rev),
316 format('List of ProB ~w (~w) modules by groups and extensions:~n',[Vers,Rev]),
317 format('\\begin{itemize}~n',[]),
318 get_module_group(Group), tools:latex_escape_atom(Group,LG),
319 format(' \\item Module Group: ~w~n',[LG]),
320 format(' \\begin{enumerate}~n',[]),
321 ( findall(MM, get_module_info(MM,group,Group), ML), sort(ML,SML),
322 member(Module,SML),
323 (get_module_info(Module,description,D) -> true ; D='?'),
324 (module_extension(Module,Ext) -> true ; Ext=core),
325 tools:latex_escape_atom(Ext,EE),tools:latex_escape_atom(Module,EM),
326 tools:latex_escape_atom(D,ED),
327 format(' \\item ~w : ~w ~n \\moduledescr{ ~w }~n',[EE,EM,ED])
328 ;
329 format(' \\end{enumerate}~n',[])
330 ),
331 fail.
332 print_modules :- format('\\end{itemize}~n',[]).
333 print_modules_csv :- nl,
334 get_module_group(Group),
335 findall(MM, get_module_info(MM,group,Group), ML), sort(ML,SML),
336 member(Module,SML),
337 (get_module_info(Module,description,D) -> true ; D='?'),
338 (module_extension(Module,Ext) -> true ; Ext=core),
339 format(' ~w,~w,~w,"~w"~n',[Group,Ext,Module,D]),
340 fail.
341 print_modules_csv :- nl.
342
343
344 :- use_module(library(lists),[maplist/3]).
345
346 % term expander to replace use_modules by stub clauses which throw errors
347 %patch_ext_use_modules((:- module(Module,_)),Layout1,Term2,Layout2) :- format('~n enter module ~w~n',[Module]),fail.
348 patch_ext_use_modules((:- use_module(Path)),Layout1,Term2,Layout2) :-
349 store_dependency(Path),
350 prune_use_module(Path,_Extension,_Reason),
351 !,
352 Layout2 = Layout1,
353 %%(prolog_load_context(module,CurModule) -> true ; CurModule=user),
354 %%format('\e[31mIgnoring use_module(~w) in ~w because extension ~w not available\e[0m~n',[Path,CurModule,Extension]),
355 Term2= (:- true).
356 patch_ext_use_modules((:- use_module(Path,Preds)),Layout1,Term2,Layout2) :-
357 store_dependency(Path),
358 prune_use_module(Path,Extension,Reason), % prolog_load_context(file,File),
359 !,
360 Layout2 = Layout1,
361 (prolog_load_context(module,CurModule) -> true ; CurModule=user),
362 %%format('\e[34mPatching use_module(~w,~w) in ~w because extension ~w not available\e[0m~n',[Path,Preds,CurModule,Extension]),
363 maplist(generate_stub(CurModule,Extension,Reason),Preds,Term2). % Term2 can be a list of clauses
364 %patch_ext_use_modules((:- use_module(Path)),_,_,_) :- format('** use_module(~w)~n',[Path]),fail.
365 %patch_ext_use_modules((:- use_module(Path,Preds)),_,_,_) :- format('** use_module(~w,~w)~n',[Path,Preds]),fail.
366
367
368 :- use_module(pathes_extensions_db, [silently_replace_unavailable_predicate/4]).
369 :- use_module(tools_strings,[ajoin/2]).
370 % generate stub clauses for all imported predicates
371 generate_stub(_,Extension,_Reason,Predicate/Arity,Clause) :-
372 silently_replace_unavailable_predicate(Predicate,Arity,Extension,BodyCode),!,
373 functor(Head,Predicate,Arity),
374 Clause = (Head :- BodyCode).
375 generate_stub(Module,Extension,Reason,Predicate/Arity,Clause) :-
376 functor(Head,Predicate,Arity),
377 ajoin(['Predicate cannot be called because extension ', Extension, ' is not loaded (', Reason, '):'],Msg),
378 Clause = (Head :- !, error_manager:add_error(Module,Msg,Predicate/Arity),fail).
379 %, portray_clause(Clause),nl.
380
381 :- use_module(tools_portability, [supported_term_expansion_style/1]).
382 :- if(supported_term_expansion_style(sicstus)).
383 :- multifile user:term_expansion/6.
384 user:term_expansion(Term1, Layout1, Ids, Term2, Layout2, [patch_unavailable_extensions|Ids]) :-
385 nonvar(Term1), nonmember(patch_unavailable_extensions,Ids),
386 patch_ext_use_modules(Term1,Layout1,Term2,Layout2).
387 :- elif(supported_term_expansion_style(swi_layout)).
388 :- multifile user:term_expansion/4.
389 user:term_expansion(Term1, Layout1, Term2, Layout2) :-
390 nonvar(Term1),
391 patch_ext_use_modules(Term1,Layout1,Term2,Layout2).
392 :- else.
393 :- multifile user:term_expansion/2.
394 user:term_expansion(Term1, Term2) :-
395 nonvar(Term1),
396 patch_ext_use_modules(Term1,[],Term2,_).
397 :- endif.
398 :- endif.