36
37:- module(prolog_deps,
38 [ file_autoload_directives/3, 39 file_auto_import/2 40 ]). 41:- use_module(library(apply), [convlist/3, maplist/3]). 42:- use_module(library(filesex), [copy_file/2]). 43:- use_module(library(lists), [select/3, append/3, member/2]). 44:- use_module(library(option), [option/2, option/3]). 45:- use_module(library(pairs), [group_pairs_by_key/2]). 46:- use_module(library(pprint), [print_term/2]). 47:- use_module(library(prolog_code), [pi_head/2]). 48:- use_module(library(prolog_source),
49 [ file_name_on_path/2,
50 path_segments_atom/2,
51 prolog_open_source/2,
52 prolog_read_source_term/4,
53 prolog_close_source/1
54 ]). 55:- use_module(library(prolog_xref),
56 [ xref_source/1,
57 xref_module/2,
58 xref_called/4,
59 xref_defined/3,
60 xref_built_in/1
61 ]). 62:- use_module(library(readutil), [read_file_to_string/3]). 63:- use_module(library(solution_sequences), [distinct/2]). 64
70
71:- multifile user:file_search_path/2. 72
73user:file_search_path(noautoload, library(.)).
74user:file_search_path(noautoload, library(semweb)).
75user:file_search_path(noautoload, library(lynx)).
76user:file_search_path(noautoload, library(tipc)).
77user:file_search_path(noautoload, library(cql)).
78user:file_search_path(noautoload, library(http)).
79user:file_search_path(noautoload, library(dcg)).
80user:file_search_path(noautoload, library(unicode)).
81user:file_search_path(noautoload, library(clp)).
82user:file_search_path(noautoload, library(pce(prolog/lib))).
83
84
117
118file_autoload_directives(File, Directives, Options) :-
119 xref_source(File),
120 findall(Head, distinct(Head, undefined(File, Head, Options)), Missing),
121 option(update(Old), Options, []),
122 convlist(missing_autoload(File, Old), Missing, Pairs),
123 keysort(Pairs, Pairs1),
124 group_pairs_by_key(Pairs1, Grouped),
125 directives(File, Grouped, Directives, Options).
126
132
133undefined(File, Undef, Options) :-
134 xref_module(File, _),
135 !,
136 xref_called_cond(File, Undef, Cond),
137 \+ ( available(File, Undef, How, Options),
138 How \== plain_file
139 ),
140 included_if_defined(Cond, Undef),
141 Undef \= (_:_).
142undefined(File, Undef, Options) :-
143 xref_called_cond(File, Undef, Cond),
144 \+ available(File, Undef, _, Options),
145 included_if_defined(Cond, Undef),
146 Undef \= (_:_).
147
149
150included_if_defined(true, _) :- !.
151included_if_defined(false, _) :- !, fail.
152included_if_defined(fail, _) :- !, fail.
153included_if_defined(current_predicate(Name/Arity), Callable) :-
154 \+ functor(Callable, Name, Arity),
155 !.
156included_if_defined(\+ Cond, Callable) :-
157 !,
158 \+ included_if_defined(Cond, Callable).
159included_if_defined((A,B), Callable) :-
160 !,
161 included_if_defined(A, Callable),
162 included_if_defined(B, Callable).
163included_if_defined((A;B), Callable) :-
164 !,
165 ( included_if_defined(A, Callable)
166 ; included_if_defined(B, Callable)
167 ).
168
169xref_called_cond(Source, Callable, Cond) :-
170 xref_called(Source, Callable, By, Cond),
171 By \= Callable. 172
176
177available(File, Called, How, Options) :-
178 xref_defined(File, Called, How0),
179 ( How0 = imported(_)
180 -> option(missing(true), Options)
181 ; true
182 ),
183 !,
184 How = How0.
185available(_, Called, How, _) :-
186 built_in_predicate(Called),
187 !,
188 How = builtin.
189available(_, Called, How, _) :-
190 Called = _:_,
191 defined(_, Called),
192 !,
193 How = module_qualified.
194available(_, M:G, How, _) :-
195 defined(ExportFile, G),
196 xref_module(ExportFile, M),
197 !,
198 How = module_overruled.
199available(_, Called, How, _) :-
200 defined(ExportFile, Called),
201 \+ xref_module(ExportFile, _),
202 !,
203 How == plain_file.
204
208
209built_in_predicate(Goal) :-
210 strip_module(Goal, _, Plain),
211 xref_built_in(Plain).
212
216
217defined(File, Callable) :-
218 xref_defined(File, Callable, How),
219 How \= imported(_).
220
221
222 225
226missing_autoload(Src, _, Head, From-Head) :-
227 xref_defined(Src, Head, imported(From)),
228 !.
229missing_autoload(Src, Directives, Head, File-Head) :-
230 src_file(Src, SrcFile),
231 member(:-(Dir), Directives),
232 directive_file(Dir, FileSpec),
233 absolute_file_name(FileSpec, File,
234 [ file_type(prolog),
235 file_errors(fail),
236 relative_to(SrcFile),
237 access(read)
238 ]),
239 exports(File, Exports),
240 member(PI, Exports),
241 is_pi(PI),
242 pi_head(PI, Head),
243 !.
244missing_autoload(_Src, _, Head, File-Head) :-
245 predicate_property(Head, autoload(File0)),
246 !,
247 ( absolute_file_name(File0, File,
248 [ access(read),
249 file_type(prolog),
250 file_errors(fail)
251 ])
252 -> true
253 ; File = File0
254 ).
255missing_autoload(_Src, _, Head, File-Head) :-
256 noautoload(Head, File),
257 !.
258missing_autoload(_Src, _, Head, _) :-
259 pi_head(PI, Head),
260 print_message(warning,
261 error(existence_error(procedure, PI), _)),
262 fail.
263
264:- if(exists_source(library(pce))). 265:- autoload(library(pce), [get/3]). 266src_file(@(Ref), File) =>
267 get(?(@(Ref), file), absolute_path, File).
268:- endif. 269src_file(File0, File) =>
270 File = File0.
271
275
276directives(File, FileAndHeads, Directives, Options) :-
277 option(update(Old), Options, []),
278 phrase(update_directives(Old, FileAndHeads, RestDeps, File),
279 Directives, Rest),
280 update_style(Old, Options, Options1),
281 maplist(directive(Options1), RestDeps, Rest0),
282 sort(Rest0, Rest).
283
284update_directives([], Deps, Deps, _) -->
285 [].
286update_directives([:-(H)|T], Deps0, Deps, File) -->
287 { update_directive(File, H, Deps0, Deps1, Directive) },
288 !,
289 [ :-(Directive) ],
290 update_directives(T, Deps1, Deps, File).
291update_directives([H|T], Deps0, Deps, File) -->
292 [ H ],
293 update_directives(T, Deps0, Deps, File).
294
295update_directive(Src, Dir0, Deps0, Deps, Dir) :-
296 src_file(Src, SrcFile),
297 directive_file(Dir0, FileSpec),
298 absolute_file_name(FileSpec, File,
299 [ file_type(prolog),
300 file_errors(fail),
301 relative_to(SrcFile),
302 access(read)
303 ]),
304 select(DepFile-Heads, Deps0, Deps),
305 same_dep_file(DepFile, File),
306 !,
307 ( Dir0 =.. [Pred,File0,Imports]
308 -> exports(File, Exports),
309 maplist(head_pi(Exports), Heads, PIs),
310 subtract_pis(PIs, Imports, New),
311 append(Imports, New, NewImports),
312 Dir =.. [Pred,File0,NewImports]
313 ; Dir = Dir0
314 ).
315
316directive_file(use_module(File), File).
317directive_file(use_module(File,_), File).
318directive_file(autoload(File), File).
319directive_file(autoload(File,_), File).
320
321same_dep_file(File, File) :-
322 !.
323same_dep_file(Dep, _File) :-
324 exists_file(Dep),
325 !,
326 fail.
327same_dep_file(Dep, File) :-
328 user:prolog_file_type(Ext, prolog),
329 file_name_extension(Dep, Ext, DepFile),
330 same_file(DepFile, File),
331 !.
332
333exports(File, Public) :-
334 E = error(_,_),
335 catch('$autoload':exports(File, _Module, Public), E,
336 ( print_message(warning, E),
337 Public = []
338 )).
339
340is_pi(Name/Arity), atom(Name), integer(Arity) => true.
341is_pi(Name//Arity), atom(Name), integer(Arity) => true.
342is_pi(_) => fail.
343
345
346head_pi(PIs, Head, PI) :-
347 head_pi(Head, PI),
348 memberchk(PI, PIs),
349 !.
350head_pi(_PIs, Head, PI) :-
351 pi_head(PI, Head).
352
353head_pi(Head, PI) :-
354 pi_head(PI0, Head),
355 ( PI = PI0
356 ; dcg_pi(PI0, PI)
357 ).
358
359dcg_pi(Module:Name/Arity, PI), integer(Arity), Arity >= 2 =>
360 DCGArity is Arity - 2,
361 PI = Module:Name//DCGArity.
362dcg_pi(Name/Arity, PI), integer(Arity), Arity >= 2 =>
363 DCGArity is Arity - 2,
364 PI = Name//DCGArity.
365dcg_pi(_/Arity, _), integer(Arity) =>
366 fail.
367
369
370subtract_pis([], _, R) =>
371 R = [].
372subtract_pis([H|T], D, R) =>
373 ( member(E, D),
374 same_pi(H, E)
375 -> subtract_pis(T, D, R)
376 ; R = [H|R1],
377 subtract_pis(T, D, R1)
378 ).
379
380same_pi(PI, PI) => true.
381same_pi(Name/A1, Name//A2) => A1 =:= A2+2.
382same_pi(Name//A1, Name/A2) => A1 =:= A2-2.
383same_pi(_,_) => fail.
384
385
390
391update_style(_Old, Options, Options) :-
392 option(directive(_), Options),
393 !.
394update_style(Old, Options, [directive(autoload/2)|Options]) :-
395 memberchk((:- autoload(_,_)), Old),
396 !.
397update_style(Old, Options, [directive(autoload/1)|Options]) :-
398 memberchk((:- autoload(_)), Old),
399 !.
400update_style(Old, Options, [directive(use_module/2)|Options]) :-
401 memberchk((:- use_module(_,_)), Old),
402 !.
403update_style(Old, Options, [directive(use_module/1)|Options]) :-
404 memberchk((:- use_module(_)), Old),
405 !.
406update_style(_, Options, Options).
407
408
412
413directive(Options, File-Heads, Directive) :-
414 file_name_extension(File, pl, LibFile),
415 file_name_on_path(LibFile, Lib0),
416 segments(Lib0, Lib),
417 maplist(pi_head, PIs, Heads),
418 make_directive(Lib, PIs, Directive, Options).
419
420segments(Term0, Term) :-
421 Term0 =.. [Alias,Atom],
422 path_segments_atom(Segments, Atom),
423 format(atom(Atom), '~q', [Segments]),
424 !,
425 Term =.. [Alias,Segments].
426segments(FilePL, File) :-
427 atom(FilePL),
428 file_name_extension(File, pl, FilePL),
429 !.
430segments(Term, Term).
431
432:- multifile
433 prolog:no_autoload_module/1. 434
435make_directive(Lib, Import, (:- use_module(Lib, Import)), Options) :-
436 option(directive(use_module/2), Options, use_autoload/2),
437 !.
438make_directive(Lib, _Import, (:- use_module(Lib)), Options) :-
439 option(directive(use_module/1), Options, use_autoload/2),
440 !.
441make_directive(Lib, _Import, (:- use_module(Lib)), Options) :-
442 option(directive(use_autoload/1), Options, use_autoload/2),
443 prolog:no_autoload_module(Lib),
444 !.
445make_directive(Lib, Import, (:- use_module(Lib, Import)), _) :-
446 prolog:no_autoload_module(Lib),
447 !.
448make_directive(Lib, _Import, (:- autoload(Lib)), Options) :-
449 option(directive(use_autoload/1), Options, use_autoload/2),
450 !.
451make_directive(Lib, Import, (:- autoload(Lib, Import)), _).
452
453
454 457
458:- dynamic
459 library_index/3, 460 autoload_directories/1, 461 index_checked_at/1. 462:- volatile
463 library_index/3,
464 autoload_directories/1,
465 index_checked_at/1. 466
467noautoload(Head, File) :-
468 functor(Head, Name, Arity),
469 context_module(Here),
470 '$autoload':load_library_index(Here:Name, Arity, Here:noautoload('INDEX')),
471 library_index(Head, _, File).
472
473
474 477
485
486file_auto_import(File, Options) :-
487 absolute_file_name(File, Path,
488 [ file_type(prolog),
489 access(read)
490 ]),
491 file_autoload_directives(Path, Directives, Options),
492 ( option(backup(Ext), Options)
493 -> file_name_extension(Path, Ext, Old),
494 copy_file(Path, Old)
495 ; true
496 ),
497 Edit = _{import:Directives, done:_},
498 ( has_import(Path)
499 -> edit_file(Old, Path, Edit.put(replace,true))
500 ; edit_file(Old, Path, Edit.put(new,true))
501 ).
502
503has_import(InFile) :-
504 setup_call_cleanup(
505 prolog_open_source(InFile, In),
506 ( repeat,
507 prolog_read_source_term(In, Term, _Expanded, []),
508 ( Term == end_of_file
509 -> !
510 ; true
511 )
512 ),
513 prolog_close_source(In)),
514 nonvar(Term),
515 import_directive(Term),
516 !.
517
518import_directive((:- use_module(_))).
519import_directive((:- use_module(_, _))).
520
522
523rewrite_term(Never,_,_,_) :-
524 never_rewrite(Never),
525 !,
526 fail.
527rewrite_term(Import,false,[],Options) :-
528 Options.done == true,
529 !,
530 import_directive(Import).
531rewrite_term(In,false,Directives,Options) :-
532 import_directive(In),
533 !,
534 append(Options.import, [nl], Directives),
535 Options.done = true.
536rewrite_term(In,true,Directives,Options) :-
537 In = (:- module(_,_)),
538 Options.get(new) == true,
539 !,
540 append(Options.import, [nl], Directives),
541 Options.done = true.
542
543never_rewrite((:- use_module(_, []))).
544
545edit_file(InFile, OutFile, Options) :-
546 read_file_to_string(InFile, String, []),
547 setup_call_cleanup(
548 prolog_open_source(InFile, In),
549 setup_call_cleanup(
550 open(OutFile, write, Out),
551 rewrite(In, Out, String, Options),
552 close(Out)),
553 prolog_close_source(In)).
554
555rewrite(In, Out, String, Options) :-
556 prolog_read_source_term(
557 In, Term, _Expanded,
558 [ term_position(StartPos),
559 subterm_positions(TermPos),
560 comments(Comments)
561 ]),
562 stream_position_data(char_count, StartPos, StartChar),
563 copy_comments(Comments, StartChar, String, Out),
564 ( Term == end_of_file
565 -> true
566 ; ( nonvar(Term),
567 rewrite_term(Term, Keep, List, Options)
568 -> ( Keep == true
569 -> copy_term_string(TermPos, String, Out)
570 ; true
571 ),
572 forall(member(T, List),
573 output_term(Out, T)),
574 ( append(_, [nl], List)
575 -> skip_blanks(In)
576 ; true
577 )
578 ; copy_term_string(TermPos, String, Out)
579 ),
580 rewrite(In, Out, String, Options)
581 ).
582
583output_term(Out, nl) :-
584 !,
585 nl(Out).
586output_term(Out, Term) :-
587 print_term(Term, [output(Out)]),
588 format(Out, '.~n', []).
589
([Pos-H|T], StartChar, String, Out) :-
591 stream_position_data(char_count, Pos, Start),
592 Start < StartChar,
593 !,
594 string_length(H, Len),
595 sub_string(String, Start, Len, _, Comment),
596 End is Start+Len+1,
597 layout_after(End, String, Layout),
598 format(Out, '~s~s', [Comment, Layout]),
599 copy_comments(T, StartChar, String, Out).
600copy_comments(_, _, _, _).
601
602copy_term_string(TermPos, String, Out) :-
603 arg(1, TermPos, Start),
604 arg(2, TermPos, End),
605 Len is End - Start,
606 sub_string(String, Start, Len, _, TermString),
607 End1 is End + 1,
608 full_stop_after(End1, String, Layout),
609 format(Out, '~s~s', [TermString, Layout]).
610
611layout_after(Index, String, [H|T]) :-
612 string_code(Index, String, H),
613 code_type(H, space),
614 !,
615 Index2 is Index+1,
616 layout_after(Index2, String, T).
617layout_after(_, _, []).
618
619full_stop_after(Index, String, [H|T]) :-
620 string_code(Index, String, H),
621 Index2 is Index+1,
622 ( code_type(H, space)
623 -> !, full_stop_after(Index2, String, T)
624 ; H == 0'.
625 -> !, layout_after(Index2, String, T)
626 ).
627full_stop_after(_, _, []).
628
629skip_blanks(In) :-
630 peek_code(In, C),
631 code_type(C, space),
632 !,
633 get_code(In, _),
634 skip_blanks(In).
635skip_blanks(_)