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, 49 autoload_directories/1, 50 index_checked_at/1. 51:- volatile
52 library_index/3,
53 autoload_directories/1,
54 index_checked_at/1. 55
56user:file_search_path(autoload, library(.)).
57
58
66
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 !.
75
80
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).
91
96
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 115
116:- thread_local
117 silent/0. 118
125
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).
147
152
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 171
175
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(_)).
183
184
190
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) :- 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).
228
229
237
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 284
295
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).
316
328
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).
407
411
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
(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 450
465
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
473system:term_expansion((:- autoload_path(Alias)),
474 [ user:file_search_path(autoload, Alias),
475 (:- reload_library_index)
476 ])