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 autoload_call/1, % :Goal 54 55 require/1 % +Predicates 56 ]). 57 58:- meta_predicate 59 '$autoload'(), 60 autoload(), 61 autoload(, ), 62 autoload_call(), 63 require(). 64 65:- dynamic 66 library_index/3, % Head x Module x Path 67 autoload_directories/1, % List 68 index_checked_at/1. % Time 69:- volatile 70 library_index/3, 71 autoload_directories/1, 72 index_checked_at/1. 73 74user:file_search_path(autoload, swi(library)). 75user:file_search_path(autoload, pce(prolog/lib)). 76user:file_search_path(autoload, app_config(lib)). 77user:file_search_path(autoload, Dir) :- 78 '$ext_library_directory'(Dir). 79 80:- create_prolog_flag(warn_autoload, false, []).
90'$find_library'(_Module, :, 2, _LoadModule, _Library) :- 91 !, fail. 92'$find_library'(Module, Name, Arity, LoadModule, Library) :- 93 load_library_index(Name, Arity), 94 functor(Head, Name, Arity), 95 ( library_index(Head, Module, Library), 96 LoadModule = Module 97 ; library_index(Head, LoadModule, Library) 98 ), 99 !.
106'$in_library'(Name, Arity, Path) :- 107 atom(Name), integer(Arity), 108 !, 109 Name/Arity \= (:)/2, 110 load_library_index(Name, Arity), 111 functor(Head, Name, Arity), 112 library_index(Head, _, Path). 113'$in_library'(Name, Arity, Path) :- 114 load_library_index(Name, Arity), 115 library_index(Head, _, Path), 116 Head \= _:_, 117 functor(Head, Name, Arity).
124:- meta_predicate 125 '$define_predicate'(). 126 127'$define_predicate'(Head) :- 128 '$defined_predicate'(Head), 129 !. 130'$define_predicate'(Term) :- 131 Term = Module:Head, 132 ( compound(Head) 133 -> compound_name_arity(Head, Name, Arity) 134 ; Name = Head, Arity = 0 135 ), 136 '$undefined_procedure'(Module, Name, Arity, retry). 137 138 139 /******************************** 140 * UPDATE INDEX * 141 ********************************/ 142 143:- thread_local 144 silent/0.
false.true.158'$update_library_index'(Options) :- 159 setof(Dir, writable_indexed_directory(Dir, Options), Dirs), 160 !, 161 setup_call_cleanup( 162 asserta(silent, Ref), 163 guarded_make_library_index(Dirs), 164 erase(Ref)), 165 ( flag('$modified_index', true, false) 166 -> reload_library_index 167 ; true 168 ). 169'$update_library_index'(_). 170 171guarded_make_library_index([]). 172guarded_make_library_index([Dir|Dirs]) :- 173 ( catch(make_library_index(Dir), E, 174 print_message(error, E)) 175 -> true 176 ; print_message(warning, goal_failed(make_library_index(Dir))) 177 ), 178 guarded_make_library_index(Dirs).
185writable_indexed_directory(Dir, Options) :- 186 current_prolog_flag(home, Home), 187 writable_indexed_directory(Dir), 188 ( sub_atom(Dir, 0, _, _, Home) 189 -> '$option'(system(true), Options, false) 190 ; '$option'(user(true), Options, true) 191 ). 192 193writable_indexed_directory(Dir) :- 194 index_file_name(IndexFile, autoload('INDEX'), [access([read,write])]), 195 file_directory_name(IndexFile, Dir). 196writable_indexed_directory(Dir) :- 197 absolute_file_name(library('MKINDEX'), 198 [ file_type(prolog), 199 access(read), 200 solutions(all), 201 file_errors(fail) 202 ], MkIndexFile), 203 file_directory_name(MkIndexFile, Dir), 204 plfile_in_dir(Dir, 'INDEX', _, IndexFile), 205 access_file(IndexFile, write). 206 207 208 /******************************** 209 * LOAD INDEX * 210 ********************************/
216reload_library_index :- 217 context_module(M), 218 reload_library_index(M). 219 220reload_library_index(M) :- 221 with_mutex('$autoload', clear_library_index(M)). 222 223clear_library_index(M) :- 224 retractall(M:library_index(_, _, _)), 225 retractall(M:autoload_directories(_)), 226 retractall(M:index_checked_at(_)).
236:- meta_predicate load_library_index(, , ). 237:- public load_library_index/3. 238 239load_library_index(Name, Arity) :- 240 load_library_index(Name, Arity, autoload('INDEX')). 241 242load_library_index(Name, Arity, M:_Spec) :- 243 atom(Name), integer(Arity), 244 functor(Head, Name, Arity), 245 M:library_index(Head, _, _), 246 !. 247load_library_index(_, _, Spec) :- 248 notrace(with_mutex('$autoload', load_library_index_p(Spec))). 249 250load_library_index_p(M:_) :- 251 M:index_checked_at(Time), 252 get_time(Now), 253 Now-Time < 60, 254 !. 255load_library_index_p(M:Spec) :- 256 findall(Index, index_file_name(Index, Spec, [access(read)]), List0), 257 '$list_to_set'(List0, List), 258 retractall(M:index_checked_at(_)), 259 get_time(Now), 260 assert(M:index_checked_at(Now)), 261 ( M:autoload_directories(List) 262 -> true 263 ; retractall(M:library_index(_, _, _)), 264 retractall(M:autoload_directories(_)), 265 read_index(List, M), 266 assert(M:autoload_directories(List)) 267 ).
autoload.
277index_file_name(IndexFile, FileSpec, Options) :- 278 absolute_file_name(FileSpec, 279 IndexFile, 280 [ file_type(prolog), 281 solutions(all), 282 file_errors(fail) 283 | Options 284 ]). 285 286read_index([], _) :- !. 287read_index([H|T], M) :- 288 !, 289 read_index(H, M), 290 read_index(T, M). 291read_index(Index, M) :- 292 print_message(silent, autoload(read_index(Dir))), 293 file_directory_name(Index, Dir), 294 setup_call_cleanup( 295 '$push_input_context'(autoload_index), 296 setup_call_cleanup( 297 win_open_index(Index, In), 298 read_index_from_stream(Dir, In, M), 299 close(In)), 300 '$pop_input_context').
INDEX.pl file. When concurrently building the index we may
run into sharing violations on Windows.307:- if(current_prolog_flag(windows, true)). 308win_open_index(Index, In) :- 309 between(1, 10, _), 310 catch(open(Index, read, In, [encoding(utf8)]), 311 error(permission_error(open, source_sink, _),_), (sleep(0.1),fail)), 312 !. 313:- endif. 314win_open_index(Index, In) :- 315 open(Index, read, In, [encoding(utf8)]). 316 317read_index_from_stream(Dir, In, M) :- 318 repeat, 319 read(In, Term), 320 assert_index(Term, Dir, M), 321 !. 322 323assert_index(end_of_file, _, _) :- !. 324assert_index(index(Term, Module, File), Dir, M) :- 325 !, 326 atomic_list_concat([Dir, '/', File], Path), 327 assertz(M:library_index(Term, Module, Path)), 328 fail. 329assert_index(index(Name, Arity, Module, File), Dir, M) :- 330 !, % Read old index format 331 functor(Head, Name, Arity), 332 head_meta_any(Head), 333 assert_index(index(Head, Module, File), Dir, M). 334assert_index(Term, Dir, _) :- 335 print_message(error, illegal_autoload_index(Dir, Term)), 336 fail. 337 338 339 /******************************** 340 * CREATE INDEX.pl * 341 ********************************/
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.
354make_library_index(Dir0) :- 355 forall(absolute_file_name(Dir0, Dir, 356 [ expand(true), 357 file_type(directory), 358 file_errors(fail), 359 solutions(all) 360 ]), 361 make_library_index2(Dir)). 362 363make_library_index2(Dir) :- 364 plfile_in_dir(Dir, 'MKINDEX', _MkIndex, AbsMkIndex), 365 access_file(AbsMkIndex, read), 366 !, 367 load_files(user:AbsMkIndex, [silent(true)]). 368make_library_index2(Dir) :- 369 findall(Pattern, source_file_pattern(Pattern), PatternList), 370 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']).
385make_library_index(Dir0, Patterns) :- 386 forall(absolute_file_name(Dir0, Dir, 387 [ expand(true), 388 file_type(directory), 389 file_errors(fail), 390 solutions(all) 391 ]), 392 make_library_index2(Dir, Patterns)). 393 394make_library_index2(Dir, Patterns) :- 395 plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex), 396 ensure_slash(Dir, DirS), 397 pattern_files(Patterns, DirS, Files), 398 ( library_index_out_of_date(Dir, AbsIndex, Files) 399 -> do_make_library_index(AbsIndex, DirS, Files), 400 set_flag('$modified_index', true) 401 ; true 402 ). 403 404ensure_slash(Dir, DirS) :- 405 ( sub_atom(Dir, _, _, 0, /) 406 -> DirS = Dir 407 ; atom_concat(Dir, /, DirS) 408 ). 409 410source_file_pattern(Pattern) :- 411 user:prolog_file_type(PlExt, prolog), 412 PlExt \== qlf, 413 atom_concat('*.', PlExt, Pattern). 414 415plfile_in_dir(Dir, Base, PlBase, File) :- 416 file_name_extension(Base, pl, PlBase), 417 atomic_list_concat([Dir, '/', PlBase], File). 418 419pattern_files([], _, []). 420pattern_files([H|T], DirS, Files) :- 421 atom_concat(DirS, H, P0), 422 expand_file_name(P0, Files0), 423 '$append'(Files0, Rest, Files), 424 pattern_files(T, DirS, Rest). 425 426library_index_out_of_date(_Dir, Index, _Files) :- 427 \+ exists_file(Index), 428 !. 429library_index_out_of_date(Dir, Index, Files) :- 430 time_file(Index, IndexTime), 431 ( time_file(Dir, DotTime), 432 DotTime - IndexTime > 0.001 % compensate for jitter 433 ; '$member'(File, Files), % and rounding 434 time_file(File, FileTime), 435 FileTime - IndexTime > 0.001 436 ), 437 !. 438 439 440do_make_library_index(Index, Dir, Files) :- 441 ensure_slash(Dir, DirS), 442 '$stage_file'(Index, StagedIndex), 443 setup_call_catcher_cleanup( 444 open(StagedIndex, write, Out), 445 ( print_message(informational, make(library_index(Dir))), 446 index_header(Out), 447 index_files(Files, DirS, Out) 448 ), 449 Catcher, 450 install_index(Out, Catcher, StagedIndex, Index)). 451 452install_index(Out, Catcher, StagedIndex, Index) :- 453 catch(close(Out), Error, true), 454 ( silent 455 -> OnError = silent 456 ; OnError = error 457 ), 458 ( var(Error) 459 -> TheCatcher = Catcher 460 ; TheCatcher = exception(Error) 461 ), 462 '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
468index_files([], _, _). 469index_files([File|Files], DirS, Fd) :- 470 ( catch(exports(File, Module, Exports, Meta, Public), E, 471 print_message(warning, E)), 472 nonvar(Module) 473 -> atom_concat(DirS, Local, File), 474 file_name_extension(Base, _, Local), 475 forall(index_term(Exports, Meta, Public, Term), 476 format(Fd, 'index(~k, ~k, ~k).~n', 477 [Term, Module, Base])) 478 ; true 479 ), 480 index_files(Files, DirS, Fd). 481 482index_term(Exports, Meta, _Public, Term) :- 483 '$member'(Export, Exports), 484 ground(Export), 485 export_term(Export, Meta, Term). 486index_term(_Exports, _Meta, Publics, (public):Head) :- 487 '$member'(Public, Publics), 488 '$pi_head'(Public, Head). 489 490export_term(Op, _Meta, Term) :- 491 Op = op(_Pri,_Type,_Name), 492 !, 493 Term = op:Op. 494export_term(PI, Meta, Head) :- 495 '$pi_head'(PI, Head), 496 ( '$member'(Head, Meta) 497 -> true 498 ; head_meta_any(Head) 499 ). 500 501head_meta_any(Head) :- 502 ( atom(Head) 503 -> true 504 ; compound_name_arguments(Head, _, Args), 505 meta_any(Args) 506 ). 507 508meta_any([]). 509meta_any([?|T]) :- 510 meta_any(T). 511 512index_header(Fd):- 513 format(Fd, '/* Creator: make/0~n~n', []), 514 format(Fd, ' Purpose: Provide index for autoload~n', []), 515 format(Fd, '*/~n~n', []).
525:- public exports/3. % using by library(prolog_deps). 526exports(File, Module, Exports) :- 527 exports(File, Module, Exports, _Meta, _Public). 528 529exports(File, Module, Exports, Meta, Public) :- 530 setup_call_cleanup( 531 push_prolog_flag(xref, true), 532 snapshot(exports_(File, Module, Exports, Meta, Public)), 533 pop_prolog_flag(xref)). 534 535exports_(File, Module, Exports, Meta, Public) :- 536 State = state(true, _, [], [], []), 537 ( '$source_term'(File, 538 _Read,_RLayout, 539 Term,_TermLayout, 540 _Stream, 541 [ syntax_errors(quiet) 542 ]), 543 ( Term = (:- module(M,ModuleExports)), 544 is_list(ModuleExports), 545 arg(1, State, true) 546 -> nb_setarg(1, State, false), 547 nb_setarg(2, State, M), 548 nb_setarg(3, State, ModuleExports), 549 fail 550 ; nb_setarg(1, State, false), 551 fail 552 ; Term = (:- export(Export)) 553 -> phrase(export_pi(Export), PIs), 554 arg(3, State, E0), 555 '$append'(E0, PIs, E1), 556 nb_setarg(3, State, E1), 557 fail 558 ; Term = (:- public(Public)) 559 -> phrase(export_pi(Public), PIs), 560 arg(5, State, E0), 561 '$append'(E0, PIs, E1), 562 nb_setarg(5, State, E1), 563 fail 564 ; Term = (:- meta_predicate(Heads)), 565 phrase(meta(Heads), M1), 566 arg(4, State, M0), 567 '$append'(M0, M1, M2), 568 nb_setarg(4, State, M2) 569 ; Term = (:- use_foreign_library(Lib)), 570 nonvar(Lib), 571 arg(2, State, M), 572 atom(M) 573 -> catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true), 574 fail 575 ; Term = (:- Directive), 576 nonvar(Directive) 577 -> fail 578 ; Term == [] % Expansion for conditionals 579 -> fail 580 ; ! 581 ) 582 ; true 583 ), 584 arg(2, State, Module), 585 arg(3, State, Exports), 586 arg(4, State, Meta), 587 arg(5, State, Public). 588 589export_pi(Var) --> 590 { var(Var) }, 591 !. 592export_pi((A,B)) --> 593 !, 594 export_pi(A), 595 export_pi(B). 596export_pi(PI) --> 597 { ground(PI) }, 598 [PI]. 599 600meta(Var) --> 601 { var(Var) }, 602 !. 603meta((A,B)) --> 604 !, 605 meta(A), 606 meta(B). 607meta(Head) --> 608 { callable(Head) }, 609 [Head]. 610 611 612 /******************************* 613 * EXTENDING * 614 *******************************/
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.
631autoload_path(Alias) :- 632 ( user:file_search_path(autoload, Alias) 633 -> true 634 ; assertz(user:file_search_path(autoload, Alias)), 635 reload_library_index 636 ). 637 638systemterm_expansion((:- autoload_path(Alias)), 639 [ user:file_search_path(autoload, Alias), 640 (:- reload_library_index) 641 ]). 642 643 644 /******************************* 645 * RUNTIME AUTOLOADER * 646 *******************************/
current_prolog_flag(autoload, true) holds.656'$autoload'(PI) :- 657 source_location(File, _Line), 658 !, 659 setup_call_cleanup( 660 '$start_aux'(File, Context), 661 '$autoload2'(PI), 662 '$end_aux'(File, Context)). 663'$autoload'(PI) :- 664 '$autoload2'(PI). 665 666'$autoload2'(PI) :- 667 setup_call_cleanup( 668 leave_sandbox(Old), 669 '$autoload3'(PI), 670 restore_sandbox(Old)). 671 672leave_sandbox(Sandboxed) :- 673 current_prolog_flag(sandboxed_load, Sandboxed), 674 set_prolog_flag(sandboxed_load, false). 675restore_sandbox(Sandboxed) :- 676 set_prolog_flag(sandboxed_load, Sandboxed). 677 678'$autoload3'(PI) :- 679 autoload_from(PI, LoadModule, FullFile), 680 do_autoload(FullFile, PI, LoadModule).
687autoload_from(Module:PI, LoadModule, FullFile) :- 688 autoload_in(Module, explicit), 689 current_autoload(Module:File, Ctx, import(Imports)), 690 memberchk(PI, Imports), 691 library_info(File, Ctx, FullFile, LoadModule, Exports), 692 ( pi_in_exports(PI, Exports) 693 -> ! 694 ; autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)), 695 fail 696 ). 697autoload_from(Module:Name/Arity, LoadModule, FullFile) :- 698 autoload_in(Module, explicit), 699 PI = Name/Arity, 700 current_autoload(Module:File, Ctx, all), 701 library_info(File, Ctx, FullFile, LoadModule, Exports), 702 pi_in_exports(PI, Exports). 703autoload_from(Module:Name/Arity, LoadModule, Library) :- 704 autoload_in(Module, general), 705 '$find_library'(Module, Name, Arity, LoadModule, Library). 706 707:- public autoload_in/2. % used in syspred 708 709autoload_in(Module, How) :- 710 current_prolog_flag(autoload, AutoLoad), 711 autoload_in(AutoLoad, How, Module), 712 !.
716autoload_in(true, _, _). 717autoload_in(explicit, explicit, _). 718autoload_in(user, _, user). 719autoload_in(user_or_explicit, explicit, _). 720autoload_in(user_or_explicit, _, user).
user. '$c_current_predicate'/2
verifies the predicate really exists, but doesn't validate
that it is defined.739do_autoload(Library, Module:Name/Arity, LoadModule) :- 740 functor(Head, Name, Arity), 741 '$update_autoload_level'([autoload(true)], Old), 742 verbose_autoload(Module:Name/Arity, Library), 743 loadable_file(Library, File), 744 '$compilation_mode'(OldComp, database), 745 ( Module == LoadModule 746 -> ensure_loaded(Module:File) 747 ; ( '$c_current_predicate'(_, LoadModule:Head), 748 '$get_predicate_attribute'(LoadModule:Head, defined, 1), 749 \+ '$loading'(Library) 750 -> Module:import(LoadModule:Name/Arity) 751 ; use_module(Module:File, [Name/Arity]) 752 ), 753 warn_autoload(Module, LoadModule:Name/Arity) 754 ), 755 '$set_compilation_mode'(OldComp), 756 '$set_autoload_level'(Old), 757 '$c_current_predicate'(_, Module:Head). 758 759loadable_file(PlFile, File) :- 760 exists_file(PlFile), !, 761 File = PlFile. 762loadable_file(PlFile, Base) :- 763 file_name_extension(Base, pl, PlFile), 764 !. 765loadable_file(File, File). 766 767verbose_autoload(PI, Library) :- 768 current_prolog_flag(verbose_autoload, true), 769 !, 770 set_prolog_flag(verbose_autoload, false), 771 print_message(informational, autoload(PI, Library)), 772 set_prolog_flag(verbose_autoload, true). 773verbose_autoload(PI, Library) :- 774 print_message(silent, autoload(PI, Library)).
780autoload_call(Goal) :-
781 '$pi_head'(PI, Goal),
782 ( current_predicate(PI)
783 -> true
784 ; '$autoload'(PI)
785 ),
786 call(Goal).autoload(File). The module must be
instantiated.794:- public % used from predicate_property/2 795 autoloadable/2. 796 797autoloadable(M:Head, FullFile) :- 798 atom(M), 799 current_module(M), 800 autoload_in(M, explicit), 801 ( callable(Head) 802 -> goal_name_arity(Head, Name, Arity), 803 autoload_from(M:Name/Arity, _, FullFile) 804 ; findall((M:H)-F, autoloadable_2(M:H, F), Pairs), 805 ( '$member'(M:Head-FullFile, Pairs) 806 ; current_autoload(M:File, Ctx, all), 807 library_info(File, Ctx, FullFile, _, Exports), 808 '$member'(PI, Exports), 809 '$pi_head'(PI, Head), 810 \+ memberchk(M:Head-_, Pairs) 811 ) 812 ). 813autoloadable(M:Head, FullFile) :- 814 ( var(M) 815 -> autoload_in(any, general) 816 ; autoload_in(M, general) 817 ), 818 ( callable(Head) 819 -> goal_name_arity(Head, Name, Arity), 820 ( '$find_library'(_, Name, Arity, _, FullFile) 821 -> true 822 ) 823 ; '$in_library'(Name, Arity, autoload), 824 functor(Head, Name, Arity) 825 ). 826 827 828autoloadable_2(M:Head, FullFile) :- 829 current_autoload(M:File, Ctx, import(Imports)), 830 library_info(File, Ctx, FullFile, _LoadModule, _Exports), 831 '$member'(PI, Imports), 832 '$pi_head'(PI, Head). 833 834goal_name_arity(Head, Name, Arity) :- 835 compound(Head), 836 !, 837 compound_name_arity(Head, Name, Arity). 838goal_name_arity(Head, Head, 0).
851library_info(Spec, _, FullFile, Module, Exports) :- 852 '$resolved_source_path'(Spec, FullFile, []), 853 !, 854 ( \+ '$loading_file'(FullFile, _Queue, _LoadThread) 855 -> '$current_module'(Module, FullFile), 856 '$module_property'(Module, exports(Exports)) 857 ; library_info_from_file(FullFile, _, Module, Exports) 858 ). 859library_info(Spec, Context, FullFile, Module, Exports) :- 860 ( Context = (Path:_Line) 861 -> Extra = [relative_to(Path)] 862 ; Extra = [] 863 ), 864 ( absolute_file_name(Spec, AbsFile, 865 [ file_type(prolog), 866 access(read), 867 file_errors(fail) 868 | Extra 869 ]) 870 -> library_info_from_file(AbsFile, FullFile, Module, Exports), 871 '$register_resolved_source_path'(Spec, FullFile) 872 ; absolute_file_name(Spec, FullFile, 873 [ file_type(prolog), 874 solutions(all), 875 file_errors(fail) 876 | Extra 877 ]), 878 source_file(FullFile), 879 '$current_module'(Module, FullFile) 880 -> '$module_property'(Module, exports(Exports)) 881 ; autoload_error(Context, no_file(Spec)), 882 fail 883 ). 884 885library_info_from_file(QlfFile, PlFile, Module, Exports) :- 886 file_name_extension(_, qlf, QlfFile), 887 !, 888 '$qlf_module'(QlfFile, Info), 889 _{module:Module, exports:Exports, file:PlFile} :< Info. 890library_info_from_file(PlFile, PlFile, Module, Exports) :- 891 setup_call_cleanup( 892 '$set_source_module'(OldModule, system), 893 setup_call_cleanup( 894 '$open_source'(PlFile, In, State, [], []), 895 '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream, 896 [PlFile], []), 897 '$close_source'(State, true)), 898 '$set_source_module'(OldModule)), 899 ( Term = (:- module(Module, Exports)) 900 -> ! 901 ; nonvar(Term), 902 skip_header(Term) 903 -> fail 904 ; '$domain_error'(module_header, Term) 905 ). 906 907skip_header(begin_of_file). 908 909 910:- dynamic printed/3. 911:- volatile printed/3. 912 913autoload_error(Context, Error) :- 914 suppress(Context, Error), 915 !. 916autoload_error(Context, Error) :- 917 get_time(Now), 918 assertz(printed(Context, Error, Now)), 919 print_message(warning, error(autoload(Error), autoload(Context))). 920 921suppress(Context, Error) :- 922 printed(Context, Error, Printed), 923 get_time(Now), 924 ( Now - Printed < 1 925 -> true 926 ; retractall(printed(Context, Error, _)), 927 fail 928 ). 929 930 931 /******************************* 932 * CALLBACK * 933 *******************************/ 934 935:- public 936 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.945set_autoload(FlagValue) :- 946 current_prolog_flag(autoload, FlagValue), 947 !. 948set_autoload(FlagValue) :- 949 \+ autoload_in(FlagValue, explicit, any), 950 !, 951 setup_call_cleanup( 952 nb_setval('$autoload_disabling', true), 953 materialize_autoload(Count), 954 nb_delete('$autoload_disabling')), 955 print_message(informational, autoload(disabled(Count))). 956set_autoload(_). 957 958materialize_autoload(Count) :- 959 State = state(0), 960 forall(current_predicate(M:'$autoload'/3), 961 materialize_autoload(M, State)), 962 arg(1, State, Count). 963 964materialize_autoload(M, State) :- 965 ( current_autoload(M:File, Context, Import), 966 library_info(File, Context, PlFile, _LoadModule, _Exports), 967 arg(1, State, N0), 968 N is N0+1, 969 nb_setarg(1, State, N), 970 loadable_file(PlFile, LoadFile), 971 ( Import == all 972 -> verbose_autoload(M:all, PlFile), 973 use_module(M:LoadFile) 974 ; Import = import(Preds) 975 -> verbose_autoload(M:Preds, PlFile), 976 use_module(M:LoadFile, Preds) 977 ), 978 fail 979 ; true 980 ), 981 abolish(M:'$autoload'/3). 982 983 984 /******************************* 985 * AUTOLOAD/2 * 986 *******************************/ 987 988autoload(M:File) :- 989 ( \+ autoload_in(M, explicit) 990 ; nb_current('$autoload_disabling', true) 991 ), 992 !, 993 use_module(M:File). 994autoload(M:File) :- 995 '$must_be'(filespec, File), 996 source_context(Context), 997 assert_autoload(M, File, Context, all). 998 999autoload(M:File, Imports) :- 1000 ( \+ autoload_in(M, explicit) 1001 ; nb_current('$autoload_disabling', true) 1002 ), 1003 !, 1004 use_module(M:File, Imports). 1005autoload(M:File, Imports0) :- 1006 '$must_be'(filespec, File), 1007 valid_imports(Imports0, Imports), 1008 source_context(Context), 1009 register_autoloads(Imports, M, File, Context), 1010 assert_autoload(M, File, Context, import(Imports)). 1011 1012source_context(Path:Line) :- 1013 source_location(Path, Line), 1014 !. 1015source_context(-).
1025assert_autoload(Module, File, _, Imports) :- 1026 current_autoload(Module:File, _, Imports), 1027 !. 1028assert_autoload(Module, File, Context, Imports) :- 1029 set_admin_properties(Module), 1030 Clause = Module:'$autoload'(File, Context, Imports), 1031 '$initialization_context'(Source, Ctx), 1032 '$store_admin_clause2'(Clause, _Layout, Source, Ctx). 1033 1034set_admin_properties(Module) :- 1035 predicate_property(Module:'$autoload'(_,_,_), discontiguous), 1036 !. 1037set_admin_properties(Module) :- 1038 discontiguous(Module:'$autoload'/3). 1039 1040valid_imports(Imports0, Imports) :- 1041 '$must_be'(list, Imports0), 1042 valid_import_list(Imports0, Imports). 1043 1044valid_import_list([], []). 1045valid_import_list([H0|T0], [H|T]) :- 1046 '$pi_head'(H0, Head), 1047 '$pi_head'(H, Head), 1048 valid_import_list(T0, T).
autoload flag on all predicates declared using autoload/2
to prevent duplicates or the user defining the same predicate.
1057register_autoloads([], _, _, _). 1058register_autoloads([PI|T], Module, File, Context) :- 1059 PI = Name/Arity, 1060 functor(Head, Name, Arity), 1061 ( '$get_predicate_attribute'(Module:Head, autoload, 1) 1062 -> ( current_autoload(Module:_File0, _Ctx0, import(Imports)), 1063 memberchk(PI, Imports) 1064 -> '$permission_error'(redefine, imported_procedure, PI), 1065 fail 1066 ; Done = true 1067 ) 1068 ; '$c_current_predicate'(_, Module:Head), % no auto-import 1069 '$get_predicate_attribute'(Module:Head, imported, From) 1070 -> ( ( '$resolved_source_path'(File, FullFile) 1071 -> true 1072 ; '$resolve_source_path'(File, FullFile, []) 1073 ), 1074 module_property(From, file(FullFile)) 1075 -> Done = true 1076 ; print_message(warning, 1077 autoload(already_defined(Module:PI, From))), 1078 Done = true 1079 ) 1080 ; true 1081 ), 1082 ( Done == true 1083 -> true 1084 ; '$set_predicate_attribute'(Module:Head, autoload, 1) 1085 ), 1086 register_autoloads(T, Module, File, Context). 1087 1088pi_in_exports(PI, Exports) :- 1089 '$member'(E, Exports), 1090 canonical_pi(E, PI), 1091 !. 1092 1093canonical_pi(Var, _) :- 1094 var(Var), !, fail. 1095canonical_pi(Name/Arity, Name/Arity). 1096canonical_pi(Name//A0, Name/Arity) :- 1097 Arity is A0 + 2. 1098 1099current_autoload(M:File, Context, Term) :- 1100 '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1), 1101 M:'$autoload'(File, Context, Term). 1102 1103 /******************************* 1104 * CHECK * 1105 *******************************/
1111warn_autoload(TargetModule, PI) :- 1112 current_prolog_flag(warn_autoload, true), 1113 \+ current_prolog_flag(xref, true), 1114 \+ nb_current('$autoload_warning', true), 1115 \+ nowarn_autoload(TargetModule, PI), 1116 '$pi_head'(PI, Head), 1117 source_file(Head, File), 1118 '$source_defines_expansion'(File), 1119 setup_call_cleanup( 1120 b_setval('$autoload_warning', true), 1121 print_message(warning, 1122 deprecated(autoload(TargetModule, File, PI, expansion))), 1123 nb_delete('$autoload_warning')). 1124warn_autoload(_, _).
1139nowarn_autoload(TargetModule, LoadModule:PI) :- 1140 NoWarn = LoadModule:'$nowarn_autoload'(PI,TargetModule), 1141 '$c_current_predicate'(_, NoWarn), 1142 \+ '$get_predicate_attribute'(NoWarn, imported, _From), 1143 call(NoWarn). 1144 1145 1146 /******************************* 1147 * REQUIRE * 1148 *******************************/
1155require(M:Spec) :- 1156 ( is_list(Spec) 1157 -> List = Spec 1158 ; phrase(comma_list(Spec), List) 1159 ), !, 1160 require(List, M, FromLib), 1161 keysort(FromLib, Sorted), 1162 by_file(Sorted, Autoload), 1163 forall('$member'(File-Import, Autoload), 1164 autoload(M:File, Import)). 1165require(_:Spec) :- 1166 '$type_error'(list, Spec). 1167 1168require([],_, []). 1169require([H|T], M, Needed) :- 1170 '$pi_head'(H, Head), 1171 ( '$get_predicate_attribute'(system:Head, defined, 1) 1172 -> require(T, M, Needed) 1173 ; '$pi_head'(Module:Name/Arity, M:Head), 1174 ( '$find_library'(Module, Name, Arity, LoadModule, Library) 1175 -> ( current_predicate(LoadModule:Name/Arity) 1176 -> Module:import(LoadModule:Name/Arity), 1177 require(T, M, Needed) 1178 ; Needed = [Library-H|More], 1179 require(T, M, More) 1180 ) 1181 ; print_message(error, error(existence_error(procedure, Name/Arity), _)), 1182 require(T, M, Needed) 1183 ) 1184 ). 1185 1186by_file([], []). 1187by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :- 1188 on_path(File, Spec), 1189 same_file(T0, File, PIs, T1), 1190 by_file(T1, T). 1191 1192on_path(Library, library(Base)) :- 1193 file_base_name(Library, Base), 1194 findall(Path, plain_source(library(Base), Path), [Library]), 1195 !. 1196on_path(Library, Library). 1197 1198plain_source(Spec, Path) :- 1199 absolute_file_name(Spec, PathExt, 1200 [ file_type(prolog), 1201 access(read), 1202 file_errors(fail), 1203 solutions(all) 1204 ]), 1205 file_name_extension(Path, _, PathExt). 1206 1207same_file([File-PI|T0], File, [PI|PIs], T) :- 1208 !, 1209 same_file(T0, File, PIs, T). 1210same_file(List, _, [], List). 1211 1212comma_list(Var) --> 1213 { var(Var), 1214 !, 1215 '$instantiation_error'(Var) 1216 }. 1217comma_list((A,B)) --> 1218 !, 1219 comma_list(A), 1220 comma_list(B). 1221comma_list(A) --> 1222 [A]