1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 1985-2012, University of Amsterdam 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module('$autoload', 37 [ '$find_library'/5, 38 '$in_library'/3, 39 '$define_predicate'/1, 40 '$update_library_index'/0, 41 make_library_index/1, 42 make_library_index/2, 43 reload_library_index/0, 44 autoload_path/1 45 ]). 46 47:- dynamic 48 library_index/3, % Head x Module x Path 49 autoload_directories/1, % List 50 index_checked_at/1. % Time 51:- volatile 52 library_index/3, 53 autoload_directories/1, 54 index_checked_at/1. 55 56user:file_search_path(autoload, library(.)).
67'$find_library'(Module, Name, Arity, LoadModule, Library) :-
68 load_library_index(Name, Arity),
69 functor(Head, Name, Arity),
70 ( library_index(Head, Module, Library),
71 LoadModule = Module
72 ; library_index(Head, LoadModule, Library)
73 ),
74 !.
81'$in_library'(Name, Arity, Path) :- 82 atom(Name), integer(Arity), 83 !, 84 load_library_index(Name, Arity), 85 functor(Head, Name, Arity), 86 library_index(Head, _, Path). 87'$in_library'(Name, Arity, Path) :- 88 load_library_index(Name, Arity), 89 library_index(Head, _, Path), 90 functor(Head, Name, Arity).
97:- meta_predicate 98 '$define_predicate'( ). 99 100'$define_predicate'(Head) :- 101 '$defined_predicate'(Head), 102 !. 103'$define_predicate'(Term) :- 104 Term = Module:Head, 105 ( compound(Head) 106 -> compound_name_arity(Head, Name, Arity) 107 ; Name = Head, Arity = 0 108 ), 109 '$undefined_procedure'(Module, Name, Arity, retry). 110 111 112 /******************************** 113 * UPDATE INDEX * 114 ********************************/ 115 116:- thread_local 117 silent/0.
126'$update_library_index' :- 127 setof(Dir, writable_indexed_directory(Dir), Dirs), 128 !, 129 setup_call_cleanup( 130 asserta(silent, Ref), 131 guarded_make_library_index(Dirs), 132 erase(Ref)), 133 ( flag('$modified_index', true, false) 134 -> reload_library_index 135 ; true 136 ). 137'$update_library_index'. 138 139guarded_make_library_index([]). 140guarded_make_library_index([Dir|Dirs]) :- 141 ( catch(make_library_index(Dir), E, 142 print_message(error, E)) 143 -> true 144 ; print_message(warning, goal_failed(make_library_index(Dir))) 145 ), 146 guarded_make_library_index(Dirs).
153writable_indexed_directory(Dir) :- 154 index_file_name(IndexFile, [access([read,write])]), 155 file_directory_name(IndexFile, Dir). 156writable_indexed_directory(Dir) :- 157 absolute_file_name(library('MKINDEX'), 158 [ file_type(prolog), 159 access(read), 160 solutions(all), 161 file_errors(fail) 162 ], MkIndexFile), 163 file_directory_name(MkIndexFile, Dir), 164 plfile_in_dir(Dir, 'INDEX', _, IndexFile), 165 access_file(IndexFile, write). 166 167 168 /******************************** 169 * LOAD INDEX * 170 ********************************/
176reload_library_index :- 177 with_mutex('$autoload', clear_library_index). 178 179clear_library_index :- 180 retractall(library_index(_, _, _)), 181 retractall(autoload_directories(_)), 182 retractall(index_checked_at(_)).
191load_library_index(Name, Arity) :- 192 atom(Name), integer(Arity), 193 functor(Head, Name, Arity), 194 library_index(Head, _, _), 195 !. 196load_library_index(_, _) :- 197 notrace(with_mutex('$autoload', load_library_index_p)). 198 199load_library_index_p :- 200 index_checked_at(Time), 201 get_time(Now), 202 Now-Time < 60, 203 !. 204load_library_index_p :- 205 findall(Index, index_file_name(Index, [access(read)]), List0), 206 list_set(List0, List), 207 retractall(index_checked_at(_)), 208 get_time(Now), 209 assert(index_checked_at(Now)), 210 ( autoload_directories(List) 211 -> true 212 ; retractall(library_index(_, _, _)), 213 retractall(autoload_directories(_)), 214 read_index(List), 215 assert(autoload_directories(List)) 216 ). 217 218list_set([], R) :- % == list_to_set/2 from library(lists) 219 closel(R). 220list_set([H|T], R) :- 221 memberchk(H, R), 222 !, 223 list_set(T, R). 224 225closel([]) :- !. 226closel([_|T]) :- 227 closel(T).
autoload
.
238index_file_name(IndexFile, Options) :- 239 absolute_file_name(autoload('INDEX'), 240 IndexFile, 241 [ file_type(prolog), 242 solutions(all), 243 file_errors(fail) 244 | Options 245 ]). 246 247read_index([]) :- !. 248read_index([H|T]) :- 249 !, 250 read_index(H), 251 read_index(T). 252read_index(Index) :- 253 print_message(silent, autoload(read_index(Dir))), 254 file_directory_name(Index, Dir), 255 setup_call_cleanup( 256 '$push_input_context'(autoload_index), 257 setup_call_cleanup( 258 open(Index, read, In), 259 read_index_from_stream(Dir, In), 260 close(In)), 261 '$pop_input_context'). 262 263read_index_from_stream(Dir, In) :- 264 repeat, 265 read(In, Term), 266 assert_index(Term, Dir), 267 !. 268 269assert_index(end_of_file, _) :- !. 270assert_index(index(Name, Arity, Module, File), Dir) :- 271 !, 272 functor(Head, Name, Arity), 273 atomic_list_concat([Dir, '/', File], Path), 274 assertz(library_index(Head, Module, Path)), 275 fail. 276assert_index(Term, Dir) :- 277 print_message(error, illegal_autoload_index(Dir, Term)), 278 fail. 279 280 281 /******************************** 282 * CREATE INDEX.pl * 283 ********************************/
INDEX.pl
. In Dir contains a file
MKINDEX.pl
, this file is loaded and we assume that the index is
created by directives that appearin this file. Otherwise, all
source files are scanned for their module-header and all
exported predicates are added to the autoload index.
296make_library_index(Dir0) :- 297 forall(absolute_file_name(Dir0, Dir, 298 [ expand(true), 299 file_type(directory), 300 file_errors(fail), 301 solutions(all) 302 ]), 303 make_library_index2(Dir)). 304 305make_library_index2(Dir) :- 306 plfile_in_dir(Dir, 'MKINDEX', MkIndex, AbsMkIndex), 307 access_file(AbsMkIndex, read), 308 !, 309 setup_call_cleanup( 310 working_directory(OldDir, Dir), 311 load_files(user:MkIndex, [silent(true)]), 312 working_directory(_, OldDir)). 313make_library_index2(Dir) :- 314 findall(Pattern, source_file_pattern(Pattern), PatternList), 315 make_library_index2(Dir, PatternList).
INDEX.pl
for Dir by scanning all files
that match any of the file-patterns in Patterns. Typically, this
appears as a directive in MKINDEX.pl
. For example:
:- make_library_index(., ['*.pl']).
329make_library_index(Dir0, Patterns) :- 330 forall(absolute_file_name(Dir0, Dir, 331 [ expand(true), 332 file_type(directory), 333 file_errors(fail), 334 solutions(all) 335 ]), 336 make_library_index2(Dir, Patterns)). 337 338make_library_index2(Dir, Patterns) :- 339 plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex), 340 ensure_slash(Dir, DirS), 341 pattern_files(Patterns, DirS, Files), 342 ( library_index_out_of_date(AbsIndex, Files) 343 -> do_make_library_index(AbsIndex, DirS, Files), 344 flag('$modified_index', _, true) 345 ; true 346 ). 347 348ensure_slash(Dir, DirS) :- 349 ( sub_atom(Dir, _, _, 0, /) 350 -> DirS = Dir 351 ; atom_concat(Dir, /, DirS) 352 ). 353 354source_file_pattern(Pattern) :- 355 user:prolog_file_type(PlExt, prolog), 356 PlExt \== qlf, 357 atom_concat('*.', PlExt, Pattern). 358 359plfile_in_dir(Dir, Base, PlBase, File) :- 360 file_name_extension(Base, pl, PlBase), 361 atomic_list_concat([Dir, '/', PlBase], File). 362 363pattern_files([], _, []). 364pattern_files([H|T], DirS, Files) :- 365 atom_concat(DirS, H, P0), 366 expand_file_name(P0, Files0), 367 '$append'(Files0, Rest, Files), 368 pattern_files(T, DirS, Rest). 369 370library_index_out_of_date(Index, _Files) :- 371 \+ exists_file(Index), 372 !. 373library_index_out_of_date(Index, Files) :- 374 time_file(Index, IndexTime), 375 ( time_file('.', DotTime), 376 DotTime > IndexTime 377 ; '$member'(File, Files), 378 time_file(File, FileTime), 379 FileTime > IndexTime 380 ), 381 !. 382 383 384do_make_library_index(Index, Dir, Files) :- 385 ensure_slash(Dir, DirS), 386 '$stage_file'(Index, StagedIndex), 387 setup_call_catcher_cleanup( 388 open(StagedIndex, write, Out), 389 ( print_message(informational, make(library_index(Dir))), 390 index_header(Out), 391 index_files(Files, DirS, Out) 392 ), 393 Catcher, 394 install_index(Out, Catcher, StagedIndex, Index)). 395 396install_index(Out, Catcher, StagedIndex, Index) :- 397 catch(close(Out), Error, true), 398 ( silent 399 -> OnError = silent 400 ; OnError = error 401 ), 402 ( var(Error) 403 -> TheCatcher = Catcher 404 ; TheCatcher = exception(Error) 405 ), 406 '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
412index_files([], _, _). 413index_files([File|Files], DirS, Fd) :- 414 catch(setup_call_cleanup( 415 open(File, read, In), 416 read(In, Term), 417 close(In)), 418 E, print_message(warning, E)), 419 ( Term = (:- module(Module, Public)), 420 is_list(Public) 421 -> atom_concat(DirS, Local, File), 422 file_name_extension(Base, _, Local), 423 forall(public_predicate(Public, Name/Arity), 424 format(Fd, 'index((~k), ~k, ~k, ~k).~n', 425 [Name, Arity, Module, Base])) 426 ; true 427 ), 428 index_files(Files, DirS, Fd). 429 430public_predicate(Public, PI) :- 431 '$member'(PI0, Public), 432 canonical_pi(PI0, PI). 433 434canonical_pi(Var, _) :- 435 var(Var), !, fail. 436canonical_pi(Name/Arity, Name/Arity). 437canonical_pi(Name//A0, Name/Arity) :- 438 Arity is A0 + 2. 439 440 441index_header(Fd):- 442 format(Fd, '/* Creator: make/0~n~n', []), 443 format(Fd, ' Purpose: Provide index for autoload~n', []), 444 format(Fd, '*/~n~n', []). 445 446 447 /******************************* 448 * EXTENDING * 449 *******************************/
autoload
and reloads the library
index. For example:
:- autoload_path(library(http)).
If this call appears as a directive, it is term-expanded into a clause for file_search_path/2 and a directive calling reload_library_index/0. This keeps source information and allows for removing this directive.
466autoload_path(Alias) :- 467 ( user:file_search_path(autoload, Alias) 468 -> true 469 ; assertz(user:file_search_path(autoload, Alias)), 470 reload_library_index 471 ). 472 473systemterm_expansion((:- autoload_path(Alias)), 474 [ user:file_search_path(autoload, Alias), 475 (:- reload_library_index) 476 ])