1 % (c) 2022-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_lib, [lib_component_full_path/4,
6 lib_component_available/6,
7 check_lib_component_available/1,
8 unavailable_extension/2,
9 available_extension/1,
10 check_lib_contents/2, check_lib_contents/3,
11 install_lib_component/2,
12 safe_load_foreign_resource/2]).
13
14 % TO DO: interface with lib_component_implements_extension/2
15
16
17 :- use_module(module_information,[module_info/2]).
18 :- module_info(group,typechecker).
19 :- module_info(description,'This module checks the contents of the lib folder and can install some missing components').
20
21 :- use_module(library(file_systems)).
22 :- use_module(library(system)).
23 :- use_module(pathes, [runtime_application_path/1]).
24 :- use_module(pathes_extensions_db, [compile_time_unavailable_extension/2,
25 prob_extension/1, lib_component_implements_extension/4, lib_file_suffix/3]).
26 :- use_module(tools_platform, [host_platform/1, host_processor/1]).
27 :- use_module(tools_printing,[format_with_colour_nl/4]).
28 :- use_module(error_manager).
29 :- use_module(preferences, [get_preference/2]).
30 :- use_module(probsrc(tools_strings),[ajoin/2, ajoin_with_sep/3]).
31 :- use_module(system_call).
32 :- use_module(debug,[debug_format/3]).
33
34 % The predicates in this module can only be called at runtime,
35 % because they check whether certain files are present in the lib directory.
36 % For compile-time checks,
37 % use compile_time_unavailable_extension/2 from the pathes_extensions_db module instead
38 % (possibly with an additional runtime check).
39
40 add_file_suffix(Name,Kind,FullFile) :-
41 host_platform(Platform),
42 lib_file_suffix(Kind,Platform,Suffix),
43 atom_concat(Name,Suffix,FullFile).
44
45 lib_component_full_path(executable,Name,ltsmin_extension,FullFile) :-
46 !,
47 get_preference(path_to_ltsmin,LTSMinRel),
48 runtime_application_path(ProBHome),
49 absolute_file_name(LTSMinRel,LTSMinAbs,[relative_to(ProBHome)]),
50 add_file_suffix(Name,executable,File),
51 absolute_file_name(File,FullFile,[relative_to(LTSMinAbs)]).
52 lib_component_full_path(Kind,Name,_,FullFile) :-
53 add_file_suffix(Name,Kind,File),
54 absolute_file_name(prob_lib(File),FullFile).
55
56 check_lib_component_available(Component) :-
57 lib_component_available(Component,_Kind,_Name,FullFile,_ProBExtension,Available),
58 (Available=true
59 -> add_message(Component,'File for component is available: ',FullFile)
60 ; add_warning(Component,'File for component does not exist: ',FullFile)
61 ).
62
63 lib_component_available(C,Kind,Name,FullFile,ProBExtension,Available) :-
64 ? lib_component_implements_extension(C,Kind,Name,ProBExtension),
65 lib_component_full_path(Kind,Name,ProBExtension,FullFile),
66 (file_exists(FullFile) -> Available=true ; Available=false).
67
68 unavailable_extension(E, Reason) :- compile_time_unavailable_extension(E, Reason), !.
69 unavailable_extension(E, Reason) :-
70 lib_component_available(_Component, _Kind, _Name, FullFile, E, false),
71 atom_concat('required file missing: ', FullFile, Reason).
72
73 available_extension(E) :-
74 prob_extension(E),
75 \+ unavailable_extension(E,_).
76
77 prob_lib_folder(F) :- absolute_file_name(prob_lib('/'),F).
78
79 check_lib_contents(Stream,Verbose) :-
80 check_lib_contents(Stream,_,Verbose).
81 check_lib_contents(Stream,Comp,Verbose) :-
82 prob_lib_folder(FF),
83 (Verbose=silent -> true ; format_with_colour_nl(Stream,[blue],'Checking contents of lib folder: ~w',[FF])),
84 lib_component_available(Comp,Kind,_Name,_FullFile,ProBExtension,Available),
85 (Available=true
86 -> (Verbose=verbose -> format_with_colour_nl(Stream,[green],'~w (~w) found',[Comp,Kind])
87 ; true
88 )
89 ; format_with_colour_nl(Stream,[red],'~w (~w) not found, ~w not available',[Comp,Kind,ProBExtension]),
90 format_with_colour_nl(Stream,[blue],' * to install component run: probcli --install ~w~n',[Comp])
91 ),
92 fail.
93 check_lib_contents(_,_,_).
94
95
96 % url(Component,Platform,Processor,URL,File,Kind)
97 url(ltsmin,linux,_,
98 'https://github.com/utwente-fmt/ltsmin/releases/download/v3.0.2/','ltsmin-v3.0.2-linux.tgz',tgz).
99 url(ltsmin,darwin,_, % these are Intel binaries; but they run under Rosetta
100 'https://github.com/utwente-fmt/ltsmin/releases/download/v3.0.2/','ltsmin-v3.0.2-osx.tgz',tgz).
101
102 url(ltl2ba,linux,x86_64,'https://stups.hhu-hosting.de/downloads/ltl2ba_pl/sicstus4.9/linux-x86_64/','ltl2ba.so',bundle).
103 url(ltl2ba,darwin,_,'https://stups.hhu-hosting.de/downloads/ltl2ba_pl/sicstus4.9/macos/','ltl2ba.bundle',bundle).
104 url(ltl2ba,windows,x86_64,'https://stups.hhu-hosting.de/downloads/ltl2ba_pl/sicstus4.9/windows-x86_64/','ltl2ba.dll',bundle).
105 url(setlog,_,_,'https://stups.hhu-hosting.de/downloads/setlog_wrapper/','setlog-cli.qlf',qlf).
106 url(z3,darwin,aarch64,Z3,'z3-5.0.0-arm64-osx-13.3.zip',
107 zip('z3-5.0.0-arm64-osx-13.3/bin/libz3.dylib')) :- z3_url(Z3).
108 url(z3,darwin,x86_64,Z3,'z3-5.0.0-x64-osx-13.3.zip',
109 zip('z3-5.0.0-x64-osx-13.3/bin/libz3.dylib')) :- z3_url(Z3).
110 url(z3,darwin,x86_64,Z3,'z3-5.0.0-x64-win.zip',
111 zip('z3-5.0.0-x64-win/bin/libz3.dylib')) :- z3_url(Z3).
112 url(z3,linux,x86_64,Z3,'z3-5.0.0-x64-glibc-2.39.zip',
113 zip('z3-5.0.0-x64-glibc-2.39/bin/libz3.dylib')) :- z3_url(Z3).
114
115 z3_url('https://github.com/Z3Prover/z3/releases/download/z3-5.0.0/').
116 %ltsmin_url('https://github.com/utwente-fmt/ltsmin/releases').
117
118 url7(Component,Plat,Proc,URL,File,Localfile,Kind) :- Localfile=File,
119 url(Component,Plat,Proc,URL,File,Kind).
120 url7(plantuml,_,_,'https://github.com/plantuml/plantuml/releases/download/v1.2024.8/','plantuml-epl-1.2024.8.jar','plantuml.jar',jar).
121
122 % curl -L https://github.com/utwente-fmt/ltsmin/releases/download/v3.0.2/ltsmin-v3.0.2-osx.tgz -o ~/Desktop/ltsmin-v3.0.2-osx.tgz
123
124 install_lib_component(Component,Opts) :-
125 host_platform(Plat),
126 host_processor(Proc),
127 url7(Component,Plat,Proc,URL,File,Localfile,Kind),!,
128 install_lib_component_aux(Component,URL,File,Localfile,Kind,Opts).
129 install_lib_component(Component,_) :-
130 host_platform(Plat),
131 host_processor(Proc),
132 url(Component,Plat,RProc,_,_,_), % we know the component and there are rules
133 Proc \= RProc,!, % but not for this processor
134 add_error(install_lib_component,'No rules to install for this processor:',Component).
135 install_lib_component(Component,_) :-
136 host_platform(Plat),
137 url(Component,RPlat,_,_,_,_), % we know the component and there are rules
138 Plat \= RPlat,!, % but not for this platform
139 add_error(install_lib_component,'No rules to install on this platform:',Component).
140 install_lib_component(Component,_) :-
141 add_error(install_lib_component,'Unknown component, no rules to install:',Component).
142
143
144 install_lib_component_aux(Component,URL,File,Localfile,Kind,Opts) :-
145 (member(silent,Opts) -> true
146 ; format_with_colour_nl(user_output,[blue],'Installing component ~w from ~w',[Component,URL])),
147 ajoin([URL,File],FullURL),
148 absolute_file_name(prob_lib(Localfile),LocalLibFile),
149 my_system_call('curl',['-L',FullURL,'-o',LocalLibFile],Opts), % will not work on Windows
150 % TODO: we could use curl -I FullURL to detect if file exists; we should get HTTP/1.1 200 OK
151 prob_lib_folder(LibFolder),
152 complete_installation(Component,LibFolder,LocalLibFile,Kind,Opts),
153 !,
154 (member(dryrun,Opts) -> true ; format_with_colour_nl(user_output,green,'Installation complete',[])).
155 install_lib_component_aux(Component,_,_,_,_,_) :-
156 add_error(install_lib_component,'Installation of component failed:',Component).
157
158 % perform steps after downloading file
159 complete_installation(ltsmin,LibFolder,LocalLibFile,_,Opts) :- !,
160 my_system_call('tar',['-xvzf',LocalLibFile,'-C',LibFolder],Opts), % creates v3.0.2 folder
161 ajoin([LibFolder,'/v3.0.2/bin'],LTSMinFolder),
162 move_file(LTSMinFolder,LibFolder,'prob2lts-seq',Opts),
163 move_file(LTSMinFolder,LibFolder,'prob2lts-sym',Opts),
164 move_file(LTSMinFolder,LibFolder,'prob2lts-dist',Opts),
165 move_file(LTSMinFolder,LibFolder,'ltsmin-printtrace',Opts),
166 my_system_call('rm',['-rf',LTSMinFolder],Opts).
167 complete_installation(z3,LibFolder,LocalLibFile,zip(FileToExtract),Opts) :- !,
168 my_system_call('unzip',['-j', '-o', '-d', LibFolder, % -j: junk paths, -o: overwrite existing libz3
169 LocalLibFile,FileToExtract],Opts),
170 my_system_call('rm',['-rf',LocalLibFile],Opts),
171 format_with_colour_nl(user_output,[blue],'Make sure that the library in ~w can be found.',[LibFolder]),
172 ld_library_path(LDPATH),
173 format_with_colour_nl(user_output,[blue],'You need to set the environment variable ~w, e.g., as follows:',[LDPATH]),
174 format_with_colour_nl(user_output,[blue],' ~w=~w:$~w probcli -repl',[LDPATH,LibFolder,LDPATH]).
175 complete_installation(_,_,_,_,_). % nothing to do
176
177 ld_library_path(Path) :- host_platform(P), ld_library_path(P,Path).
178 ld_library_path(linux,'LD_LIBRARY_PATH').
179 ld_library_path(darwin,'DYLD_LIBRARY_PATH').
180 ld_library_path(windows,'PATH').
181
182 move_file(From,To,File,Opts) :-
183 ajoin([From,'/',File],FF), %path join
184 my_system_call('mv',[FF,To],Opts).
185
186 my_system_call(Cmd,Options,Opts) :-
187 ajoin_with_sep(Options,' ',OL),
188 (member(dryrun,Opts)
189 -> format('~w ~w~n',[Cmd,OL])
190 ; absolute_file_name(path(Cmd),
191 FullCmd,
192 [access(exist),extensions(['.exe','']),solutions(all),file_errors(fail)])
193 ->
194 format_with_colour_nl(user_output,[blue],' --> ~w ~w',[FullCmd,OL]),
195 system_call(FullCmd,Options,_OutputText,ErrText,Exit),
196 (Exit=exit(0) -> true
197 ; format_with_colour_nl(user_error,[red],'~w command failed (~w) : ~s',[Cmd,Exit,ErrText]),
198 fail
199 )
200 ; add_error(pathes_lib,'Cannot execute command:',Cmd),
201 format_with_colour_nl(user_error,[blue],'Try executing the command by hand:~n ~w ~w~n',[Cmd,OL]),
202 fail
203 ).
204
205 % --------------------
206
207 safe_load_foreign_resource(SourceModule,Lib) :-
208 % print(loading_foreign_resource(Lib)),nl,flush_output,
209 catch(safe_load_foreign_resource2(SourceModule,Lib), E, (
210 host_processor(Proc),
211 (host_platform(darwin) -> host_processor(Proc),
212 ajoin(['Could not load library ',Lib,
213 ', be sure to set LD_LIBRARY_PATH to include the lib folder of ProB for the right architecture (',
214 Proc,'). Exception:'],Msg)
215 ; host_platform(linux) ->
216 ajoin(['Could not load library ',Lib,
217 ', be sure to set LD_LIBRARY_PATH to include the lib folder of ProB on Linux. Exception:'],Msg)
218 ; ajoin(['Could not load library ',Lib, '. Exception:'],Msg)
219 ),
220 safe_add_error(Lib,Msg,E),
221 fail
222 )).
223
224 % check for existence error
225 % we need to prefix load_foreign_resource with the right module
226 safe_load_foreign_resource2(SourceModule,Lib) :-
227 E=error(existence_error(_,_),_),
228 debug_format(9,'Loading foreign resource module ~w from ~w~n',[SourceModule,Lib]),
229 flush_output, % flush output in case of crash (e.g., Killed: 9 on macOS)
230 catch(SourceModule:load_foreign_resource(library(Lib)), E, (
231 prob_lib_folder(LibF),
232 ajoin(['Could not load library ',Lib,
233 ', check that it is available in the lib folder ',LibF,
234 ' of ProB (you can run probcli -check_lib). Exception:'],Msg),
235 safe_add_error(Lib,Msg,E),
236 check_lib_component_available(Lib),
237 fail
238 )).
239
240 safe_add_error(Lib,Msg,E) :-
241 E2=error(existence_error(_,_),_),
242 catch(error_manager:add_error(Lib,Msg,E), E2, % maybe because counter library could not be loaded
243 format_with_colour_nl(user_error,[red],'~w ~w~n',[Msg,E])).