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-2023, 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'/0, 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, Name, Arity, LoadModule, Library) :-
88 load_library_index(Name, Arity),
89 functor(Head, Name, Arity),
90 ( library_index(Head, Module, Library),
91 LoadModule = Module
92 ; library_index(Head, LoadModule, Library)
93 ),
94 !.
101'$in_library'(Name, Arity, Path) :- 102 atom(Name), integer(Arity), 103 !, 104 load_library_index(Name, Arity), 105 functor(Head, Name, Arity), 106 library_index(Head, _, Path). 107'$in_library'(Name, Arity, Path) :- 108 load_library_index(Name, Arity), 109 library_index(Head, _, Path), 110 functor(Head, Name, Arity).
117:- meta_predicate 118 '$define_predicate'( ). 119 120'$define_predicate'(Head) :- 121 '$defined_predicate'(Head), 122 !. 123'$define_predicate'(Term) :- 124 Term = Module:Head, 125 ( compound(Head) 126 -> compound_name_arity(Head, Name, Arity) 127 ; Name = Head, Arity = 0 128 ), 129 '$undefined_procedure'(Module, Name, Arity, retry). 130 131 132 /******************************** 133 * UPDATE INDEX * 134 ********************************/ 135 136:- thread_local 137 silent/0.
146'$update_library_index' :- 147 setof(Dir, writable_indexed_directory(Dir), Dirs), 148 !, 149 setup_call_cleanup( 150 asserta(silent, Ref), 151 guarded_make_library_index(Dirs), 152 erase(Ref)), 153 ( flag('$modified_index', true, false) 154 -> reload_library_index 155 ; true 156 ). 157'$update_library_index'. 158 159guarded_make_library_index([]). 160guarded_make_library_index([Dir|Dirs]) :- 161 ( catch(make_library_index(Dir), E, 162 print_message(error, E)) 163 -> true 164 ; print_message(warning, goal_failed(make_library_index(Dir))) 165 ), 166 guarded_make_library_index(Dirs).
173writable_indexed_directory(Dir) :- 174 index_file_name(IndexFile, autoload('INDEX'), [access([read,write])]), 175 file_directory_name(IndexFile, Dir). 176writable_indexed_directory(Dir) :- 177 absolute_file_name(library('MKINDEX'), 178 [ file_type(prolog), 179 access(read), 180 solutions(all), 181 file_errors(fail) 182 ], MkIndexFile), 183 file_directory_name(MkIndexFile, Dir), 184 plfile_in_dir(Dir, 'INDEX', _, IndexFile), 185 access_file(IndexFile, write). 186 187 188 /******************************** 189 * LOAD INDEX * 190 ********************************/
196reload_library_index :- 197 context_module(M), 198 reload_library_index(M). 199 200reload_library_index(M) :- 201 with_mutex('$autoload', clear_library_index(M)). 202 203clear_library_index(M) :- 204 retractall(M:library_index(_, _, _)), 205 retractall(M:autoload_directories(_)), 206 retractall(M:index_checked_at(_)).
216:- meta_predicate load_library_index( , , ). 217:- public load_library_index/3. 218 219load_library_index(Name, Arity) :- 220 load_library_index(Name, Arity, autoload('INDEX')). 221 222load_library_index(Name, Arity, M:_Spec) :- 223 atom(Name), integer(Arity), 224 functor(Head, Name, Arity), 225 M:library_index(Head, _, _), 226 !. 227load_library_index(_, _, Spec) :- 228 notrace(with_mutex('$autoload', load_library_index_p(Spec))). 229 230load_library_index_p(M:_) :- 231 M:index_checked_at(Time), 232 get_time(Now), 233 Now-Time < 60, 234 !. 235load_library_index_p(M:Spec) :- 236 findall(Index, index_file_name(Index, Spec, [access(read)]), List0), 237 '$list_to_set'(List0, List), 238 retractall(M:index_checked_at(_)), 239 get_time(Now), 240 assert(M:index_checked_at(Now)), 241 ( M:autoload_directories(List) 242 -> true 243 ; retractall(M:library_index(_, _, _)), 244 retractall(M:autoload_directories(_)), 245 read_index(List, M), 246 assert(M:autoload_directories(List)) 247 ).
autoload
.
257index_file_name(IndexFile, FileSpec, Options) :- 258 absolute_file_name(FileSpec, 259 IndexFile, 260 [ file_type(prolog), 261 solutions(all), 262 file_errors(fail) 263 | Options 264 ]). 265 266read_index([], _) :- !. 267read_index([H|T], M) :- 268 !, 269 read_index(H, M), 270 read_index(T, M). 271read_index(Index, M) :- 272 print_message(silent, autoload(read_index(Dir))), 273 file_directory_name(Index, Dir), 274 setup_call_cleanup( 275 '$push_input_context'(autoload_index), 276 setup_call_cleanup( 277 open(Index, read, In), 278 read_index_from_stream(Dir, In, M), 279 close(In)), 280 '$pop_input_context'). 281 282read_index_from_stream(Dir, In, M) :- 283 repeat, 284 read(In, Term), 285 assert_index(Term, Dir, M), 286 !. 287 288assert_index(end_of_file, _, _) :- !. 289assert_index(index(Name, Arity, Module, File), Dir, M) :- 290 !, 291 functor(Head, Name, Arity), 292 atomic_list_concat([Dir, '/', File], Path), 293 assertz(M:library_index(Head, Module, Path)), 294 fail. 295assert_index(Term, Dir, _) :- 296 print_message(error, illegal_autoload_index(Dir, Term)), 297 fail. 298 299 300 /******************************** 301 * CREATE INDEX.pl * 302 ********************************/
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.
315make_library_index(Dir0) :- 316 forall(absolute_file_name(Dir0, Dir, 317 [ expand(true), 318 file_type(directory), 319 file_errors(fail), 320 solutions(all) 321 ]), 322 make_library_index2(Dir)). 323 324make_library_index2(Dir) :- 325 plfile_in_dir(Dir, 'MKINDEX', _MkIndex, AbsMkIndex), 326 access_file(AbsMkIndex, read), 327 !, 328 load_files(user:AbsMkIndex, [silent(true)]). 329make_library_index2(Dir) :- 330 findall(Pattern, source_file_pattern(Pattern), PatternList), 331 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']).
346make_library_index(Dir0, Patterns) :- 347 forall(absolute_file_name(Dir0, Dir, 348 [ expand(true), 349 file_type(directory), 350 file_errors(fail), 351 solutions(all) 352 ]), 353 make_library_index2(Dir, Patterns)). 354 355make_library_index2(Dir, Patterns) :- 356 plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex), 357 ensure_slash(Dir, DirS), 358 pattern_files(Patterns, DirS, Files), 359 ( library_index_out_of_date(Dir, AbsIndex, Files) 360 -> do_make_library_index(AbsIndex, DirS, Files), 361 set_flag('$modified_index', true) 362 ; true 363 ). 364 365ensure_slash(Dir, DirS) :- 366 ( sub_atom(Dir, _, _, 0, /) 367 -> DirS = Dir 368 ; atom_concat(Dir, /, DirS) 369 ). 370 371source_file_pattern(Pattern) :- 372 user:prolog_file_type(PlExt, prolog), 373 PlExt \== qlf, 374 atom_concat('*.', PlExt, Pattern). 375 376plfile_in_dir(Dir, Base, PlBase, File) :- 377 file_name_extension(Base, pl, PlBase), 378 atomic_list_concat([Dir, '/', PlBase], File). 379 380pattern_files([], _, []). 381pattern_files([H|T], DirS, Files) :- 382 atom_concat(DirS, H, P0), 383 expand_file_name(P0, Files0), 384 '$append'(Files0, Rest, Files), 385 pattern_files(T, DirS, Rest). 386 387library_index_out_of_date(_Dir, Index, _Files) :- 388 \+ exists_file(Index), 389 !. 390library_index_out_of_date(Dir, Index, Files) :- 391 time_file(Index, IndexTime), 392 ( time_file(Dir, DotTime), 393 DotTime - IndexTime > 0.001 % compensate for jitter 394 ; '$member'(File, Files), % and rounding 395 time_file(File, FileTime), 396 FileTime - IndexTime > 0.001 397 ), 398 !. 399 400 401do_make_library_index(Index, Dir, Files) :- 402 ensure_slash(Dir, DirS), 403 '$stage_file'(Index, StagedIndex), 404 setup_call_catcher_cleanup( 405 open(StagedIndex, write, Out), 406 ( print_message(informational, make(library_index(Dir))), 407 index_header(Out), 408 index_files(Files, DirS, Out) 409 ), 410 Catcher, 411 install_index(Out, Catcher, StagedIndex, Index)). 412 413install_index(Out, Catcher, StagedIndex, Index) :- 414 catch(close(Out), Error, true), 415 ( silent 416 -> OnError = silent 417 ; OnError = error 418 ), 419 ( var(Error) 420 -> TheCatcher = Catcher 421 ; TheCatcher = exception(Error) 422 ), 423 '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
429index_files([], _, _). 430index_files([File|Files], DirS, Fd) :- 431 ( catch(exports(File, Module, Public), E, 432 print_message(warning, E)), 433 nonvar(Module) 434 -> atom_concat(DirS, Local, File), 435 file_name_extension(Base, _, Local), 436 forall(public_predicate(Public, Name/Arity), 437 format(Fd, 'index((~k), ~k, ~k, ~k).~n', 438 [Name, Arity, Module, Base])) 439 ; true 440 ), 441 index_files(Files, DirS, Fd). 442 443public_predicate(Public, PI) :- 444 '$member'(PI0, Public), 445 canonical_pi(PI0, PI). 446 447canonical_pi(Var, _) :- 448 var(Var), !, fail. 449canonical_pi(Name/Arity, Name/Arity). 450canonical_pi(Name//A0, Name/Arity) :- 451 Arity is A0 + 2. 452 453 454index_header(Fd):- 455 format(Fd, '/* Creator: make/0~n~n', []), 456 format(Fd, ' Purpose: Provide index for autoload~n', []), 457 format(Fd, '*/~n~n', []). 458 459exports(File, Module, Exports) :- 460 ( current_prolog_flag(xref, Old) 461 -> true 462 ; Old = false 463 ), 464 setup_call_cleanup( 465 set_prolog_flag(xref, true), 466 snapshot(exports_(File, Module, Exports)), 467 set_prolog_flag(xref, Old)). 468 469exports_(File, Module, Exports) :- 470 State = state(true, _, []), 471 ( '$source_term'(File, 472 _Read,_RLayout, 473 Term,_TermLayout, 474 _Stream, 475 [ syntax_errors(quiet) 476 ]), 477 ( Term = (:- module(M,Public)), 478 is_list(Public), 479 arg(1, State, true) 480 -> nb_setarg(1, State, false), 481 nb_setarg(2, State, M), 482 nb_setarg(3, State, Public), 483 fail 484 ; nb_setarg(1, State, false), 485 fail 486 ; Term = (:- export(PI)), 487 ground(PI) 488 -> arg(3, State, E0), 489 '$append'(E0, [PI], E1), 490 nb_setarg(3, State, E1), 491 fail 492 ; Term = (:- use_foreign_library(Lib)), 493 nonvar(Lib), 494 arg(2, State, M), 495 atom(M) 496 -> catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true), 497 fail 498 ; Term = (:- Directive), 499 nonvar(Directive) 500 -> fail 501 ; ! 502 ) 503 ; true 504 ), 505 arg(2, State, Module), 506 arg(3, State, Exports). 507 508 509 /******************************* 510 * EXTENDING * 511 *******************************/
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.
528autoload_path(Alias) :- 529 ( user:file_search_path(autoload, Alias) 530 -> true 531 ; assertz(user:file_search_path(autoload, Alias)), 532 reload_library_index 533 ). 534 535systemterm_expansion((:- autoload_path(Alias)), 536 [ user:file_search_path(autoload, Alias), 537 (:- reload_library_index) 538 ]). 539 540 541 /******************************* 542 * RUNTIME AUTOLOADER * 543 *******************************/
current_prolog_flag(autoload, true)
holds.553'$autoload'(PI) :- 554 source_location(File, _Line), 555 !, 556 setup_call_cleanup( 557 '$start_aux'(File, Context), 558 '$autoload2'(PI), 559 '$end_aux'(File, Context)). 560'$autoload'(PI) :- 561 '$autoload2'(PI). 562 563'$autoload2'(PI) :- 564 setup_call_cleanup( 565 leave_sandbox(Old), 566 '$autoload3'(PI), 567 restore_sandbox(Old)). 568 569leave_sandbox(Sandboxed) :- 570 current_prolog_flag(sandboxed_load, Sandboxed), 571 set_prolog_flag(sandboxed_load, false). 572restore_sandbox(Sandboxed) :- 573 set_prolog_flag(sandboxed_load, Sandboxed). 574 575'$autoload3'(PI) :- 576 autoload_from(PI, LoadModule, FullFile), 577 do_autoload(FullFile, PI, LoadModule).
584autoload_from(Module:PI, LoadModule, FullFile) :- 585 autoload_in(Module, explicit), 586 current_autoload(Module:File, Ctx, import(Imports)), 587 memberchk(PI, Imports), 588 library_info(File, Ctx, FullFile, LoadModule, Exports), 589 ( pi_in_exports(PI, Exports) 590 -> ! 591 ; autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)), 592 fail 593 ). 594autoload_from(Module:Name/Arity, LoadModule, FullFile) :- 595 autoload_in(Module, explicit), 596 PI = Name/Arity, 597 current_autoload(Module:File, Ctx, all), 598 library_info(File, Ctx, FullFile, LoadModule, Exports), 599 pi_in_exports(PI, Exports). 600autoload_from(Module:Name/Arity, LoadModule, Library) :- 601 autoload_in(Module, general), 602 '$find_library'(Module, Name, Arity, LoadModule, Library). 603 604:- public autoload_in/2. % used in syspred 605 606autoload_in(Module, How) :- 607 current_prolog_flag(autoload, AutoLoad), 608 autoload_in(AutoLoad, How, Module), 609 !.
613autoload_in(true, _, _). 614autoload_in(explicit, explicit, _). 615autoload_in(user, _, user). 616autoload_in(user_or_explicit, explicit, _). 617autoload_in(user_or_explicit, _, user).
user
. '$c_current_predicate'/2
verifies the predicate really exists, but doesn't validate
that it is defined.633do_autoload(Library, Module:Name/Arity, LoadModule) :- 634 functor(Head, Name, Arity), 635 '$update_autoload_level'([autoload(true)], Old), 636 verbose_autoload(Module:Name/Arity, Library), 637 '$compilation_mode'(OldComp, database), 638 ( Module == LoadModule 639 -> ensure_loaded(Module:Library) 640 ; ( '$c_current_predicate'(_, LoadModule:Head), 641 '$get_predicate_attribute'(LoadModule:Head, defined, 1), 642 \+ '$loading'(Library) 643 -> Module:import(LoadModule:Name/Arity) 644 ; use_module(Module:Library, [Name/Arity]) 645 ), 646 warn_autoload(Module, LoadModule:Name/Arity) 647 ), 648 '$set_compilation_mode'(OldComp), 649 '$set_autoload_level'(Old), 650 '$c_current_predicate'(_, Module:Head). 651 652verbose_autoload(PI, Library) :- 653 current_prolog_flag(verbose_autoload, true), 654 !, 655 set_prolog_flag(verbose_autoload, false), 656 print_message(informational, autoload(PI, Library)), 657 set_prolog_flag(verbose_autoload, true). 658verbose_autoload(PI, Library) :- 659 print_message(silent, autoload(PI, Library)).
autoload(File)
. The module must be
instantiated.668:- public % used from predicate_property/2 669 autoloadable/2. 670 671autoloadable(M:Head, FullFile) :- 672 atom(M), 673 current_module(M), 674 autoload_in(M, explicit), 675 ( callable(Head) 676 -> goal_name_arity(Head, Name, Arity), 677 autoload_from(M:Name/Arity, _, FullFile) 678 ; findall((M:H)-F, autoloadable_2(M:H, F), Pairs), 679 ( '$member'(M:Head-FullFile, Pairs) 680 ; current_autoload(M:File, Ctx, all), 681 library_info(File, Ctx, FullFile, _, Exports), 682 '$member'(PI, Exports), 683 '$pi_head'(PI, Head), 684 \+ memberchk(M:Head-_, Pairs) 685 ) 686 ). 687autoloadable(M:Head, FullFile) :- 688 ( var(M) 689 -> autoload_in(any, general) 690 ; autoload_in(M, general) 691 ), 692 ( callable(Head) 693 -> goal_name_arity(Head, Name, Arity), 694 ( '$find_library'(_, Name, Arity, _, FullFile) 695 -> true 696 ) 697 ; '$in_library'(Name, Arity, autoload), 698 functor(Head, Name, Arity) 699 ). 700 701 702autoloadable_2(M:Head, FullFile) :- 703 current_autoload(M:File, Ctx, import(Imports)), 704 library_info(File, Ctx, FullFile, _LoadModule, _Exports), 705 '$member'(PI, Imports), 706 '$pi_head'(PI, Head). 707 708goal_name_arity(Head, Name, Arity) :- 709 compound(Head), 710 !, 711 compound_name_arity(Head, Name, Arity). 712goal_name_arity(Head, Head, 0).
718library_info(Spec, _, FullFile, Module, Exports) :- 719 '$resolved_source_path'(Spec, FullFile, []), 720 !, 721 ( \+ '$loading_file'(FullFile, _Queue, _LoadThread) 722 -> '$current_module'(Module, FullFile), 723 '$module_property'(Module, exports(Exports)) 724 ; library_info_from_file(FullFile, Module, Exports) 725 ). 726library_info(Spec, Context, FullFile, Module, Exports) :- 727 ( Context = (Path:_Line) 728 -> Extra = [relative_to(Path)] 729 ; Extra = [] 730 ), 731 ( absolute_file_name(Spec, FullFile, 732 [ file_type(prolog), 733 access(read), 734 file_errors(fail) 735 | Extra 736 ]) 737 -> '$register_resolved_source_path'(Spec, FullFile), 738 library_info_from_file(FullFile, Module, Exports) 739 ; autoload_error(Context, no_file(Spec)), 740 fail 741 ). 742 743library_info_from_file(FullFile, Module, Exports) :- 744 setup_call_cleanup( 745 '$set_source_module'(OldModule, system), 746 setup_call_cleanup( 747 '$open_source'(FullFile, In, State, [], []), 748 '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream, 749 [FullFile], []), 750 '$close_source'(State, true)), 751 '$set_source_module'(OldModule)), 752 ( Term = (:- module(Module, Exports)) 753 -> ! 754 ; nonvar(Term), 755 skip_header(Term) 756 -> fail 757 ; '$domain_error'(module_header, Term) 758 ). 759 760skip_header(begin_of_file). 761 762 763:- dynamic printed/3. 764:- volatile printed/3. 765 766autoload_error(Context, Error) :- 767 suppress(Context, Error), 768 !. 769autoload_error(Context, Error) :- 770 get_time(Now), 771 assertz(printed(Context, Error, Now)), 772 print_message(warning, error(autoload(Error), autoload(Context))). 773 774suppress(Context, Error) :- 775 printed(Context, Error, Printed), 776 get_time(Now), 777 ( Now - Printed < 1 778 -> true 779 ; retractall(printed(Context, Error, _)), 780 fail 781 ). 782 783 784 /******************************* 785 * CALLBACK * 786 *******************************/ 787 788:- public 789 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.798set_autoload(FlagValue) :- 799 current_prolog_flag(autoload, FlagValue), 800 !. 801set_autoload(FlagValue) :- 802 \+ autoload_in(FlagValue, explicit, any), 803 !, 804 setup_call_cleanup( 805 nb_setval('$autoload_disabling', true), 806 materialize_autoload(Count), 807 nb_delete('$autoload_disabling')), 808 print_message(informational, autoload(disabled(Count))). 809set_autoload(_). 810 811materialize_autoload(Count) :- 812 State = state(0), 813 forall(current_predicate(M:'$autoload'/3), 814 materialize_autoload(M, State)), 815 arg(1, State, Count). 816 817materialize_autoload(M, State) :- 818 ( current_autoload(M:File, Context, Import), 819 library_info(File, Context, FullFile, _LoadModule, _Exports), 820 arg(1, State, N0), 821 N is N0+1, 822 nb_setarg(1, State, N), 823 ( Import == all 824 -> verbose_autoload(M:all, FullFile), 825 use_module(M:FullFile) 826 ; Import = import(Preds) 827 -> verbose_autoload(M:Preds, FullFile), 828 use_module(M:FullFile, Preds) 829 ), 830 fail 831 ; true 832 ), 833 abolish(M:'$autoload'/3). 834 835 836 /******************************* 837 * AUTOLOAD/2 * 838 *******************************/ 839 840autoload(M:File) :- 841 ( \+ autoload_in(M, explicit) 842 ; nb_current('$autoload_disabling', true) 843 ), 844 !, 845 use_module(M:File). 846autoload(M:File) :- 847 '$must_be'(filespec, File), 848 source_context(Context), 849 ( current_autoload(M:File, _, import(all)) 850 -> true 851 ; assert_autoload(M:'$autoload'(File, Context, all)) 852 ). 853 854autoload(M:File, Imports) :- 855 ( \+ autoload_in(M, explicit) 856 ; nb_current('$autoload_disabling', true) 857 ), 858 !, 859 use_module(M:File, Imports). 860autoload(M:File, Imports0) :- 861 '$must_be'(filespec, File), 862 valid_imports(Imports0, Imports), 863 source_context(Context), 864 register_autoloads(Imports, M, File, Context), 865 ( current_autoload(M:File, _, import(Imports)) 866 -> true 867 ; assert_autoload(M:'$autoload'(File, Context, import(Imports))) 868 ). 869 870source_context(Path:Line) :- 871 source_location(Path, Line), 872 !. 873source_context(-). 874 875assert_autoload(Clause) :- 876 '$initialization_context'(Source, Ctx), 877 '$store_admin_clause2'(Clause, _Layout, Source, Ctx). 878 879valid_imports(Imports0, Imports) :- 880 '$must_be'(list, Imports0), 881 valid_import_list(Imports0, Imports). 882 883valid_import_list([], []). 884valid_import_list([H0|T0], [H|T]) :- 885 '$pi_head'(H0, Head), 886 '$pi_head'(H, Head), 887 valid_import_list(T0, T).
autoload
flag on all predicates declared using autoload/2
to prevent duplicates or the user defining the same predicate.894register_autoloads([], _, _, _). 895register_autoloads([PI|T], Module, File, Context) :- 896 PI = Name/Arity, 897 functor(Head, Name, Arity), 898 ( '$get_predicate_attribute'(Module:Head, autoload, 1) 899 -> ( current_autoload(Module:_File0, _Ctx0, import(Imports)), 900 memberchk(PI, Imports) 901 -> '$permission_error'(redefine, imported_procedure, PI), 902 fail 903 ; Done = true 904 ) 905 ; '$c_current_predicate'(_, Module:Head), % no auto-import 906 '$get_predicate_attribute'(Module:Head, imported, From) 907 -> ( ( '$resolved_source_path'(File, FullFile) 908 -> true 909 ; '$resolve_source_path'(File, FullFile, []) 910 ), 911 module_property(From, file(FullFile)) 912 -> Done = true 913 ; print_message(warning, 914 autoload(already_defined(Module:PI, From))), 915 Done = true 916 ) 917 ; true 918 ), 919 ( Done == true 920 -> true 921 ; '$set_predicate_attribute'(Module:Head, autoload, 1) 922 ), 923 register_autoloads(T, Module, File, Context). 924 925pi_in_exports(PI, Exports) :- 926 '$member'(E, Exports), 927 canonical_pi(E, PI), 928 !. 929 930current_autoload(M:File, Context, Term) :- 931 '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1), 932 M:'$autoload'(File, Context, Term). 933 934 /******************************* 935 * CHECK * 936 *******************************/ 937 938warn_autoload(TargetModule, PI) :- 939 current_prolog_flag(warn_autoload, true), 940 \+ current_prolog_flag(xref, true), 941 \+ nb_current('$autoload_warning', true), 942 '$pi_head'(PI, Head), 943 source_file(Head, File), 944 expansion_hook(P), 945 source_file(P, File), 946 !, 947 setup_call_cleanup( 948 b_setval('$autoload_warning', true), 949 print_message(warning, 950 deprecated(autoload(TargetModule, File, PI, expansion))), 951 nb_delete('$autoload_warning')). 952warn_autoload(_, _). 953 954expansion_hook(user:goal_expansion(_,_)). 955expansion_hook(user:goal_expansion(_,_,_,_)). 956expansion_hook(system:goal_expansion(_,_)). 957expansion_hook(system:goal_expansion(_,_,_,_)). 958 959 960 /******************************* 961 * REQUIRE * 962 *******************************/
969require(M:Spec) :- 970 ( is_list(Spec) 971 -> List = Spec 972 ; phrase(comma_list(Spec), List) 973 ), !, 974 require(List, M, FromLib), 975 keysort(FromLib, Sorted), 976 by_file(Sorted, Autoload), 977 forall('$member'(File-Import, Autoload), 978 autoload(M:File, Import)). 979require(_:Spec) :- 980 '$type_error'(list, Spec). 981 982require([],_, []). 983require([H|T], M, Needed) :- 984 '$pi_head'(H, Head), 985 ( '$get_predicate_attribute'(system:Head, defined, 1) 986 -> require(T, M, Needed) 987 ; '$pi_head'(Module:Name/Arity, M:Head), 988 ( '$find_library'(Module, Name, Arity, LoadModule, Library) 989 -> ( current_predicate(LoadModule:Name/Arity) 990 -> Module:import(LoadModule:Name/Arity), 991 require(T, M, Needed) 992 ; Needed = [Library-H|More], 993 require(T, M, More) 994 ) 995 ; print_message(error, error(existence_error(procedure, Name/Arity), _)), 996 require(T, M, Needed) 997 ) 998 ). 999 1000by_file([], []). 1001by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :- 1002 on_path(File, Spec), 1003 same_file(T0, File, PIs, T1), 1004 by_file(T1, T). 1005 1006on_path(Library, library(Base)) :- 1007 file_base_name(Library, Base), 1008 findall(Path, plain_source(library(Base), Path), [Library]), 1009 !. 1010on_path(Library, Library). 1011 1012plain_source(Spec, Path) :- 1013 absolute_file_name(Spec, PathExt, 1014 [ file_type(prolog), 1015 access(read), 1016 file_errors(fail), 1017 solutions(all) 1018 ]), 1019 file_name_extension(Path, _, PathExt). 1020 1021same_file([File-PI|T0], File, [PI|PIs], T) :- 1022 !, 1023 same_file(T0, File, PIs, T). 1024same_file(List, _, [], List). 1025 1026comma_list(Var) --> 1027 { var(Var), 1028 !, 1029 '$instantiation_error'(Var) 1030 }. 1031comma_list((A,B)) --> 1032 !, 1033 comma_list(A), 1034 comma_list(B). 1035comma_list(A) --> 1036 [A]