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) 1985-2025, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module('$autoload', 39 [ '$find_library'/5, 40 '$in_library'/3, 41 '$define_predicate'/1, 42 '$update_library_index'/1, % +Options 43 '$autoload'/1, 44 45 make_library_index/1, 46 make_library_index/2, 47 reload_library_index/0, 48 autoload_path/1, 49 50 autoload/1, % +File 51 autoload/2, % +File, +Imports 52 53 require/1 % +Predicates 54 ]). 55 56:- meta_predicate 57 '$autoload'(:), 58 autoload(:), 59 autoload(:, +), 60 require(:). 61 62:- dynamic 63 library_index/3, % Head x Module x Path 64 autoload_directories/1, % List 65 index_checked_at/1. % Time 66:- volatile 67 library_index/3, 68 autoload_directories/1, 69 index_checked_at/1. 70 71user:file_search_path(autoload, swi(library)). 72user:file_search_path(autoload, pce(prolog/lib)). 73user:file_search_path(autoload, app_config(lib)). 74user:file_search_path(autoload, Dir) :- 75 '$ext_library_directory'(Dir). 76 77:- create_prolog_flag(warn_autoload, false, []).
87'$find_library'(_Module, :, 2, _LoadModule, _Library) :- 88 !, fail. 89'$find_library'(Module, Name, Arity, LoadModule, Library) :- 90 load_library_index(Name, Arity), 91 functor(Head, Name, Arity), 92 ( library_index(Head, Module, Library), 93 LoadModule = Module 94 ; library_index(Head, LoadModule, Library) 95 ), 96 !.
103'$in_library'(Name, Arity, Path) :- 104 atom(Name), integer(Arity), 105 !, 106 Name/Arity \= (:)/2, 107 load_library_index(Name, Arity), 108 functor(Head, Name, Arity), 109 library_index(Head, _, Path). 110'$in_library'(Name, Arity, Path) :- 111 load_library_index(Name, Arity), 112 library_index(Head, _, Path), 113 Head \= _:_, 114 functor(Head, Name, Arity).
121:- meta_predicate 122 '$define_predicate'(:). 123 124'$define_predicate'(Head) :- 125 '$defined_predicate'(Head), 126 !. 127'$define_predicate'(Term) :- 128 Term = Module:Head, 129 ( compound(Head) 130 -> compound_name_arity(Head, Name, Arity) 131 ; Name = Head, Arity = 0 132 ), 133 '$undefined_procedure'(Module, Name, Arity, retry). 134 135 136 /******************************** 137 * UPDATE INDEX * 138 ********************************/ 139 140:- thread_local 141 silent/0.
false
.true
.155'$update_library_index'(Options) :- 156 setof(Dir, writable_indexed_directory(Dir, Options), Dirs), 157 !, 158 setup_call_cleanup( 159 asserta(silent, Ref), 160 guarded_make_library_index(Dirs), 161 erase(Ref)), 162 ( flag('$modified_index', true, false) 163 -> reload_library_index 164 ; true 165 ). 166'$update_library_index'(_). 167 168guarded_make_library_index([]). 169guarded_make_library_index([Dir|Dirs]) :- 170 ( catch(make_library_index(Dir), E, 171 print_message(error, E)) 172 -> true 173 ; print_message(warning, goal_failed(make_library_index(Dir))) 174 ), 175 guarded_make_library_index(Dirs).
182writable_indexed_directory(Dir, Options) :- 183 current_prolog_flag(home, Home), 184 writable_indexed_directory(Dir), 185 ( sub_atom(Dir, 0, _, _, Home) 186 -> '$option'(system(true), Options, false) 187 ; '$option'(user(true), Options, true) 188 ). 189 190writable_indexed_directory(Dir) :- 191 index_file_name(IndexFile, autoload('INDEX'), [access([read,write])]), 192 file_directory_name(IndexFile, Dir). 193writable_indexed_directory(Dir) :- 194 absolute_file_name(library('MKINDEX'), 195 [ file_type(prolog), 196 access(read), 197 solutions(all), 198 file_errors(fail) 199 ], MkIndexFile), 200 file_directory_name(MkIndexFile, Dir), 201 plfile_in_dir(Dir, 'INDEX', _, IndexFile), 202 access_file(IndexFile, write). 203 204 205 /******************************** 206 * LOAD INDEX * 207 ********************************/
213reload_library_index :- 214 context_module(M), 215 reload_library_index(M). 216 217reload_library_index(M) :- 218 with_mutex('$autoload', clear_library_index(M)). 219 220clear_library_index(M) :- 221 retractall(M:library_index(_, _, _)), 222 retractall(M:autoload_directories(_)), 223 retractall(M:index_checked_at(_)).
233:- meta_predicate load_library_index(?, ?, :). 234:- public load_library_index/3. 235 236load_library_index(Name, Arity) :- 237 load_library_index(Name, Arity, autoload('INDEX')). 238 239load_library_index(Name, Arity, M:_Spec) :- 240 atom(Name), integer(Arity), 241 functor(Head, Name, Arity), 242 M:library_index(Head, _, _), 243 !. 244load_library_index(_, _, Spec) :- 245 notrace(with_mutex('$autoload', load_library_index_p(Spec))). 246 247load_library_index_p(M:_) :- 248 M:index_checked_at(Time), 249 get_time(Now), 250 Now-Time < 60, 251 !. 252load_library_index_p(M:Spec) :- 253 findall(Index, index_file_name(Index, Spec, [access(read)]), List0), 254 '$list_to_set'(List0, List), 255 retractall(M:index_checked_at(_)), 256 get_time(Now), 257 assert(M:index_checked_at(Now)), 258 ( M:autoload_directories(List) 259 -> true 260 ; retractall(M:library_index(_, _, _)), 261 retractall(M:autoload_directories(_)), 262 read_index(List, M), 263 assert(M:autoload_directories(List)) 264 ).
autoload
.
274index_file_name(IndexFile, FileSpec, Options) :- 275 absolute_file_name(FileSpec, 276 IndexFile, 277 [ file_type(prolog), 278 solutions(all), 279 file_errors(fail) 280 | Options 281 ]). 282 283read_index([], _) :- !. 284read_index([H|T], M) :- 285 !, 286 read_index(H, M), 287 read_index(T, M). 288read_index(Index, M) :- 289 print_message(silent, autoload(read_index(Dir))), 290 file_directory_name(Index, Dir), 291 setup_call_cleanup( 292 '$push_input_context'(autoload_index), 293 setup_call_cleanup( 294 open(Index, read, In), 295 read_index_from_stream(Dir, In, M), 296 close(In)), 297 '$pop_input_context'). 298 299read_index_from_stream(Dir, In, M) :- 300 repeat, 301 read(In, Term), 302 assert_index(Term, Dir, M), 303 !. 304 305assert_index(end_of_file, _, _) :- !. 306assert_index(index(Term, Module, File), Dir, M) :- 307 !, 308 atomic_list_concat([Dir, '/', File], Path), 309 assertz(M:library_index(Term, Module, Path)), 310 fail. 311assert_index(index(Name, Arity, Module, File), Dir, M) :- 312 !, % Read old index format 313 functor(Head, Name, Arity), 314 head_meta_any(Head), 315 assert_index(index(Head, Module, File), Dir, M). 316assert_index(Term, Dir, _) :- 317 print_message(error, illegal_autoload_index(Dir, Term)), 318 fail. 319 320 321 /******************************** 322 * CREATE INDEX.pl * 323 ********************************/
INDEX.pl
. In Dir contains a file
MKINDEX.pl
, this file is loaded and we assume that the index is
created by directives that appearin this file. Otherwise, all
source files are scanned for their module-header and all
exported predicates are added to the autoload index.
336make_library_index(Dir0) :- 337 forall(absolute_file_name(Dir0, Dir, 338 [ expand(true), 339 file_type(directory), 340 file_errors(fail), 341 solutions(all) 342 ]), 343 make_library_index2(Dir)). 344 345make_library_index2(Dir) :- 346 plfile_in_dir(Dir, 'MKINDEX', _MkIndex, AbsMkIndex), 347 access_file(AbsMkIndex, read), 348 !, 349 load_files(user:AbsMkIndex, [silent(true)]). 350make_library_index2(Dir) :- 351 findall(Pattern, source_file_pattern(Pattern), PatternList), 352 make_library_index2(Dir, PatternList).
INDEX.pl
for Dir by scanning all files
that match any of the file-patterns in Patterns. Typically, this
appears as a directive in MKINDEX.pl
. For example:
:- prolog_load_context(directory, Dir), make_library_index(Dir, ['*.pl']).
367make_library_index(Dir0, Patterns) :- 368 forall(absolute_file_name(Dir0, Dir, 369 [ expand(true), 370 file_type(directory), 371 file_errors(fail), 372 solutions(all) 373 ]), 374 make_library_index2(Dir, Patterns)). 375 376make_library_index2(Dir, Patterns) :- 377 plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex), 378 ensure_slash(Dir, DirS), 379 pattern_files(Patterns, DirS, Files), 380 ( library_index_out_of_date(Dir, AbsIndex, Files) 381 -> do_make_library_index(AbsIndex, DirS, Files), 382 set_flag('$modified_index', true) 383 ; true 384 ). 385 386ensure_slash(Dir, DirS) :- 387 ( sub_atom(Dir, _, _, 0, /) 388 -> DirS = Dir 389 ; atom_concat(Dir, /, DirS) 390 ). 391 392source_file_pattern(Pattern) :- 393 user:prolog_file_type(PlExt, prolog), 394 PlExt \== qlf, 395 atom_concat('*.', PlExt, Pattern). 396 397plfile_in_dir(Dir, Base, PlBase, File) :- 398 file_name_extension(Base, pl, PlBase), 399 atomic_list_concat([Dir, '/', PlBase], File). 400 401pattern_files([], _, []). 402pattern_files([H|T], DirS, Files) :- 403 atom_concat(DirS, H, P0), 404 expand_file_name(P0, Files0), 405 '$append'(Files0, Rest, Files), 406 pattern_files(T, DirS, Rest). 407 408library_index_out_of_date(_Dir, Index, _Files) :- 409 \+ exists_file(Index), 410 !. 411library_index_out_of_date(Dir, Index, Files) :- 412 time_file(Index, IndexTime), 413 ( time_file(Dir, DotTime), 414 DotTime - IndexTime > 0.001 % compensate for jitter 415 ; '$member'(File, Files), % and rounding 416 time_file(File, FileTime), 417 FileTime - IndexTime > 0.001 418 ), 419 !. 420 421 422do_make_library_index(Index, Dir, Files) :- 423 ensure_slash(Dir, DirS), 424 '$stage_file'(Index, StagedIndex), 425 setup_call_catcher_cleanup( 426 open(StagedIndex, write, Out), 427 ( print_message(informational, make(library_index(Dir))), 428 index_header(Out), 429 index_files(Files, DirS, Out) 430 ), 431 Catcher, 432 install_index(Out, Catcher, StagedIndex, Index)). 433 434install_index(Out, Catcher, StagedIndex, Index) :- 435 catch(close(Out), Error, true), 436 ( silent 437 -> OnError = silent 438 ; OnError = error 439 ), 440 ( var(Error) 441 -> TheCatcher = Catcher 442 ; TheCatcher = exception(Error) 443 ), 444 '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
450index_files([], _, _). 451index_files([File|Files], DirS, Fd) :- 452 ( catch(exports(File, Module, Exports, Meta, Public), E, 453 print_message(warning, E)), 454 nonvar(Module) 455 -> atom_concat(DirS, Local, File), 456 file_name_extension(Base, _, Local), 457 forall(index_term(Exports, Meta, Public, Term), 458 format(Fd, 'index(~k, ~k, ~k).~n', 459 [Term, Module, Base])) 460 ; true 461 ), 462 index_files(Files, DirS, Fd). 463 464index_term(Exports, Meta, _Public, Term) :- 465 '$member'(Export, Exports), 466 ground(Export), 467 export_term(Export, Meta, Term). 468index_term(_Exports, _Meta, Publics, (public):Head) :- 469 '$member'(Public, Publics), 470 '$pi_head'(Public, Head). 471 472export_term(Op, _Meta, Term) :- 473 Op = op(_Pri,_Type,_Name), 474 !, 475 Term = op:Op. 476export_term(PI, Meta, Head) :- 477 '$pi_head'(PI, Head), 478 ( '$member'(Head, Meta) 479 -> true 480 ; head_meta_any(Head) 481 ). 482 483head_meta_any(Head) :- 484 ( atom(Head) 485 -> true 486 ; compound_name_arguments(Head, _, Args), 487 meta_any(Args) 488 ). 489 490meta_any([]). 491meta_any([?|T]) :- 492 meta_any(T). 493 494index_header(Fd):- 495 format(Fd, '/* Creator: make/0~n~n', []), 496 format(Fd, ' Purpose: Provide index for autoload~n', []), 497 format(Fd, '*/~n~n', []).
507:- public exports/3. % using by library(prolog_deps). 508exports(File, Module, Exports) :- 509 exports(File, Module, Exports, _Meta, _Public). 510 511exports(File, Module, Exports, Meta, Public) :- 512 ( current_prolog_flag(xref, Old) 513 -> true 514 ; Old = false 515 ), 516 setup_call_cleanup( 517 set_prolog_flag(xref, true), 518 snapshot(exports_(File, Module, Exports, Meta, Public)), 519 set_prolog_flag(xref, Old)). 520 521exports_(File, Module, Exports, Meta, Public) :- 522 State = state(true, _, [], [], []), 523 ( '$source_term'(File, 524 _Read,_RLayout, 525 Term,_TermLayout, 526 _Stream, 527 [ syntax_errors(quiet) 528 ]), 529 ( Term = (:- module(M,ModuleExports)), 530 is_list(ModuleExports), 531 arg(1, State, true) 532 -> nb_setarg(1, State, false), 533 nb_setarg(2, State, M), 534 nb_setarg(3, State, ModuleExports), 535 fail 536 ; nb_setarg(1, State, false), 537 fail 538 ; Term = (:- export(Export)) 539 -> phrase(export_pi(Export), PIs), 540 arg(3, State, E0), 541 '$append'(E0, PIs, E1), 542 nb_setarg(3, State, E1), 543 fail 544 ; Term = (:- public(Public)) 545 -> phrase(export_pi(Public), PIs), 546 arg(5, State, E0), 547 '$append'(E0, PIs, E1), 548 nb_setarg(5, State, E1), 549 fail 550 ; Term = (:- meta_predicate(Heads)), 551 phrase(meta(Heads), M1), 552 arg(4, State, M0), 553 '$append'(M0, M1, M2), 554 nb_setarg(4, State, M2) 555 ; Term = (:- use_foreign_library(Lib)), 556 nonvar(Lib), 557 arg(2, State, M), 558 atom(M) 559 -> catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true), 560 fail 561 ; Term = (:- Directive), 562 nonvar(Directive) 563 -> fail 564 ; Term == [] % Expansion for conditionals 565 -> fail 566 ; ! 567 ) 568 ; true 569 ), 570 arg(2, State, Module), 571 arg(3, State, Exports), 572 arg(4, State, Meta), 573 arg(5, State, Public). 574 575export_pi(Var) --> 576 { var(Var) }, 577 !. 578export_pi((A,B)) --> 579 !, 580 export_pi(A), 581 export_pi(B). 582export_pi(PI) --> 583 { ground(PI) }, 584 [PI]. 585 586meta(Var) --> 587 { var(Var) }, 588 !. 589meta((A,B)) --> 590 !, 591 meta(A), 592 meta(B). 593meta(Head) --> 594 { callable(Head) }, 595 [Head]. 596 597 598 /******************************* 599 * EXTENDING * 600 *******************************/
autoload
and reloads the library
index. For example:
:- autoload_path(library(http)).
If this call appears as a directive, it is term-expanded into a clause for file_search_path/2 and a directive calling reload_library_index/0. This keeps source information and allows for removing this directive.
617autoload_path(Alias) :- 618 ( user:file_search_path(autoload, Alias) 619 -> true 620 ; assertz(user:file_search_path(autoload, Alias)), 621 reload_library_index 622 ). 623 624systemterm_expansion((:- autoload_path(Alias)), 625 [ user:file_search_path(autoload, Alias), 626 (:- reload_library_index) 627 ]). 628 629 630 /******************************* 631 * RUNTIME AUTOLOADER * 632 *******************************/
current_prolog_flag(autoload, true)
holds.642'$autoload'(PI) :- 643 source_location(File, _Line), 644 !, 645 setup_call_cleanup( 646 '$start_aux'(File, Context), 647 '$autoload2'(PI), 648 '$end_aux'(File, Context)). 649'$autoload'(PI) :- 650 '$autoload2'(PI). 651 652'$autoload2'(PI) :- 653 setup_call_cleanup( 654 leave_sandbox(Old), 655 '$autoload3'(PI), 656 restore_sandbox(Old)). 657 658leave_sandbox(Sandboxed) :- 659 current_prolog_flag(sandboxed_load, Sandboxed), 660 set_prolog_flag(sandboxed_load, false). 661restore_sandbox(Sandboxed) :- 662 set_prolog_flag(sandboxed_load, Sandboxed). 663 664'$autoload3'(PI) :- 665 autoload_from(PI, LoadModule, FullFile), 666 do_autoload(FullFile, PI, LoadModule).
673autoload_from(Module:PI, LoadModule, FullFile) :- 674 autoload_in(Module, explicit), 675 current_autoload(Module:File, Ctx, import(Imports)), 676 memberchk(PI, Imports), 677 library_info(File, Ctx, FullFile, LoadModule, Exports), 678 ( pi_in_exports(PI, Exports) 679 -> ! 680 ; autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)), 681 fail 682 ). 683autoload_from(Module:Name/Arity, LoadModule, FullFile) :- 684 autoload_in(Module, explicit), 685 PI = Name/Arity, 686 current_autoload(Module:File, Ctx, all), 687 library_info(File, Ctx, FullFile, LoadModule, Exports), 688 pi_in_exports(PI, Exports). 689autoload_from(Module:Name/Arity, LoadModule, Library) :- 690 autoload_in(Module, general), 691 '$find_library'(Module, Name, Arity, LoadModule, Library). 692 693:- public autoload_in/2. % used in syspred 694 695autoload_in(Module, How) :- 696 current_prolog_flag(autoload, AutoLoad), 697 autoload_in(AutoLoad, How, Module), 698 !.
702autoload_in(true, _, _). 703autoload_in(explicit, explicit, _). 704autoload_in(user, _, user). 705autoload_in(user_or_explicit, explicit, _). 706autoload_in(user_or_explicit, _, user).
user
. '$c_current_predicate'/2
verifies the predicate really exists, but doesn't validate
that it is defined.725do_autoload(Library, Module:Name/Arity, LoadModule) :- 726 functor(Head, Name, Arity), 727 '$update_autoload_level'([autoload(true)], Old), 728 verbose_autoload(Module:Name/Arity, Library), 729 loadable_file(Library, File), 730 '$compilation_mode'(OldComp, database), 731 ( Module == LoadModule 732 -> ensure_loaded(Module:File) 733 ; ( '$c_current_predicate'(_, LoadModule:Head), 734 '$get_predicate_attribute'(LoadModule:Head, defined, 1), 735 \+ '$loading'(Library) 736 -> Module:import(LoadModule:Name/Arity) 737 ; use_module(Module:File, [Name/Arity]) 738 ), 739 warn_autoload(Module, LoadModule:Name/Arity) 740 ), 741 '$set_compilation_mode'(OldComp), 742 '$set_autoload_level'(Old), 743 '$c_current_predicate'(_, Module:Head). 744 745loadable_file(PlFile, File) :- 746 exists_file(PlFile), !, 747 File = PlFile. 748loadable_file(PlFile, Base) :- 749 file_name_extension(Base, pl, PlFile), 750 !. 751loadable_file(File, File). 752 753verbose_autoload(PI, Library) :- 754 current_prolog_flag(verbose_autoload, true), 755 !, 756 set_prolog_flag(verbose_autoload, false), 757 print_message(informational, autoload(PI, Library)), 758 set_prolog_flag(verbose_autoload, true). 759verbose_autoload(PI, Library) :- 760 print_message(silent, autoload(PI, Library)).
autoload(File)
. The module must be
instantiated.769:- public % used from predicate_property/2 770 autoloadable/2. 771 772autoloadable(M:Head, FullFile) :- 773 atom(M), 774 current_module(M), 775 autoload_in(M, explicit), 776 ( callable(Head) 777 -> goal_name_arity(Head, Name, Arity), 778 autoload_from(M:Name/Arity, _, FullFile) 779 ; findall((M:H)-F, autoloadable_2(M:H, F), Pairs), 780 ( '$member'(M:Head-FullFile, Pairs) 781 ; current_autoload(M:File, Ctx, all), 782 library_info(File, Ctx, FullFile, _, Exports), 783 '$member'(PI, Exports), 784 '$pi_head'(PI, Head), 785 \+ memberchk(M:Head-_, Pairs) 786 ) 787 ). 788autoloadable(M:Head, FullFile) :- 789 ( var(M) 790 -> autoload_in(any, general) 791 ; autoload_in(M, general) 792 ), 793 ( callable(Head) 794 -> goal_name_arity(Head, Name, Arity), 795 ( '$find_library'(_, Name, Arity, _, FullFile) 796 -> true 797 ) 798 ; '$in_library'(Name, Arity, autoload), 799 functor(Head, Name, Arity) 800 ). 801 802 803autoloadable_2(M:Head, FullFile) :- 804 current_autoload(M:File, Ctx, import(Imports)), 805 library_info(File, Ctx, FullFile, _LoadModule, _Exports), 806 '$member'(PI, Imports), 807 '$pi_head'(PI, Head). 808 809goal_name_arity(Head, Name, Arity) :- 810 compound(Head), 811 !, 812 compound_name_arity(Head, Name, Arity). 813goal_name_arity(Head, Head, 0).
826library_info(Spec, _, FullFile, Module, Exports) :- 827 '$resolved_source_path'(Spec, FullFile, []), 828 !, 829 ( \+ '$loading_file'(FullFile, _Queue, _LoadThread) 830 -> '$current_module'(Module, FullFile), 831 '$module_property'(Module, exports(Exports)) 832 ; library_info_from_file(FullFile, _, Module, Exports) 833 ). 834library_info(Spec, Context, FullFile, Module, Exports) :- 835 ( Context = (Path:_Line) 836 -> Extra = [relative_to(Path)] 837 ; Extra = [] 838 ), 839 ( absolute_file_name(Spec, AbsFile, 840 [ file_type(prolog), 841 access(read), 842 file_errors(fail) 843 | Extra 844 ]) 845 -> library_info_from_file(AbsFile, FullFile, Module, Exports), 846 '$register_resolved_source_path'(Spec, FullFile) 847 ; absolute_file_name(Spec, FullFile, 848 [ file_type(prolog), 849 solutions(all), 850 file_errors(fail) 851 | Extra 852 ]), 853 source_file(FullFile), 854 '$current_module'(Module, FullFile) 855 -> '$module_property'(Module, exports(Exports)) 856 ; autoload_error(Context, no_file(Spec)), 857 fail 858 ). 859 860library_info_from_file(QlfFile, PlFile, Module, Exports) :- 861 file_name_extension(_, qlf, QlfFile), 862 !, 863 '$qlf_module'(QlfFile, Info), 864 _{module:Module, exports:Exports, file:PlFile} :< Info. 865library_info_from_file(PlFile, PlFile, Module, Exports) :- 866 setup_call_cleanup( 867 '$set_source_module'(OldModule, system), 868 setup_call_cleanup( 869 '$open_source'(PlFile, In, State, [], []), 870 '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream, 871 [PlFile], []), 872 '$close_source'(State, true)), 873 '$set_source_module'(OldModule)), 874 ( Term = (:- module(Module, Exports)) 875 -> ! 876 ; nonvar(Term), 877 skip_header(Term) 878 -> fail 879 ; '$domain_error'(module_header, Term) 880 ). 881 882skip_header(begin_of_file). 883 884 885:- dynamic printed/3. 886:- volatile printed/3. 887 888autoload_error(Context, Error) :- 889 suppress(Context, Error), 890 !. 891autoload_error(Context, Error) :- 892 get_time(Now), 893 assertz(printed(Context, Error, Now)), 894 print_message(warning, error(autoload(Error), autoload(Context))). 895 896suppress(Context, Error) :- 897 printed(Context, Error, Printed), 898 get_time(Now), 899 ( Now - Printed < 1 900 -> true 901 ; retractall(printed(Context, Error, _)), 902 fail 903 ). 904 905 906 /******************************* 907 * CALLBACK * 908 *******************************/ 909 910:- public 911 set_autoload/1.
false
we should materialize all registered
requests for autoloading. We must do so before disabling autoloading
as loading the files may require autoloading.920set_autoload(FlagValue) :- 921 current_prolog_flag(autoload, FlagValue), 922 !. 923set_autoload(FlagValue) :- 924 \+ autoload_in(FlagValue, explicit, any), 925 !, 926 setup_call_cleanup( 927 nb_setval('$autoload_disabling', true), 928 materialize_autoload(Count), 929 nb_delete('$autoload_disabling')), 930 print_message(informational, autoload(disabled(Count))). 931set_autoload(_). 932 933materialize_autoload(Count) :- 934 State = state(0), 935 forall(current_predicate(M:'$autoload'/3), 936 materialize_autoload(M, State)), 937 arg(1, State, Count). 938 939materialize_autoload(M, State) :- 940 ( current_autoload(M:File, Context, Import), 941 library_info(File, Context, PlFile, _LoadModule, _Exports), 942 arg(1, State, N0), 943 N is N0+1, 944 nb_setarg(1, State, N), 945 loadable_file(PlFile, LoadFile), 946 ( Import == all 947 -> verbose_autoload(M:all, PlFile), 948 use_module(M:LoadFile) 949 ; Import = import(Preds) 950 -> verbose_autoload(M:Preds, PlFile), 951 use_module(M:LoadFile, Preds) 952 ), 953 fail 954 ; true 955 ), 956 abolish(M:'$autoload'/3). 957 958 959 /******************************* 960 * AUTOLOAD/2 * 961 *******************************/ 962 963autoload(M:File) :- 964 ( \+ autoload_in(M, explicit) 965 ; nb_current('$autoload_disabling', true) 966 ), 967 !, 968 use_module(M:File). 969autoload(M:File) :- 970 '$must_be'(filespec, File), 971 source_context(Context), 972 ( current_autoload(M:File, _, import(all)) 973 -> true 974 ; assert_autoload(M:'$autoload'(File, Context, all)) 975 ). 976 977autoload(M:File, Imports) :- 978 ( \+ autoload_in(M, explicit) 979 ; nb_current('$autoload_disabling', true) 980 ), 981 !, 982 use_module(M:File, Imports). 983autoload(M:File, Imports0) :- 984 '$must_be'(filespec, File), 985 valid_imports(Imports0, Imports), 986 source_context(Context), 987 register_autoloads(Imports, M, File, Context), 988 ( current_autoload(M:File, _, import(Imports)) 989 -> true 990 ; assert_autoload(M:'$autoload'(File, Context, import(Imports))) 991 ). 992 993source_context(Path:Line) :- 994 source_location(Path, Line), 995 !. 996source_context(-). 997 998assert_autoload(Clause) :- 999 '$initialization_context'(Source, Ctx), 1000 '$store_admin_clause2'(Clause, _Layout, Source, Ctx). 1001 1002valid_imports(Imports0, Imports) :- 1003 '$must_be'(list, Imports0), 1004 valid_import_list(Imports0, Imports). 1005 1006valid_import_list([], []). 1007valid_import_list([H0|T0], [H|T]) :- 1008 '$pi_head'(H0, Head), 1009 '$pi_head'(H, Head), 1010 valid_import_list(T0, T).
autoload
flag on all predicates declared using autoload/2
to prevent duplicates or the user defining the same predicate.
1019register_autoloads([], _, _, _). 1020register_autoloads([PI|T], Module, File, Context) :- 1021 PI = Name/Arity, 1022 functor(Head, Name, Arity), 1023 ( '$get_predicate_attribute'(Module:Head, autoload, 1) 1024 -> ( current_autoload(Module:_File0, _Ctx0, import(Imports)), 1025 memberchk(PI, Imports) 1026 -> '$permission_error'(redefine, imported_procedure, PI), 1027 fail 1028 ; Done = true 1029 ) 1030 ; '$c_current_predicate'(_, Module:Head), % no auto-import 1031 '$get_predicate_attribute'(Module:Head, imported, From) 1032 -> ( ( '$resolved_source_path'(File, FullFile) 1033 -> true 1034 ; '$resolve_source_path'(File, FullFile, []) 1035 ), 1036 module_property(From, file(FullFile)) 1037 -> Done = true 1038 ; print_message(warning, 1039 autoload(already_defined(Module:PI, From))), 1040 Done = true 1041 ) 1042 ; true 1043 ), 1044 ( Done == true 1045 -> true 1046 ; '$set_predicate_attribute'(Module:Head, autoload, 1) 1047 ), 1048 register_autoloads(T, Module, File, Context). 1049 1050pi_in_exports(PI, Exports) :- 1051 '$member'(E, Exports), 1052 canonical_pi(E, PI), 1053 !. 1054 1055canonical_pi(Var, _) :- 1056 var(Var), !, fail. 1057canonical_pi(Name/Arity, Name/Arity). 1058canonical_pi(Name//A0, Name/Arity) :- 1059 Arity is A0 + 2. 1060 1061current_autoload(M:File, Context, Term) :- 1062 '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1), 1063 M:'$autoload'(File, Context, Term). 1064 1065 /******************************* 1066 * CHECK * 1067 *******************************/
1073warn_autoload(TargetModule, PI) :- 1074 current_prolog_flag(warn_autoload, true), 1075 \+ current_prolog_flag(xref, true), 1076 \+ nb_current('$autoload_warning', true), 1077 \+ nowarn_autoload(TargetModule, PI), 1078 '$pi_head'(PI, Head), 1079 source_file(Head, File), 1080 expansion_hook(P), 1081 source_file(P, File), 1082 !, 1083 setup_call_cleanup( 1084 b_setval('$autoload_warning', true), 1085 print_message(warning, 1086 deprecated(autoload(TargetModule, File, PI, expansion))), 1087 nb_delete('$autoload_warning')). 1088warn_autoload(_, _). 1089 1090expansion_hook(user:goal_expansion(_,_)). 1091expansion_hook(user:goal_expansion(_,_,_,_)). 1092expansion_hook(system:goal_expansion(_,_)). 1093expansion_hook(system:goal_expansion(_,_,_,_)).
1108nowarn_autoload(TargetModule, LoadModule:PI) :- 1109 NoWarn = LoadModule:'$nowarn_autoload'(PI,TargetModule), 1110 '$c_current_predicate'(_, NoWarn), 1111 \+ '$get_predicate_attribute'(NoWarn, imported, _From), 1112 call(NoWarn). 1113 1114 1115 /******************************* 1116 * REQUIRE * 1117 *******************************/
1124require(M:Spec) :- 1125 ( is_list(Spec) 1126 -> List = Spec 1127 ; phrase(comma_list(Spec), List) 1128 ), !, 1129 require(List, M, FromLib), 1130 keysort(FromLib, Sorted), 1131 by_file(Sorted, Autoload), 1132 forall('$member'(File-Import, Autoload), 1133 autoload(M:File, Import)). 1134require(_:Spec) :- 1135 '$type_error'(list, Spec). 1136 1137require([],_, []). 1138require([H|T], M, Needed) :- 1139 '$pi_head'(H, Head), 1140 ( '$get_predicate_attribute'(system:Head, defined, 1) 1141 -> require(T, M, Needed) 1142 ; '$pi_head'(Module:Name/Arity, M:Head), 1143 ( '$find_library'(Module, Name, Arity, LoadModule, Library) 1144 -> ( current_predicate(LoadModule:Name/Arity) 1145 -> Module:import(LoadModule:Name/Arity), 1146 require(T, M, Needed) 1147 ; Needed = [Library-H|More], 1148 require(T, M, More) 1149 ) 1150 ; print_message(error, error(existence_error(procedure, Name/Arity), _)), 1151 require(T, M, Needed) 1152 ) 1153 ). 1154 1155by_file([], []). 1156by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :- 1157 on_path(File, Spec), 1158 same_file(T0, File, PIs, T1), 1159 by_file(T1, T). 1160 1161on_path(Library, library(Base)) :- 1162 file_base_name(Library, Base), 1163 findall(Path, plain_source(library(Base), Path), [Library]), 1164 !. 1165on_path(Library, Library). 1166 1167plain_source(Spec, Path) :- 1168 absolute_file_name(Spec, PathExt, 1169 [ file_type(prolog), 1170 access(read), 1171 file_errors(fail), 1172 solutions(all) 1173 ]), 1174 file_name_extension(Path, _, PathExt). 1175 1176same_file([File-PI|T0], File, [PI|PIs], T) :- 1177 !, 1178 same_file(T0, File, PIs, T). 1179same_file(List, _, [], List). 1180 1181comma_list(Var) --> 1182 { var(Var), 1183 !, 1184 '$instantiation_error'(Var) 1185 }. 1186comma_list((A,B)) --> 1187 !, 1188 comma_list(A), 1189 comma_list(B). 1190comma_list(A) --> 1191 [A]