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