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,object/1]). 288src_file(Ref, File) =>
289 object(Ref),
290 get(?(Ref, file), absolute_path, File).
291:- endif. 292src_file(File0, File) =>
293 File = File0.
294
298
299directives(File, FileAndHeads, Directives, Options) :-
300 option(update(Old), Options, []),
301 phrase(update_directives(Old, FileAndHeads, RestDeps, File),
302 Directives, Rest),
303 update_style(Old, Options, Options1),
304 maplist(directive(Options1), RestDeps, Rest0),
305 sort(Rest0, Rest).
306
307update_directives([], Deps, Deps, _) -->
308 [].
309update_directives([:-(H)|T], Deps0, Deps, File) -->
310 { update_directive(File, H, Deps0, Deps1, Directive) },
311 !,
312 [ :-(Directive) ],
313 update_directives(T, Deps1, Deps, File).
314update_directives([H|T], Deps0, Deps, File) -->
315 [ H ],
316 update_directives(T, Deps0, Deps, File).
317
318update_directive(Src, Dir0, Deps0, Deps, Dir) :-
319 src_file(Src, SrcFile),
320 directive_file(Dir0, FileSpec),
321 absolute_file_name(FileSpec, File,
322 [ file_type(prolog),
323 file_errors(fail),
324 relative_to(SrcFile),
325 access(read)
326 ]),
327 qlf_pl_file(File, PlFile),
328 select(DepFile-Heads, Deps0, Deps),
329 same_dep_file(DepFile, PlFile),
330 !,
331 ( Dir0 =.. [Pred,File0,Imports]
332 -> xref_public_list(PlFile, SrcFile, [exports(Exports)]),
333 maplist(head_pi(Exports), Heads, PIs),
334 subtract_pis(PIs, Imports, New),
335 append(Imports, New, NewImports),
336 Dir =.. [Pred,File0,NewImports]
337 ; Dir = Dir0
338 ).
339
340directive_file(use_module(File), File).
341directive_file(use_module(File,_), File).
342directive_file(autoload(File), File).
343directive_file(autoload(File,_), File).
344
345qlf_pl_file(File, PlFile) :-
346 file_name_extension(_Base, Ext, File),
347 user:prolog_file_type(Ext, qlf),
348 !,
349 '$qlf_module'(File, Info),
350 PlFile = Info.get(file).
351qlf_pl_file(File, File).
352
353same_dep_file(File, File) :-
354 !.
355same_dep_file(Dep, _File) :-
356 exists_file(Dep),
357 !,
358 fail.
359same_dep_file(Dep, File) :-
360 user:prolog_file_type(Ext, prolog),
361 file_name_extension(Dep, Ext, DepFile),
362 same_file(DepFile, File),
363 !.
364
365is_pi(Name/Arity), atom(Name), integer(Arity) => true.
366is_pi(Name//Arity), atom(Name), integer(Arity) => true.
367is_pi(_) => fail.
368
370
371head_pi(PIs, Head, PI) :-
372 head_pi(Head, PI),
373 memberchk(PI, PIs),
374 !.
375head_pi(_PIs, Head, PI) :-
376 pi_head(PI, Head).
377
378head_pi(Head, PI) :-
379 pi_head(PI0, Head),
380 ( PI = PI0
381 ; dcg_pi(PI0, PI)
382 ).
383
384dcg_pi(Module:Name/Arity, PI), integer(Arity), Arity >= 2 =>
385 DCGArity is Arity - 2,
386 PI = Module:Name//DCGArity.
387dcg_pi(Name/Arity, PI), integer(Arity), Arity >= 2 =>
388 DCGArity is Arity - 2,
389 PI = Name//DCGArity.
390dcg_pi(_/Arity, _), integer(Arity) =>
391 fail.
392
394
395subtract_pis([], _, R) =>
396 R = [].
397subtract_pis([H|T], D, R) =>
398 ( member(E, D),
399 same_pi(H, E)
400 -> subtract_pis(T, D, R)
401 ; R = [H|R1],
402 subtract_pis(T, D, R1)
403 ).
404
405same_pi(PI, PI) => true.
406same_pi(Name/A1, Name//A2) => A1 =:= A2+2.
407same_pi(Name//A1, Name/A2) => A1 =:= A2-2.
408same_pi(_,_) => fail.
409
410
415
416update_style(_Old, Options, Options) :-
417 option(directive(_), Options),
418 !.
419update_style(Old, Options, [directive(autoload/2)|Options]) :-
420 memberchk((:- autoload(_,_)), Old),
421 !.
422update_style(Old, Options, [directive(autoload/1)|Options]) :-
423 memberchk((:- autoload(_)), Old),
424 !.
425update_style(Old, Options, [directive(use_module/2)|Options]) :-
426 memberchk((:- use_module(_,_)), Old),
427 !.
428update_style(Old, Options, [directive(use_module/1)|Options]) :-
429 memberchk((:- use_module(_)), Old),
430 !.
431update_style(_, Options, Options).
432
433
437
438directive(Options, File-Heads, Directive) :-
439 file_name_extension(File, pl, LibFile),
440 file_name_on_path(LibFile, Lib0),
441 segments(Lib0, Lib),
442 maplist(pi_head, PIs, Heads),
443 make_directive(Lib, PIs, Directive, Options).
444
445segments(Term0, Term) :-
446 Term0 =.. [Alias,Atom],
447 path_segments_atom(Segments, Atom),
448 format(atom(Atom), '~q', [Segments]),
449 !,
450 Term =.. [Alias,Segments].
451segments(FilePL, File) :-
452 atom(FilePL),
453 file_name_extension(File, pl, FilePL),
454 !.
455segments(Term, Term).
456
457:- multifile
458 prolog:no_autoload_module/1. 459
460make_directive(Lib, Import, (:- use_module(Lib, Import)), Options) :-
461 option(directive(use_module/2), Options, use_autoload/2),
462 !.
463make_directive(Lib, _Import, (:- use_module(Lib)), Options) :-
464 option(directive(use_module/1), Options, use_autoload/2),
465 !.
466make_directive(Lib, _Import, (:- use_module(Lib)), Options) :-
467 option(directive(use_autoload/1), Options, use_autoload/2),
468 prolog:no_autoload_module(Lib),
469 !.
470make_directive(Lib, Import, (:- use_module(Lib, Import)), _) :-
471 prolog:no_autoload_module(Lib),
472 !.
473make_directive(Lib, _Import, (:- autoload(Lib)), Options) :-
474 option(directive(use_autoload/1), Options, use_autoload/2),
475 !.
476make_directive(Lib, Import, (:- autoload(Lib, Import)), _).
477
478
479 482
483:- dynamic
484 library_index/3, 485 autoload_directories/1, 486 index_checked_at/1. 487:- volatile
488 library_index/3,
489 autoload_directories/1,
490 index_checked_at/1. 491
497
498noautoload(Head, File) :-
499 functor(Head, Name, Arity),
500 functor(GenHead, Name, Arity),
501 context_module(Here),
502 '$autoload':load_library_index(Here:Name, Arity, Here:noautoload('INDEX')),
503 library_index(GenHead, _, File),
504 !.
505
506
507 510
518
519file_auto_import(File, Options) :-
520 absolute_file_name(File, Path,
521 [ file_type(prolog),
522 access(read)
523 ]),
524 file_autoload_directives(Path, Directives, Options),
525 ( option(backup(Ext), Options)
526 -> file_name_extension(Path, Ext, Old),
527 copy_file_ext(Path, Old)
528 ; true
529 ),
530 Edit = _{import:Directives, done:_},
531 ( has_import(Path)
532 -> edit_file(Old, Path, Edit.put(replace,true))
533 ; edit_file(Old, Path, Edit.put(new,true))
534 ).
535
536:- if(current_predicate(copy_file/2)). 537copy_file_ext(From, To) :-
538 copy_file(From, To).
539:- else. 540copy_file_ext(_From, _To) :-
541 existence_error(predicate, copy_file/2).
542:- endif. 543
544has_import(InFile) :-
545 setup_call_cleanup(
546 prolog_open_source(InFile, In),
547 ( repeat,
548 prolog_read_source_term(In, Term, _Expanded, []),
549 ( Term == end_of_file
550 -> !
551 ; true
552 )
553 ),
554 prolog_close_source(In)),
555 nonvar(Term),
556 import_directive(Term),
557 !.
558
559import_directive((:- use_module(_))).
560import_directive((:- use_module(_, _))).
561
563
564rewrite_term(Never,_,_,_) :-
565 never_rewrite(Never),
566 !,
567 fail.
568rewrite_term(Import,false,[],Options) :-
569 Options.done == true,
570 !,
571 import_directive(Import).
572rewrite_term(In,false,Directives,Options) :-
573 import_directive(In),
574 !,
575 append(Options.import, [nl], Directives),
576 Options.done = true.
577rewrite_term(In,true,Directives,Options) :-
578 In = (:- module(_,_)),
579 Options.get(new) == true,
580 !,
581 append(Options.import, [nl], Directives),
582 Options.done = true.
583
584never_rewrite((:- use_module(_, []))).
585
586edit_file(InFile, OutFile, Options) :-
587 read_file_to_string(InFile, String, []),
588 setup_call_cleanup(
589 prolog_open_source(InFile, In),
590 setup_call_cleanup(
591 open(OutFile, write, Out),
592 rewrite(In, Out, String, Options),
593 close(Out)),
594 prolog_close_source(In)).
595
596rewrite(In, Out, String, Options) :-
597 prolog_read_source_term(
598 In, Term, _Expanded,
599 [ term_position(StartPos),
600 subterm_positions(TermPos),
601 comments(Comments)
602 ]),
603 stream_position_data(char_count, StartPos, StartChar),
604 copy_comments(Comments, StartChar, String, Out),
605 ( Term == end_of_file
606 -> true
607 ; ( nonvar(Term),
608 rewrite_term(Term, Keep, List, Options)
609 -> ( Keep == true
610 -> copy_term_string(TermPos, String, Out)
611 ; true
612 ),
613 forall(member(T, List),
614 output_term(Out, T)),
615 ( append(_, [nl], List)
616 -> skip_blanks(In)
617 ; true
618 )
619 ; copy_term_string(TermPos, String, Out)
620 ),
621 rewrite(In, Out, String, Options)
622 ).
623
624output_term(Out, nl) :-
625 !,
626 nl(Out).
627output_term(Out, Term) :-
628 print_term(Term, [output(Out)]),
629 format(Out, '.~n', []).
630
([Pos-H|T], StartChar, String, Out) :-
632 stream_position_data(char_count, Pos, Start),
633 Start < StartChar,
634 !,
635 string_length(H, Len),
636 sub_string(String, Start, Len, _, Comment),
637 End is Start+Len+1,
638 layout_after(End, String, Layout),
639 format(Out, '~s~s', [Comment, Layout]),
640 copy_comments(T, StartChar, String, Out).
641copy_comments(_, _, _, _).
642
643copy_term_string(TermPos, String, Out) :-
644 arg(1, TermPos, Start),
645 arg(2, TermPos, End),
646 Len is End - Start,
647 sub_string(String, Start, Len, _, TermString),
648 End1 is End + 1,
649 full_stop_after(End1, String, Layout),
650 format(Out, '~s~s', [TermString, Layout]).
651
652layout_after(Index, String, [H|T]) :-
653 string_code(Index, String, H),
654 code_type(H, space),
655 !,
656 Index2 is Index+1,
657 layout_after(Index2, String, T).
658layout_after(_, _, []).
659
660full_stop_after(Index, String, [H|T]) :-
661 string_code(Index, String, H),
662 Index2 is Index+1,
663 ( code_type(H, space)
664 -> !, full_stop_after(Index2, String, T)
665 ; H == 0'.
666 -> !, layout_after(Index2, String, T)
667 ).
668full_stop_after(_, _, []).
669
670skip_blanks(In) :-
671 peek_code(In, C),
672 code_type(C, space),
673 !,
674 get_code(In, _),
675 skip_blanks(In).
676skip_blanks(_)