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-2024, 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:- 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]).
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))).
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.
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).
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 \= (_:_).
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. % recursive calls
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.
209built_in_predicate(Goal) :-
210 strip_module(Goal, _, Plain),
211 xref_built_in(Plain).
217defined(File, Callable) :- 218 xref_defined(File, Callable, How), 219 How \= imported(_). 220 221 222 /******************************* 223 * GENERATE OUTPUT * 224 *******************************/ 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.
update(Old)
.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.
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.
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.
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).
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 /******************************* 455 * NO AUTOLOAD * 456 *******************************/ 457 458:- dynamic 459 library_index/3, % Head x Module x Path 460 autoload_directories/1, % List 461 index_checked_at/1. % Time 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 /******************************* 475 * REPLACE * 476 *******************************/
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(_, _))).
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 590copy_comments([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(_)
Compute file dependencies
This module computes file dependencies for modules as a set of directives. */