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, VU University Amsterdam 7 CWI, Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(prolog_deps, 37 [ file_autoload_directives/3, % +File, -Directives, +Options 38 file_auto_import/2 % +File, +Options 39 ]). 40:- use_module(library(apply), [convlist/3, maplist/3]). 41:- use_module(library(filesex), [copy_file/2]). 42:- use_module(library(lists), [select/3, subtract/3, append/3, member/2]). 43:- use_module(library(option), [option/2, option/3]). 44:- use_module(library(pairs), [group_pairs_by_key/2]). 45:- use_module(library(pprint), [print_term/2]). 46:- use_module(library(prolog_code), [pi_head/2]). 47:- use_module(library(prolog_source), 48 [ file_name_on_path/2, 49 path_segments_atom/2, 50 prolog_open_source/2, 51 prolog_read_source_term/4, 52 prolog_close_source/1 53 ]). 54:- use_module(library(prolog_xref), 55 [ xref_source/1, 56 xref_module/2, 57 xref_called/4, 58 xref_defined/3, 59 xref_built_in/1 60 ]). 61:- use_module(library(readutil), [read_file_to_string/3]). 62:- use_module(library(solution_sequences), [distinct/2]).
70:- multifile user:file_search_path/2. 71 72user:file_search_path(noautoload, library(.)). 73user:file_search_path(noautoload, library(semweb)). 74user:file_search_path(noautoload, library(lynx)). 75user:file_search_path(noautoload, library(tipc)). 76user:file_search_path(noautoload, library(cql)). 77user:file_search_path(noautoload, library(http)). 78user:file_search_path(noautoload, library(dcg)). 79user:file_search_path(noautoload, library(unicode)). 80user:file_search_path(noautoload, library(clp)). 81user: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.
117file_autoload_directives(File, Directives, Options) :-
118 xref_source(File),
119 findall(Head, distinct(Head, undefined(File, Head, Options)), Missing),
120 convlist(missing_autoload(File), Missing, Pairs),
121 keysort(Pairs, Pairs1),
122 group_pairs_by_key(Pairs1, Grouped),
123 directives(Grouped, Directives, Options).
131undefined(File, Undef, Options) :- 132 xref_module(File, _), 133 !, 134 xref_called_cond(File, Undef, Cond), 135 \+ ( available(File, Undef, How, Options), 136 How \== plain_file 137 ), 138 included_if_defined(Cond, Undef), 139 Undef \= (_:_). 140undefined(File, Undef, Options) :- 141 xref_called_cond(File, Undef, Cond), 142 \+ available(File, Undef, _, Options), 143 included_if_defined(Cond, Undef), 144 Undef \= (_:_).
148included_if_defined(true, _) :- !. 149included_if_defined(false, _) :- !, fail. 150included_if_defined(fail, _) :- !, fail. 151included_if_defined(current_predicate(Name/Arity), Callable) :- 152 \+ functor(Callable, Name, Arity), 153 !. 154included_if_defined(\+ Cond, Callable) :- 155 !, 156 \+ included_if_defined(Cond, Callable). 157included_if_defined((A,B), Callable) :- 158 !, 159 included_if_defined(A, Callable), 160 included_if_defined(B, Callable). 161included_if_defined((A;B), Callable) :- 162 !, 163 ( included_if_defined(A, Callable) 164 ; included_if_defined(B, Callable) 165 ). 166 167xref_called_cond(Source, Callable, Cond) :- 168 xref_called(Source, Callable, By, Cond), 169 By \= Callable. % recursive calls
175available(File, Called, How, Options) :- 176 xref_defined(File, Called, How0), 177 ( How0 = imported(_) 178 -> option(missing(true), Options) 179 ; true 180 ), 181 !, 182 How = How0. 183available(_, Called, How, _) :- 184 built_in_predicate(Called), 185 !, 186 How = builtin. 187available(_, Called, How, _) :- 188 Called = _:_, 189 defined(_, Called), 190 !, 191 How = module_qualified. 192available(_, M:G, How, _) :- 193 defined(ExportFile, G), 194 xref_module(ExportFile, M), 195 !, 196 How = module_overruled. 197available(_, Called, How, _) :- 198 defined(ExportFile, Called), 199 \+ xref_module(ExportFile, _), 200 !, 201 How == plain_file.
207built_in_predicate(Goal) :-
208 strip_module(Goal, _, Plain),
209 xref_built_in(Plain).
215defined(File, Callable) :- 216 xref_defined(File, Callable, How), 217 How \= imported(_). 218 219 220 /******************************* 221 * GENERATE OUTPUT * 222 *******************************/ 223 224missing_autoload(Src, Head, From-Head) :- 225 xref_defined(Src, Head, imported(From)), 226 !. 227missing_autoload(_Src, Head, File-Head) :- 228 predicate_property(Head, autoload(File0)), 229 !, 230 ( absolute_file_name(File0, File, 231 [ access(read), 232 file_type(prolog), 233 file_errors(fail) 234 ]) 235 -> true 236 ; File = File0 237 ). 238missing_autoload(_Src, Head, File-Head) :- 239 noautoload(Head, File), 240 !. 241missing_autoload(_Src, Head, _) :- 242 pi_head(PI, Head), 243 print_message(warning, 244 error(existence_error(procedure, PI), _)), 245 fail.
update(Old)
.251directives(FileAndHeads, Directives, Options) :- 252 option(update(Old), Options, []), 253 phrase(update_directives(Old, FileAndHeads, RestDeps), Directives, Rest), 254 update_style(Old, Options, Options1), 255 maplist(directive(Options1), RestDeps, Rest0), 256 sort(Rest0, Rest). 257 258update_directives([], Deps, Deps) --> 259 []. 260update_directives([:-(H)|T], Deps0, Deps) --> 261 { update_directive(H, Deps0, Deps1, Directive) }, 262 !, 263 [ :-(Directive) ], 264 update_directives(T, Deps1, Deps). 265update_directives([H|T], Deps0, Deps) --> 266 [ H ], 267 update_directives(T, Deps0, Deps). 268 269update_directive(Dir0, Deps0, Deps, Dir) :- 270 directive_file(Dir0, FileSpec), 271 absolute_file_name(FileSpec, File, 272 [ file_type(prolog), 273 file_errors(fail), 274 access(read) 275 ]), 276 select(DepFile-Heads, Deps0, Deps), 277 same_dep_file(DepFile, File), 278 !, 279 ( Dir0 =.. [Pred,File0,Imports] 280 -> maplist(pi_head, PIs, Heads), 281 subtract(PIs, Imports, New), 282 append(Imports, New, NewImports), 283 Dir =.. [Pred,File0,NewImports] 284 ; Dir = Dir0 285 ). 286 287directive_file(use_module(File), File). 288directive_file(use_module(File,_), File). 289directive_file(autoload(File), File). 290directive_file(autoload(File,_), File). 291 292same_dep_file(File, File) :- 293 !. 294same_dep_file(Dep, _File) :- 295 exists_file(Dep), 296 !, 297 fail. 298same_dep_file(Dep, File) :- 299 user:prolog_file_type(Ext, prolog), 300 file_name_extension(Dep, Ext, DepFile), 301 same_file(DepFile, File), 302 !.
310update_style(_Old, Options, Options) :- 311 option(directive(_), Options), 312 !. 313update_style(Old, Options, [directive(autoload/2)|Options]) :- 314 memberchk((:- autoload(_,_)), Old), 315 !. 316update_style(Old, Options, [directive(autoload/1)|Options]) :- 317 memberchk((:- autoload(_)), Old), 318 !. 319update_style(Old, Options, [directive(use_module/2)|Options]) :- 320 memberchk((:- use_module(_,_)), Old), 321 !. 322update_style(Old, Options, [directive(use_module/1)|Options]) :- 323 memberchk((:- use_module(_)), Old), 324 !. 325update_style(_, Options, Options).
332directive(Options, File-Heads, Directive) :- 333 file_name_extension(File, pl, LibFile), 334 file_name_on_path(LibFile, Lib0), 335 segments(Lib0, Lib), 336 maplist(pi_head, PIs, Heads), 337 make_directive(Lib, PIs, Directive, Options). 338 339segments(Term0, Term) :- 340 Term0 =.. [Alias,Atom], 341 path_segments_atom(Segments, Atom), 342 format(atom(Atom), '~q', [Segments]), 343 !, 344 Term =.. [Alias,Segments]. 345segments(FilePL, File) :- 346 atom(FilePL), 347 file_name_extension(File, pl, FilePL), 348 !. 349segments(Term, Term). 350 351:- multifile 352 prolog:no_autoload_module/1. 353 354make_directive(Lib, Import, (:- use_module(Lib, Import)), Options) :- 355 option(directive(use_module/2), Options, use_autoload/2), 356 !. 357make_directive(Lib, _Import, (:- use_module(Lib)), Options) :- 358 option(directive(use_module/1), Options, use_autoload/2), 359 !. 360make_directive(Lib, _Import, (:- use_module(Lib)), Options) :- 361 option(directive(use_autoload/1), Options, use_autoload/2), 362 prolog:no_autoload_module(Lib), 363 !. 364make_directive(Lib, Import, (:- use_module(Lib, Import)), _) :- 365 prolog:no_autoload_module(Lib), 366 !. 367make_directive(Lib, _Import, (:- autoload(Lib)), Options) :- 368 option(directive(use_autoload/1), Options, use_autoload/2), 369 !. 370make_directive(Lib, Import, (:- autoload(Lib, Import)), _). 371 372 373 /******************************* 374 * NO AUTOLOAD * 375 *******************************/ 376 377:- dynamic 378 library_index/3, % Head x Module x Path 379 autoload_directories/1, % List 380 index_checked_at/1. % Time 381:- volatile 382 library_index/3, 383 autoload_directories/1, 384 index_checked_at/1. 385 386noautoload(Head, File) :- 387 functor(Head, Name, Arity), 388 context_module(Here), 389 '$autoload':load_library_index(Here:Name, Arity, Here:noautoload('INDEX')), 390 library_index(Head, _, File). 391 392 393 /******************************* 394 * REPLACE * 395 *******************************/
405file_auto_import(File, Options) :- 406 absolute_file_name(File, Path, 407 [ file_type(prolog), 408 access(read) 409 ]), 410 file_autoload_directives(Path, Directives, Options), 411 ( option(backup(Ext), Options) 412 -> file_name_extension(Path, Ext, Old), 413 copy_file(Path, Old) 414 ; true 415 ), 416 Edit = _{import:Directives, done:_}, 417 ( has_import(Path) 418 -> edit_file(Old, Path, Edit.put(replace,true)) 419 ; edit_file(Old, Path, Edit.put(new,true)) 420 ). 421 422has_import(InFile) :- 423 setup_call_cleanup( 424 prolog_open_source(InFile, In), 425 ( repeat, 426 prolog_read_source_term(In, Term, _Expanded, []), 427 ( Term == end_of_file 428 -> ! 429 ; true 430 ) 431 ), 432 prolog_close_source(In)), 433 nonvar(Term), 434 import_directive(Term), 435 !. 436 437import_directive((:- use_module(_))). 438import_directive((:- use_module(_, _))).
442rewrite_term(Never,_,_,_) :- 443 never_rewrite(Never), 444 !, 445 fail. 446rewrite_term(Import,false,[],Options) :- 447 Options.done == true, 448 !, 449 import_directive(Import). 450rewrite_term(In,false,Directives,Options) :- 451 import_directive(In), 452 !, 453 append(Options.import, [nl], Directives), 454 Options.done = true. 455rewrite_term(In,true,Directives,Options) :- 456 In = (:- module(_,_)), 457 Options.get(new) == true, 458 !, 459 append(Options.import, [nl], Directives), 460 Options.done = true. 461 462never_rewrite((:- use_module(_, []))). 463 464edit_file(InFile, OutFile, Options) :- 465 read_file_to_string(InFile, String, []), 466 setup_call_cleanup( 467 prolog_open_source(InFile, In), 468 setup_call_cleanup( 469 open(OutFile, write, Out), 470 rewrite(In, Out, String, Options), 471 close(Out)), 472 prolog_close_source(In)). 473 474rewrite(In, Out, String, Options) :- 475 prolog_read_source_term( 476 In, Term, _Expanded, 477 [ term_position(StartPos), 478 subterm_positions(TermPos), 479 comments(Comments) 480 ]), 481 stream_position_data(char_count, StartPos, StartChar), 482 copy_comments(Comments, StartChar, String, Out), 483 ( Term == end_of_file 484 -> true 485 ; ( nonvar(Term), 486 rewrite_term(Term, Keep, List, Options) 487 -> ( Keep == true 488 -> copy_term_string(TermPos, String, Out) 489 ; true 490 ), 491 forall(member(T, List), 492 output_term(Out, T)), 493 ( append(_, [nl], List) 494 -> skip_blanks(In) 495 ; true 496 ) 497 ; copy_term_string(TermPos, String, Out) 498 ), 499 rewrite(In, Out, String, Options) 500 ). 501 502output_term(Out, nl) :- 503 !, 504 nl(Out). 505output_term(Out, Term) :- 506 print_term(Term, [output(Out)]), 507 format(Out, '.~n', []). 508 509copy_comments([Pos-H|T], StartChar, String, Out) :- 510 stream_position_data(char_count, Pos, Start), 511 Start < StartChar, 512 !, 513 string_length(H, Len), 514 sub_string(String, Start, Len, _, Comment), 515 End is Start+Len+1, 516 layout_after(End, String, Layout), 517 format(Out, '~s~s', [Comment, Layout]), 518 copy_comments(T, StartChar, String, Out). 519copy_comments(_, _, _, _). 520 521copy_term_string(TermPos, String, Out) :- 522 arg(1, TermPos, Start), 523 arg(2, TermPos, End), 524 Len is End - Start, 525 sub_string(String, Start, Len, _, TermString), 526 End1 is End + 1, 527 full_stop_after(End1, String, Layout), 528 format(Out, '~s~s', [TermString, Layout]). 529 530layout_after(Index, String, [H|T]) :- 531 string_code(Index, String, H), 532 code_type(H, space), 533 !, 534 Index2 is Index+1, 535 layout_after(Index2, String, T). 536layout_after(_, _, []). 537 538full_stop_after(Index, String, [H|T]) :- 539 string_code(Index, String, H), 540 Index2 is Index+1, 541 ( code_type(H, space) 542 -> !, full_stop_after(Index2, String, T) 543 ; H == 0'. 544 -> !, layout_after(Index2, String, T) 545 ). 546full_stop_after(_, _, []). 547 548skip_blanks(In) :- 549 peek_code(In, C), 550 code_type(C, space), 551 !, 552 get_code(In, _), 553 skip_blanks(In). 554skip_blanks(_)
Compute file dependencies
This module computes file dependencies for modules as a set of directives. */