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 atom_concat('*.', PlExt, Pattern). 357 358plfile_in_dir(Dir, Base, PlBase, File) :- 359 file_name_extension(Base, pl, PlBase), 360 atomic_list_concat([Dir, '/', PlBase], File). 361 362pattern_files([], _, []). 363pattern_files([H|T], DirS, Files) :- 364 atom_concat(DirS, H, P0), 365 expand_file_name(P0, Files0), 366 '$append'(Files0, Rest, Files), 367 pattern_files(T, DirS, Rest). 368 369library_index_out_of_date(Index, _Files) :- 370 \+ exists_file(Index), 371 !. 372library_index_out_of_date(Index, Files) :- 373 time_file(Index, IndexTime), 374 ( time_file('.', DotTime), 375 DotTime > IndexTime 376 ; '$member'(File, Files), 377 time_file(File, FileTime), 378 FileTime > IndexTime 379 ), 380 !. 381 382 383do_make_library_index(Index, Dir, Files) :- 384 ensure_slash(Dir, DirS), 385 catch(setup_call_cleanup( 386 open(Index, write, Fd), 387 ( print_message(informational, make(library_index(Dir))), 388 index_header(Fd), 389 index_files(Files, DirS, Fd) 390 ), 391 close(Fd)), 392 E, index_error(E)). 393 394index_error(E) :- 395 silent, 396 E = error(permission_error(open, source_sink, _)), 397 !. 398index_error(E) :- 399 print_message(error, E). 400 401 402index_files([], _, _). 403index_files([File|Files], DirS, Fd) :- 404 catch(setup_call_cleanup( 405 open(File, read, In), 406 read(In, Term), 407 close(In)), 408 E, print_message(warning, E)), 409 ( Term = (:- module(Module, Public)), 410 is_list(Public) 411 -> atom_concat(DirS, Local, File), 412 file_name_extension(Base, _, Local), 413 forall(public_predicate(Public, Name/Arity), 414 format(Fd, 'index((~k), ~k, ~k, ~k).~n', 415 [Name, Arity, Module, Base])) 416 ; true 417 ), 418 index_files(Files, DirS, Fd). 419 420public_predicate(Public, PI) :- 421 '$member'(PI0, Public), 422 canonical_pi(PI0, PI). 423 424canonical_pi(Var, _) :- 425 var(Var), !, fail. 426canonical_pi(Name/Arity, Name/Arity). 427canonical_pi(Name//A0, Name/Arity) :- 428 Arity is A0 + 2. 429 430 431index_header(Fd):- 432 format(Fd, '/* Creator: make/0~n~n', []), 433 format(Fd, ' Purpose: Provide index for autoload~n', []), 434 format(Fd, '*/~n~n', []). 435 436 437 /******************************* 438 * EXTENDING * 439 *******************************/
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.
456autoload_path(Alias) :- 457 ( user:file_search_path(autoload, Alias) 458 -> true 459 ; assertz(user:file_search_path(autoload, Alias)), 460 reload_library_index 461 ). 462 463systemterm_expansion((:- autoload_path(Alias)), 464 [ user:file_search_path(autoload, Alias), 465 (:- reload_library_index) 466 ])