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 ( current_prolog_flag(xref, Old) 531 -> true 532 ; Old = false 533 ), 534 setup_call_cleanup( 535 set_prolog_flag(xref, true), 536 snapshot(exports_(File, Module, Exports, Meta, Public)), 537 set_prolog_flag(xref, Old)). 538 539exports_(File, Module, Exports, Meta, Public) :- 540 State = state(true, _, [], [], []), 541 ( '$source_term'(File, 542 _Read,_RLayout, 543 Term,_TermLayout, 544 _Stream, 545 [ syntax_errors(quiet) 546 ]), 547 ( Term = (:- module(M,ModuleExports)), 548 is_list(ModuleExports), 549 arg(1, State, true) 550 -> nb_setarg(1, State, false), 551 nb_setarg(2, State, M), 552 nb_setarg(3, State, ModuleExports), 553 fail 554 ; nb_setarg(1, State, false), 555 fail 556 ; Term = (:- export(Export)) 557 -> phrase(export_pi(Export), PIs), 558 arg(3, State, E0), 559 '$append'(E0, PIs, E1), 560 nb_setarg(3, State, E1), 561 fail 562 ; Term = (:- public(Public)) 563 -> phrase(export_pi(Public), PIs), 564 arg(5, State, E0), 565 '$append'(E0, PIs, E1), 566 nb_setarg(5, State, E1), 567 fail 568 ; Term = (:- meta_predicate(Heads)), 569 phrase(meta(Heads), M1), 570 arg(4, State, M0), 571 '$append'(M0, M1, M2), 572 nb_setarg(4, State, M2) 573 ; Term = (:- use_foreign_library(Lib)), 574 nonvar(Lib), 575 arg(2, State, M), 576 atom(M) 577 -> catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true), 578 fail 579 ; Term = (:- Directive), 580 nonvar(Directive) 581 -> fail 582 ; Term == [] % Expansion for conditionals 583 -> fail 584 ; ! 585 ) 586 ; true 587 ), 588 arg(2, State, Module), 589 arg(3, State, Exports), 590 arg(4, State, Meta), 591 arg(5, State, Public). 592 593export_pi(Var) --> 594 { var(Var) }, 595 !. 596export_pi((A,B)) --> 597 !, 598 export_pi(A), 599 export_pi(B). 600export_pi(PI) --> 601 { ground(PI) }, 602 [PI]. 603 604meta(Var) --> 605 { var(Var) }, 606 !. 607meta((A,B)) --> 608 !, 609 meta(A), 610 meta(B). 611meta(Head) --> 612 { callable(Head) }, 613 [Head]. 614 615 616 /******************************* 617 * EXTENDING * 618 *******************************/
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.
635autoload_path(Alias) :- 636 ( user:file_search_path(autoload, Alias) 637 -> true 638 ; assertz(user:file_search_path(autoload, Alias)), 639 reload_library_index 640 ). 641 642systemterm_expansion((:- autoload_path(Alias)), 643 [ user:file_search_path(autoload, Alias), 644 (:- reload_library_index) 645 ]). 646 647 648 /******************************* 649 * RUNTIME AUTOLOADER * 650 *******************************/
current_prolog_flag(autoload, true)
holds.660'$autoload'(PI) :- 661 source_location(File, _Line), 662 !, 663 setup_call_cleanup( 664 '$start_aux'(File, Context), 665 '$autoload2'(PI), 666 '$end_aux'(File, Context)). 667'$autoload'(PI) :- 668 '$autoload2'(PI). 669 670'$autoload2'(PI) :- 671 setup_call_cleanup( 672 leave_sandbox(Old), 673 '$autoload3'(PI), 674 restore_sandbox(Old)). 675 676leave_sandbox(Sandboxed) :- 677 current_prolog_flag(sandboxed_load, Sandboxed), 678 set_prolog_flag(sandboxed_load, false). 679restore_sandbox(Sandboxed) :- 680 set_prolog_flag(sandboxed_load, Sandboxed). 681 682'$autoload3'(PI) :- 683 autoload_from(PI, LoadModule, FullFile), 684 do_autoload(FullFile, PI, LoadModule).
691autoload_from(Module:PI, LoadModule, FullFile) :- 692 autoload_in(Module, explicit), 693 current_autoload(Module:File, Ctx, import(Imports)), 694 memberchk(PI, Imports), 695 library_info(File, Ctx, FullFile, LoadModule, Exports), 696 ( pi_in_exports(PI, Exports) 697 -> ! 698 ; autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)), 699 fail 700 ). 701autoload_from(Module:Name/Arity, LoadModule, FullFile) :- 702 autoload_in(Module, explicit), 703 PI = Name/Arity, 704 current_autoload(Module:File, Ctx, all), 705 library_info(File, Ctx, FullFile, LoadModule, Exports), 706 pi_in_exports(PI, Exports). 707autoload_from(Module:Name/Arity, LoadModule, Library) :- 708 autoload_in(Module, general), 709 '$find_library'(Module, Name, Arity, LoadModule, Library). 710 711:- public autoload_in/2. % used in syspred 712 713autoload_in(Module, How) :- 714 current_prolog_flag(autoload, AutoLoad), 715 autoload_in(AutoLoad, How, Module), 716 !.
720autoload_in(true, _, _). 721autoload_in(explicit, explicit, _). 722autoload_in(user, _, user). 723autoload_in(user_or_explicit, explicit, _). 724autoload_in(user_or_explicit, _, user).
user
. '$c_current_predicate'/2
verifies the predicate really exists, but doesn't validate
that it is defined.743do_autoload(Library, Module:Name/Arity, LoadModule) :- 744 functor(Head, Name, Arity), 745 '$update_autoload_level'([autoload(true)], Old), 746 verbose_autoload(Module:Name/Arity, Library), 747 loadable_file(Library, File), 748 '$compilation_mode'(OldComp, database), 749 ( Module == LoadModule 750 -> ensure_loaded(Module:File) 751 ; ( '$c_current_predicate'(_, LoadModule:Head), 752 '$get_predicate_attribute'(LoadModule:Head, defined, 1), 753 \+ '$loading'(Library) 754 -> Module:import(LoadModule:Name/Arity) 755 ; use_module(Module:File, [Name/Arity]) 756 ), 757 warn_autoload(Module, LoadModule:Name/Arity) 758 ), 759 '$set_compilation_mode'(OldComp), 760 '$set_autoload_level'(Old), 761 '$c_current_predicate'(_, Module:Head). 762 763loadable_file(PlFile, File) :- 764 exists_file(PlFile), !, 765 File = PlFile. 766loadable_file(PlFile, Base) :- 767 file_name_extension(Base, pl, PlFile), 768 !. 769loadable_file(File, File). 770 771verbose_autoload(PI, Library) :- 772 current_prolog_flag(verbose_autoload, true), 773 !, 774 set_prolog_flag(verbose_autoload, false), 775 print_message(informational, autoload(PI, Library)), 776 set_prolog_flag(verbose_autoload, true). 777verbose_autoload(PI, Library) :- 778 print_message(silent, autoload(PI, Library)).
784autoload_call(Goal) :-
785 '$pi_head'(PI, Goal),
786 ( current_predicate(PI)
787 -> true
788 ; '$autoload'(PI)
789 ),
790 call(Goal).
autoload(File)
. The module must be
instantiated.798:- public % used from predicate_property/2 799 autoloadable/2. 800 801autoloadable(M:Head, FullFile) :- 802 atom(M), 803 current_module(M), 804 autoload_in(M, explicit), 805 ( callable(Head) 806 -> goal_name_arity(Head, Name, Arity), 807 autoload_from(M:Name/Arity, _, FullFile) 808 ; findall((M:H)-F, autoloadable_2(M:H, F), Pairs), 809 ( '$member'(M:Head-FullFile, Pairs) 810 ; current_autoload(M:File, Ctx, all), 811 library_info(File, Ctx, FullFile, _, Exports), 812 '$member'(PI, Exports), 813 '$pi_head'(PI, Head), 814 \+ memberchk(M:Head-_, Pairs) 815 ) 816 ). 817autoloadable(M:Head, FullFile) :- 818 ( var(M) 819 -> autoload_in(any, general) 820 ; autoload_in(M, general) 821 ), 822 ( callable(Head) 823 -> goal_name_arity(Head, Name, Arity), 824 ( '$find_library'(_, Name, Arity, _, FullFile) 825 -> true 826 ) 827 ; '$in_library'(Name, Arity, autoload), 828 functor(Head, Name, Arity) 829 ). 830 831 832autoloadable_2(M:Head, FullFile) :- 833 current_autoload(M:File, Ctx, import(Imports)), 834 library_info(File, Ctx, FullFile, _LoadModule, _Exports), 835 '$member'(PI, Imports), 836 '$pi_head'(PI, Head). 837 838goal_name_arity(Head, Name, Arity) :- 839 compound(Head), 840 !, 841 compound_name_arity(Head, Name, Arity). 842goal_name_arity(Head, Head, 0).
855library_info(Spec, _, FullFile, Module, Exports) :- 856 '$resolved_source_path'(Spec, FullFile, []), 857 !, 858 ( \+ '$loading_file'(FullFile, _Queue, _LoadThread) 859 -> '$current_module'(Module, FullFile), 860 '$module_property'(Module, exports(Exports)) 861 ; library_info_from_file(FullFile, _, Module, Exports) 862 ). 863library_info(Spec, Context, FullFile, Module, Exports) :- 864 ( Context = (Path:_Line) 865 -> Extra = [relative_to(Path)] 866 ; Extra = [] 867 ), 868 ( absolute_file_name(Spec, AbsFile, 869 [ file_type(prolog), 870 access(read), 871 file_errors(fail) 872 | Extra 873 ]) 874 -> library_info_from_file(AbsFile, FullFile, Module, Exports), 875 '$register_resolved_source_path'(Spec, FullFile) 876 ; absolute_file_name(Spec, FullFile, 877 [ file_type(prolog), 878 solutions(all), 879 file_errors(fail) 880 | Extra 881 ]), 882 source_file(FullFile), 883 '$current_module'(Module, FullFile) 884 -> '$module_property'(Module, exports(Exports)) 885 ; autoload_error(Context, no_file(Spec)), 886 fail 887 ). 888 889library_info_from_file(QlfFile, PlFile, Module, Exports) :- 890 file_name_extension(_, qlf, QlfFile), 891 !, 892 '$qlf_module'(QlfFile, Info), 893 _{module:Module, exports:Exports, file:PlFile} :< Info. 894library_info_from_file(PlFile, PlFile, Module, Exports) :- 895 setup_call_cleanup( 896 '$set_source_module'(OldModule, system), 897 setup_call_cleanup( 898 '$open_source'(PlFile, In, State, [], []), 899 '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream, 900 [PlFile], []), 901 '$close_source'(State, true)), 902 '$set_source_module'(OldModule)), 903 ( Term = (:- module(Module, Exports)) 904 -> ! 905 ; nonvar(Term), 906 skip_header(Term) 907 -> fail 908 ; '$domain_error'(module_header, Term) 909 ). 910 911skip_header(begin_of_file). 912 913 914:- dynamic printed/3. 915:- volatile printed/3. 916 917autoload_error(Context, Error) :- 918 suppress(Context, Error), 919 !. 920autoload_error(Context, Error) :- 921 get_time(Now), 922 assertz(printed(Context, Error, Now)), 923 print_message(warning, error(autoload(Error), autoload(Context))). 924 925suppress(Context, Error) :- 926 printed(Context, Error, Printed), 927 get_time(Now), 928 ( Now - Printed < 1 929 -> true 930 ; retractall(printed(Context, Error, _)), 931 fail 932 ). 933 934 935 /******************************* 936 * CALLBACK * 937 *******************************/ 938 939:- public 940 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.949set_autoload(FlagValue) :- 950 current_prolog_flag(autoload, FlagValue), 951 !. 952set_autoload(FlagValue) :- 953 \+ autoload_in(FlagValue, explicit, any), 954 !, 955 setup_call_cleanup( 956 nb_setval('$autoload_disabling', true), 957 materialize_autoload(Count), 958 nb_delete('$autoload_disabling')), 959 print_message(informational, autoload(disabled(Count))). 960set_autoload(_). 961 962materialize_autoload(Count) :- 963 State = state(0), 964 forall(current_predicate(M:'$autoload'/3), 965 materialize_autoload(M, State)), 966 arg(1, State, Count). 967 968materialize_autoload(M, State) :- 969 ( current_autoload(M:File, Context, Import), 970 library_info(File, Context, PlFile, _LoadModule, _Exports), 971 arg(1, State, N0), 972 N is N0+1, 973 nb_setarg(1, State, N), 974 loadable_file(PlFile, LoadFile), 975 ( Import == all 976 -> verbose_autoload(M:all, PlFile), 977 use_module(M:LoadFile) 978 ; Import = import(Preds) 979 -> verbose_autoload(M:Preds, PlFile), 980 use_module(M:LoadFile, Preds) 981 ), 982 fail 983 ; true 984 ), 985 abolish(M:'$autoload'/3). 986 987 988 /******************************* 989 * AUTOLOAD/2 * 990 *******************************/ 991 992autoload(M:File) :- 993 ( \+ autoload_in(M, explicit) 994 ; nb_current('$autoload_disabling', true) 995 ), 996 !, 997 use_module(M:File). 998autoload(M:File) :- 999 '$must_be'(filespec, File), 1000 source_context(Context), 1001 assert_autoload(M, File, Context, all). 1002 1003autoload(M:File, Imports) :- 1004 ( \+ autoload_in(M, explicit) 1005 ; nb_current('$autoload_disabling', true) 1006 ), 1007 !, 1008 use_module(M:File, Imports). 1009autoload(M:File, Imports0) :- 1010 '$must_be'(filespec, File), 1011 valid_imports(Imports0, Imports), 1012 source_context(Context), 1013 register_autoloads(Imports, M, File, Context), 1014 assert_autoload(M, File, Context, import(Imports)). 1015 1016source_context(Path:Line) :- 1017 source_location(Path, Line), 1018 !. 1019source_context(-).
1029assert_autoload(Module, File, _, Imports) :- 1030 current_autoload(Module:File, _, Imports), 1031 !. 1032assert_autoload(Module, File, Context, Imports) :- 1033 set_admin_properties(Module), 1034 Clause = Module:'$autoload'(File, Context, Imports), 1035 '$initialization_context'(Source, Ctx), 1036 '$store_admin_clause2'(Clause, _Layout, Source, Ctx). 1037 1038set_admin_properties(Module) :- 1039 predicate_property(Module:'$autoload'(_,_,_), discontiguous), 1040 !. 1041set_admin_properties(Module) :- 1042 discontiguous(Module:'$autoload'/3). 1043 1044valid_imports(Imports0, Imports) :- 1045 '$must_be'(list, Imports0), 1046 valid_import_list(Imports0, Imports). 1047 1048valid_import_list([], []). 1049valid_import_list([H0|T0], [H|T]) :- 1050 '$pi_head'(H0, Head), 1051 '$pi_head'(H, Head), 1052 valid_import_list(T0, T).
autoload
flag on all predicates declared using autoload/2
to prevent duplicates or the user defining the same predicate.
1061register_autoloads([], _, _, _). 1062register_autoloads([PI|T], Module, File, Context) :- 1063 PI = Name/Arity, 1064 functor(Head, Name, Arity), 1065 ( '$get_predicate_attribute'(Module:Head, autoload, 1) 1066 -> ( current_autoload(Module:_File0, _Ctx0, import(Imports)), 1067 memberchk(PI, Imports) 1068 -> '$permission_error'(redefine, imported_procedure, PI), 1069 fail 1070 ; Done = true 1071 ) 1072 ; '$c_current_predicate'(_, Module:Head), % no auto-import 1073 '$get_predicate_attribute'(Module:Head, imported, From) 1074 -> ( ( '$resolved_source_path'(File, FullFile) 1075 -> true 1076 ; '$resolve_source_path'(File, FullFile, []) 1077 ), 1078 module_property(From, file(FullFile)) 1079 -> Done = true 1080 ; print_message(warning, 1081 autoload(already_defined(Module:PI, From))), 1082 Done = true 1083 ) 1084 ; true 1085 ), 1086 ( Done == true 1087 -> true 1088 ; '$set_predicate_attribute'(Module:Head, autoload, 1) 1089 ), 1090 register_autoloads(T, Module, File, Context). 1091 1092pi_in_exports(PI, Exports) :- 1093 '$member'(E, Exports), 1094 canonical_pi(E, PI), 1095 !. 1096 1097canonical_pi(Var, _) :- 1098 var(Var), !, fail. 1099canonical_pi(Name/Arity, Name/Arity). 1100canonical_pi(Name//A0, Name/Arity) :- 1101 Arity is A0 + 2. 1102 1103current_autoload(M:File, Context, Term) :- 1104 '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1), 1105 M:'$autoload'(File, Context, Term). 1106 1107 /******************************* 1108 * CHECK * 1109 *******************************/
1115warn_autoload(TargetModule, PI) :- 1116 current_prolog_flag(warn_autoload, true), 1117 \+ current_prolog_flag(xref, true), 1118 \+ nb_current('$autoload_warning', true), 1119 \+ nowarn_autoload(TargetModule, PI), 1120 '$pi_head'(PI, Head), 1121 source_file(Head, File), 1122 '$source_defines_expansion'(File), 1123 setup_call_cleanup( 1124 b_setval('$autoload_warning', true), 1125 print_message(warning, 1126 deprecated(autoload(TargetModule, File, PI, expansion))), 1127 nb_delete('$autoload_warning')). 1128warn_autoload(_, _).
1143nowarn_autoload(TargetModule, LoadModule:PI) :- 1144 NoWarn = LoadModule:'$nowarn_autoload'(PI,TargetModule), 1145 '$c_current_predicate'(_, NoWarn), 1146 \+ '$get_predicate_attribute'(NoWarn, imported, _From), 1147 call(NoWarn). 1148 1149 1150 /******************************* 1151 * REQUIRE * 1152 *******************************/
1159require(M:Spec) :- 1160 ( is_list(Spec) 1161 -> List = Spec 1162 ; phrase(comma_list(Spec), List) 1163 ), !, 1164 require(List, M, FromLib), 1165 keysort(FromLib, Sorted), 1166 by_file(Sorted, Autoload), 1167 forall('$member'(File-Import, Autoload), 1168 autoload(M:File, Import)). 1169require(_:Spec) :- 1170 '$type_error'(list, Spec). 1171 1172require([],_, []). 1173require([H|T], M, Needed) :- 1174 '$pi_head'(H, Head), 1175 ( '$get_predicate_attribute'(system:Head, defined, 1) 1176 -> require(T, M, Needed) 1177 ; '$pi_head'(Module:Name/Arity, M:Head), 1178 ( '$find_library'(Module, Name, Arity, LoadModule, Library) 1179 -> ( current_predicate(LoadModule:Name/Arity) 1180 -> Module:import(LoadModule:Name/Arity), 1181 require(T, M, Needed) 1182 ; Needed = [Library-H|More], 1183 require(T, M, More) 1184 ) 1185 ; print_message(error, error(existence_error(procedure, Name/Arity), _)), 1186 require(T, M, Needed) 1187 ) 1188 ). 1189 1190by_file([], []). 1191by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :- 1192 on_path(File, Spec), 1193 same_file(T0, File, PIs, T1), 1194 by_file(T1, T). 1195 1196on_path(Library, library(Base)) :- 1197 file_base_name(Library, Base), 1198 findall(Path, plain_source(library(Base), Path), [Library]), 1199 !. 1200on_path(Library, Library). 1201 1202plain_source(Spec, Path) :- 1203 absolute_file_name(Spec, PathExt, 1204 [ file_type(prolog), 1205 access(read), 1206 file_errors(fail), 1207 solutions(all) 1208 ]), 1209 file_name_extension(Path, _, PathExt). 1210 1211same_file([File-PI|T0], File, [PI|PIs], T) :- 1212 !, 1213 same_file(T0, File, PIs, T). 1214same_file(List, _, [], List). 1215 1216comma_list(Var) --> 1217 { var(Var), 1218 !, 1219 '$instantiation_error'(Var) 1220 }. 1221comma_list((A,B)) --> 1222 !, 1223 comma_list(A), 1224 comma_list(B). 1225comma_list(A) --> 1226 [A]