1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2020-2026, VU University Amsterdam 7 CWI, Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(prolog_deps, 38 [ file_autoload_directives/3, % +File, -Directives, +Options 39 file_auto_import/2 % +File, +Options 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]).
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))).
true (default false), only generate directives
for called predicates that are not already imported.
If no directive(+Directive) option is provided a
default is determined from the given directives.
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).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 \= (_:_).
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. % recursive calls
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.
214built_in_predicate(Goal) :-
215 strip_module(Goal, _, Plain),
216 xref_built_in(Plain).
222defined(File, Callable) :-
223 xref_defined(File, Callable, How),
224 How \= imported(_).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 /******************************* 245 * GENERATE OUTPUT * 246 *******************************/ 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.
update(Old).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.
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.
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.
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).
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 /******************************* 479 * NO AUTOLOAD * 480 *******************************/ 481 482:- dynamic 483 library_index/3, % Head x Module x Path 484 autoload_directories/1, % List 485 index_checked_at/1. % Time 486:- volatile 487 library_index/3, 488 autoload_directories/1, 489 index_checked_at/1.
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 /******************************* 507 * REPLACE * 508 *******************************/
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(_, _))).
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 630copy_comments([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(_)
Compute file dependencies
This module computes file dependencies for modules as a set of directives. */