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 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
(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 440
455
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
463system:term_expansion((:- autoload_path(Alias)),
464 [ user:file_search_path(autoload, Alias),
465 (:- reload_library_index)
466 ])