View source with raw comments or as raw
    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-2024, 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, []).
 $find_library(+Module, +Name, +Arity, -LoadModule, -Library) is semidet
Locate a predicate in the library. Name and arity are the name and arity of the predicate searched for. `Module' is the preferred target module. The return values are the full path name (excluding extension) of the library and module declared in that file.
   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    !.
 $in_library(+Name, +Arity, -Path) is semidet
$in_library(-Name, -Arity, -Path) is nondet
Is true if Name/Arity is in the autoload libraries.
  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).
 $define_predicate(:Head)
Make sure PredInd can be called. First test if the predicate is defined. If not, invoke the autoloader.
  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.
 $update_library_index(+Options)
Called from make/0 to update the index of the library for each library directory that has a writable index. Note that in the Windows version access_file/2 is mostly bogus. We assert silent/0 to suppress error messages. Options:
system(+Boolean)
Do (not) include system libraries. Default false.
user(+Boolean)
Do (not) include user libraries. Default true.
  151'$update_library_index'(Options) :-
  152    setof(Dir, writable_indexed_directory(Dir, Options), Dirs),
  153    !,
  154    setup_call_cleanup(
  155        asserta(silent, Ref),
  156        guarded_make_library_index(Dirs),
  157        erase(Ref)),
  158    (   flag('$modified_index', true, false)
  159    ->  reload_library_index
  160    ;   true
  161    ).
  162'$update_library_index'(_).
  163
  164guarded_make_library_index([]).
  165guarded_make_library_index([Dir|Dirs]) :-
  166    (   catch(make_library_index(Dir), E,
  167              print_message(error, E))
  168    ->  true
  169    ;   print_message(warning, goal_failed(make_library_index(Dir)))
  170    ),
  171    guarded_make_library_index(Dirs).
 writable_indexed_directory(-Dir, +Options) is nondet
True when Dir is an indexed library directory with a writable index, i.e., an index that can be updated.
  178writable_indexed_directory(Dir, Options) :-
  179    current_prolog_flag(home, Home),
  180    writable_indexed_directory(Dir),
  181    (   sub_atom(Dir, 0, _, _, Home)
  182    ->  '$option'(system(true), Options, false)
  183    ;   '$option'(user(true), Options, true)
  184    ).
  185
  186writable_indexed_directory(Dir) :-
  187    index_file_name(IndexFile, autoload('INDEX'), [access([read,write])]),
  188    file_directory_name(IndexFile, Dir).
  189writable_indexed_directory(Dir) :-
  190    absolute_file_name(library('MKINDEX'),
  191                       [ file_type(prolog),
  192                         access(read),
  193                         solutions(all),
  194                         file_errors(fail)
  195                       ], MkIndexFile),
  196    file_directory_name(MkIndexFile, Dir),
  197    plfile_in_dir(Dir, 'INDEX', _, IndexFile),
  198    access_file(IndexFile, write).
  199
  200
  201                /********************************
  202                *           LOAD INDEX          *
  203                ********************************/
 reload_library_index
Reload the index on the next call
  209reload_library_index :-
  210    context_module(M),
  211    reload_library_index(M).
  212
  213reload_library_index(M) :-
  214    with_mutex('$autoload', clear_library_index(M)).
  215
  216clear_library_index(M) :-
  217    retractall(M:library_index(_, _, _)),
  218    retractall(M:autoload_directories(_)),
  219    retractall(M:index_checked_at(_)).
 load_library_index(?Name, ?Arity) is det
 load_library_index(?Name, ?Arity, :IndexSpec) is det
Try to find Name/Arity in the library. If the predicate is there, we are happy. If not, we check whether the set of loaded libraries has changed and if so we reload the index.
  229:- meta_predicate load_library_index(?, ?, :).  230:- public load_library_index/3.  231
  232load_library_index(Name, Arity) :-
  233    load_library_index(Name, Arity, autoload('INDEX')).
  234
  235load_library_index(Name, Arity, M:_Spec) :-
  236    atom(Name), integer(Arity),
  237    functor(Head, Name, Arity),
  238    M:library_index(Head, _, _),
  239    !.
  240load_library_index(_, _, Spec) :-
  241    notrace(with_mutex('$autoload', load_library_index_p(Spec))).
  242
  243load_library_index_p(M:_) :-
  244    M:index_checked_at(Time),
  245    get_time(Now),
  246    Now-Time < 60,
  247    !.
  248load_library_index_p(M:Spec) :-
  249    findall(Index, index_file_name(Index, Spec, [access(read)]), List0),
  250    '$list_to_set'(List0, List),
  251    retractall(M:index_checked_at(_)),
  252    get_time(Now),
  253    assert(M:index_checked_at(Now)),
  254    (   M:autoload_directories(List)
  255    ->  true
  256    ;   retractall(M:library_index(_, _, _)),
  257        retractall(M:autoload_directories(_)),
  258        read_index(List, M),
  259        assert(M:autoload_directories(List))
  260    ).
 index_file_name(-IndexFile, +Spec, +Options) is nondet
True if IndexFile is an autoload index file. Options is passed to absolute_file_name/3. This predicate searches the path autoload.
See also
- file_search_path/2.
  270index_file_name(IndexFile, FileSpec, Options) :-
  271    absolute_file_name(FileSpec,
  272                       IndexFile,
  273                       [ file_type(prolog),
  274                         solutions(all),
  275                         file_errors(fail)
  276                       | Options
  277                       ]).
  278
  279read_index([], _) :- !.
  280read_index([H|T], M) :-
  281    !,
  282    read_index(H, M),
  283    read_index(T, M).
  284read_index(Index, M) :-
  285    print_message(silent, autoload(read_index(Dir))),
  286    file_directory_name(Index, Dir),
  287    setup_call_cleanup(
  288        '$push_input_context'(autoload_index),
  289        setup_call_cleanup(
  290            open(Index, read, In),
  291            read_index_from_stream(Dir, In, M),
  292            close(In)),
  293        '$pop_input_context').
  294
  295read_index_from_stream(Dir, In, M) :-
  296    repeat,
  297        read(In, Term),
  298        assert_index(Term, Dir, M),
  299    !.
  300
  301assert_index(end_of_file, _, _) :- !.
  302assert_index(index(Name, Arity, Module, File), Dir, M) :-
  303    !,
  304    functor(Head, Name, Arity),
  305    atomic_list_concat([Dir, '/', File], Path),
  306    assertz(M:library_index(Head, Module, Path)),
  307    fail.
  308assert_index(Term, Dir, _) :-
  309    print_message(error, illegal_autoload_index(Dir, Term)),
  310    fail.
  311
  312
  313                /********************************
  314                *       CREATE INDEX.pl         *
  315                ********************************/
 make_library_index(+Dir) is det
Create an index for autoloading from the directory Dir. The index file is called 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.
See also
- make_library_index/2
  328make_library_index(Dir0) :-
  329    forall(absolute_file_name(Dir0, Dir,
  330                              [ expand(true),
  331                                file_type(directory),
  332                                file_errors(fail),
  333                                solutions(all)
  334                              ]),
  335           make_library_index2(Dir)).
  336
  337make_library_index2(Dir) :-
  338    plfile_in_dir(Dir, 'MKINDEX', _MkIndex, AbsMkIndex),
  339    access_file(AbsMkIndex, read),
  340    !,
  341    load_files(user:AbsMkIndex, [silent(true)]).
  342make_library_index2(Dir) :-
  343    findall(Pattern, source_file_pattern(Pattern), PatternList),
  344    make_library_index2(Dir, PatternList).
 make_library_index(+Dir, +Patterns:list(atom)) is det
Create an autoload index 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']).
See also
- make_library_index/1.
  359make_library_index(Dir0, Patterns) :-
  360    forall(absolute_file_name(Dir0, Dir,
  361                              [ expand(true),
  362                                file_type(directory),
  363                                file_errors(fail),
  364                                solutions(all)
  365                              ]),
  366           make_library_index2(Dir, Patterns)).
  367
  368make_library_index2(Dir, Patterns) :-
  369    plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex),
  370    ensure_slash(Dir, DirS),
  371    pattern_files(Patterns, DirS, Files),
  372    (   library_index_out_of_date(Dir, AbsIndex, Files)
  373    ->  do_make_library_index(AbsIndex, DirS, Files),
  374        set_flag('$modified_index', true)
  375    ;   true
  376    ).
  377
  378ensure_slash(Dir, DirS) :-
  379    (   sub_atom(Dir, _, _, 0, /)
  380    ->  DirS = Dir
  381    ;   atom_concat(Dir, /, DirS)
  382    ).
  383
  384source_file_pattern(Pattern) :-
  385    user:prolog_file_type(PlExt, prolog),
  386    PlExt \== qlf,
  387    atom_concat('*.', PlExt, Pattern).
  388
  389plfile_in_dir(Dir, Base, PlBase, File) :-
  390    file_name_extension(Base, pl, PlBase),
  391    atomic_list_concat([Dir, '/', PlBase], File).
  392
  393pattern_files([], _, []).
  394pattern_files([H|T], DirS, Files) :-
  395    atom_concat(DirS, H, P0),
  396    expand_file_name(P0, Files0),
  397    '$append'(Files0, Rest, Files),
  398    pattern_files(T, DirS, Rest).
  399
  400library_index_out_of_date(_Dir, Index, _Files) :-
  401    \+ exists_file(Index),
  402    !.
  403library_index_out_of_date(Dir, Index, Files) :-
  404    time_file(Index, IndexTime),
  405    (   time_file(Dir, DotTime),
  406        DotTime - IndexTime > 0.001             % compensate for jitter
  407    ;   '$member'(File, Files),                 % and rounding
  408        time_file(File, FileTime),
  409        FileTime - IndexTime > 0.001
  410    ),
  411    !.
  412
  413
  414do_make_library_index(Index, Dir, Files) :-
  415    ensure_slash(Dir, DirS),
  416    '$stage_file'(Index, StagedIndex),
  417    setup_call_catcher_cleanup(
  418        open(StagedIndex, write, Out),
  419        ( print_message(informational, make(library_index(Dir))),
  420          index_header(Out),
  421          index_files(Files, DirS, Out)
  422        ),
  423        Catcher,
  424        install_index(Out, Catcher, StagedIndex, Index)).
  425
  426install_index(Out, Catcher, StagedIndex, Index) :-
  427    catch(close(Out), Error, true),
  428    (   silent
  429    ->  OnError = silent
  430    ;   OnError = error
  431    ),
  432    (   var(Error)
  433    ->  TheCatcher = Catcher
  434    ;   TheCatcher = exception(Error)
  435    ),
  436    '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
 index_files(+Files, +Directory, +Out:stream) is det
Write index for Files in Directory to the stream Out.
  442index_files([], _, _).
  443index_files([File|Files], DirS, Fd) :-
  444    (   catch(exports(File, Module, Public), E,
  445              print_message(warning, E)),
  446        nonvar(Module)
  447    ->  atom_concat(DirS, Local, File),
  448        file_name_extension(Base, _, Local),
  449        forall(public_predicate(Public, Name/Arity),
  450               format(Fd, 'index((~k), ~k, ~k, ~k).~n',
  451                      [Name, Arity, Module, Base]))
  452    ;   true
  453    ),
  454    index_files(Files, DirS, Fd).
  455
  456public_predicate(Public, PI) :-
  457    '$member'(PI0, Public),
  458    canonical_pi(PI0, PI).
  459
  460canonical_pi(Var, _) :-
  461    var(Var), !, fail.
  462canonical_pi(Name/Arity, Name/Arity).
  463canonical_pi(Name//A0,   Name/Arity) :-
  464    Arity is A0 + 2.
  465
  466
  467index_header(Fd):-
  468    format(Fd, '/*  Creator: make/0~n~n', []),
  469    format(Fd, '    Purpose: Provide index for autoload~n', []),
  470    format(Fd, '*/~n~n', []).
 exports(+File, -Module, -Exports) is det
Get the exports from a library as a list of PIs.
  476:- public exports/3.                            % using by library(prolog_deps).
  477exports(File, Module, Exports) :-
  478    (   current_prolog_flag(xref, Old)
  479    ->  true
  480    ;   Old = false
  481    ),
  482    setup_call_cleanup(
  483        set_prolog_flag(xref, true),
  484        snapshot(exports_(File, Module, Exports)),
  485        set_prolog_flag(xref, Old)).
  486
  487exports_(File, Module, Exports) :-
  488    State = state(true, _, []),
  489    (   '$source_term'(File,
  490                       _Read,_RLayout,
  491                       Term,_TermLayout,
  492                       _Stream,
  493                       [ syntax_errors(quiet)
  494                       ]),
  495        (   Term = (:- module(M,Public)),
  496            is_list(Public),
  497            arg(1, State, true)
  498        ->  nb_setarg(1, State, false),
  499            nb_setarg(2, State, M),
  500            nb_setarg(3, State, Public),
  501            fail
  502        ;   nb_setarg(1, State, false),
  503            fail
  504        ;   Term = (:- export(Export))
  505        ->  phrase(export_pi(Export), PIs),
  506            arg(3, State, E0),
  507            '$append'(E0, PIs, E1),
  508            nb_setarg(3, State, E1),
  509            fail
  510        ;   Term = (:- use_foreign_library(Lib)),
  511            nonvar(Lib),
  512            arg(2, State, M),
  513            atom(M)
  514        ->  catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true),
  515            fail
  516        ;   Term = (:- Directive),
  517            nonvar(Directive)
  518        ->  fail
  519        ;   Term == []                          % Expansion for conditionals
  520        ->  fail
  521        ;   !
  522        )
  523    ;   true
  524    ),
  525    arg(2, State, Module),
  526    arg(3, State, Exports).
  527
  528export_pi(Var) -->
  529    { var(Var) },
  530    !.
  531export_pi((A,B)) -->
  532    !,
  533    export_pi(A),
  534    export_pi(B).
  535export_pi(PI) -->
  536    { ground(PI) },
  537    [PI].
  538
  539
  540                 /*******************************
  541                 *            EXTENDING         *
  542                 *******************************/
 autoload_path(+Path) is det
Add Path to the libraries that are used by the autoloader. This extends the search path 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.

  559autoload_path(Alias) :-
  560    (   user:file_search_path(autoload, Alias)
  561    ->  true
  562    ;   assertz(user:file_search_path(autoload, Alias)),
  563        reload_library_index
  564    ).
  565
  566system:term_expansion((:- autoload_path(Alias)),
  567                      [ user:file_search_path(autoload, Alias),
  568                        (:- reload_library_index)
  569                      ]).
  570
  571
  572		 /*******************************
  573		 *      RUNTIME AUTOLOADER	*
  574		 *******************************/
Provide PI by autoloading. This checks:
  584'$autoload'(PI) :-
  585    source_location(File, _Line),
  586    !,
  587    setup_call_cleanup(
  588        '$start_aux'(File, Context),
  589        '$autoload2'(PI),
  590        '$end_aux'(File, Context)).
  591'$autoload'(PI) :-
  592    '$autoload2'(PI).
  593
  594'$autoload2'(PI) :-
  595    setup_call_cleanup(
  596        leave_sandbox(Old),
  597        '$autoload3'(PI),
  598        restore_sandbox(Old)).
  599
  600leave_sandbox(Sandboxed) :-
  601    current_prolog_flag(sandboxed_load, Sandboxed),
  602    set_prolog_flag(sandboxed_load, false).
  603restore_sandbox(Sandboxed) :-
  604    set_prolog_flag(sandboxed_load, Sandboxed).
  605
  606'$autoload3'(PI) :-
  607    autoload_from(PI, LoadModule, FullFile),
  608    do_autoload(FullFile, PI, LoadModule).
 autoload_from(+PI, -LoadModule, -File) is semidet
True when PI can be defined by loading File which is defined the module LoadModule.
  615autoload_from(Module:PI, LoadModule, FullFile) :-
  616    autoload_in(Module, explicit),
  617    current_autoload(Module:File, Ctx, import(Imports)),
  618    memberchk(PI, Imports),
  619    library_info(File, Ctx, FullFile, LoadModule, Exports),
  620    (   pi_in_exports(PI, Exports)
  621    ->  !
  622    ;   autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)),
  623        fail
  624    ).
  625autoload_from(Module:Name/Arity, LoadModule, FullFile) :-
  626    autoload_in(Module, explicit),
  627    PI = Name/Arity,
  628    current_autoload(Module:File, Ctx, all),
  629    library_info(File, Ctx, FullFile, LoadModule, Exports),
  630    pi_in_exports(PI, Exports).
  631autoload_from(Module:Name/Arity, LoadModule, Library) :-
  632    autoload_in(Module, general),
  633    '$find_library'(Module, Name, Arity, LoadModule, Library).
  634
  635:- public autoload_in/2.                        % used in syspred
  636
  637autoload_in(Module, How) :-
  638    current_prolog_flag(autoload, AutoLoad),
  639    autoload_in(AutoLoad, How, Module),
  640    !.
 autoload_in(+AutoloadFlag, +AutoloadMode, +TargetModule) is semidet
  644autoload_in(true,             _,        _).
  645autoload_in(explicit,         explicit, _).
  646autoload_in(user,             _,        user).
  647autoload_in(user_or_explicit, explicit, _).
  648autoload_in(user_or_explicit, _,        user).
 do_autoload(+File, :PI, +LoadModule) is det
Load File, importing PI into the qualified module. File is known to define LoadModule. There are three cases:
  664do_autoload(Library, Module:Name/Arity, LoadModule) :-
  665    functor(Head, Name, Arity),
  666    '$update_autoload_level'([autoload(true)], Old),
  667    verbose_autoload(Module:Name/Arity, Library),
  668    '$compilation_mode'(OldComp, database),
  669    (   Module == LoadModule
  670    ->  ensure_loaded(Module:Library)
  671    ;   (   '$c_current_predicate'(_, LoadModule:Head),
  672            '$get_predicate_attribute'(LoadModule:Head, defined, 1),
  673            \+ '$loading'(Library)
  674        ->  Module:import(LoadModule:Name/Arity)
  675        ;   use_module(Module:Library, [Name/Arity])
  676        ),
  677        warn_autoload(Module, LoadModule:Name/Arity)
  678    ),
  679    '$set_compilation_mode'(OldComp),
  680    '$set_autoload_level'(Old),
  681    '$c_current_predicate'(_, Module:Head).
  682
  683verbose_autoload(PI, Library) :-
  684    current_prolog_flag(verbose_autoload, true),
  685    !,
  686    set_prolog_flag(verbose_autoload, false),
  687    print_message(informational, autoload(PI, Library)),
  688    set_prolog_flag(verbose_autoload, true).
  689verbose_autoload(PI, Library) :-
  690    print_message(silent, autoload(PI, Library)).
 autoloadable(:Head, -File) is nondet
True when Head can be autoloaded from File. This implements the predicate_property/2 property autoload(File). The module must be instantiated.
  699:- public                               % used from predicate_property/2
  700    autoloadable/2.  701
  702autoloadable(M:Head, FullFile) :-
  703    atom(M),
  704    current_module(M),
  705    autoload_in(M, explicit),
  706    (   callable(Head)
  707    ->  goal_name_arity(Head, Name, Arity),
  708        autoload_from(M:Name/Arity, _, FullFile)
  709    ;   findall((M:H)-F, autoloadable_2(M:H, F), Pairs),
  710        (   '$member'(M:Head-FullFile, Pairs)
  711        ;   current_autoload(M:File, Ctx, all),
  712            library_info(File, Ctx, FullFile, _, Exports),
  713            '$member'(PI, Exports),
  714            '$pi_head'(PI, Head),
  715            \+ memberchk(M:Head-_, Pairs)
  716        )
  717    ).
  718autoloadable(M:Head, FullFile) :-
  719    (   var(M)
  720    ->  autoload_in(any, general)
  721    ;   autoload_in(M, general)
  722    ),
  723    (   callable(Head)
  724    ->  goal_name_arity(Head, Name, Arity),
  725        (   '$find_library'(_, Name, Arity, _, FullFile)
  726        ->  true
  727        )
  728    ;   '$in_library'(Name, Arity, autoload),
  729        functor(Head, Name, Arity)
  730    ).
  731
  732
  733autoloadable_2(M:Head, FullFile) :-
  734    current_autoload(M:File, Ctx, import(Imports)),
  735    library_info(File, Ctx, FullFile, _LoadModule, _Exports),
  736    '$member'(PI, Imports),
  737    '$pi_head'(PI, Head).
  738
  739goal_name_arity(Head, Name, Arity) :-
  740    compound(Head),
  741    !,
  742    compound_name_arity(Head, Name, Arity).
  743goal_name_arity(Head, Head, 0).
 library_info(+Spec, +AutoloadContext, -FullFile, -Module, -Exports)
Find information about a library.
  749library_info(Spec, _, FullFile, Module, Exports) :-
  750    '$resolved_source_path'(Spec, FullFile, []),
  751    !,
  752    (   \+ '$loading_file'(FullFile, _Queue, _LoadThread)
  753    ->  '$current_module'(Module, FullFile),
  754        '$module_property'(Module, exports(Exports))
  755    ;   library_info_from_file(FullFile, Module, Exports)
  756    ).
  757library_info(Spec, Context, FullFile, Module, Exports) :-
  758    (   Context = (Path:_Line)
  759    ->  Extra = [relative_to(Path)]
  760    ;   Extra = []
  761    ),
  762    (   absolute_file_name(Spec, FullFile,
  763                           [ file_type(prolog),
  764                             access(read),
  765                             file_errors(fail)
  766                           | Extra
  767                           ])
  768    ->  '$register_resolved_source_path'(Spec, FullFile),
  769        library_info_from_file(FullFile, Module, Exports)
  770    ;   autoload_error(Context, no_file(Spec)),
  771        fail
  772    ).
  773
  774library_info_from_file(FullFile, Module, Exports) :-
  775    setup_call_cleanup(
  776        '$set_source_module'(OldModule, system),
  777        setup_call_cleanup(
  778            '$open_source'(FullFile, In, State, [], []),
  779            '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream,
  780                            [FullFile], []),
  781            '$close_source'(State, true)),
  782        '$set_source_module'(OldModule)),
  783    (   Term = (:- module(Module, Exports))
  784    ->  !
  785    ;   nonvar(Term),
  786        skip_header(Term)
  787    ->  fail
  788    ;   '$domain_error'(module_header, Term)
  789    ).
  790
  791skip_header(begin_of_file).
  792
  793
  794:- dynamic printed/3.  795:- volatile printed/3.  796
  797autoload_error(Context, Error) :-
  798    suppress(Context, Error),
  799    !.
  800autoload_error(Context, Error) :-
  801    get_time(Now),
  802    assertz(printed(Context, Error, Now)),
  803    print_message(warning, error(autoload(Error), autoload(Context))).
  804
  805suppress(Context, Error) :-
  806    printed(Context, Error, Printed),
  807    get_time(Now),
  808    (   Now - Printed < 1
  809    ->  true
  810    ;   retractall(printed(Context, Error, _)),
  811        fail
  812    ).
  813
  814
  815		 /*******************************
  816		 *            CALLBACK		*
  817		 *******************************/
  818
  819:- public
  820    set_autoload/1.
 set_autoload(+Value) is det
Hook called from set_prolog_flag/2 when autoloading is switched. If the desired value is false we should materialize all registered requests for autoloading. We must do so before disabling autoloading as loading the files may require autoloading.
  829set_autoload(FlagValue) :-
  830    current_prolog_flag(autoload, FlagValue),
  831    !.
  832set_autoload(FlagValue) :-
  833    \+ autoload_in(FlagValue, explicit, any),
  834    !,
  835    setup_call_cleanup(
  836        nb_setval('$autoload_disabling', true),
  837        materialize_autoload(Count),
  838        nb_delete('$autoload_disabling')),
  839    print_message(informational, autoload(disabled(Count))).
  840set_autoload(_).
  841
  842materialize_autoload(Count) :-
  843    State = state(0),
  844    forall(current_predicate(M:'$autoload'/3),
  845           materialize_autoload(M, State)),
  846    arg(1, State, Count).
  847
  848materialize_autoload(M, State) :-
  849    (   current_autoload(M:File, Context, Import),
  850        library_info(File, Context, FullFile, _LoadModule, _Exports),
  851        arg(1, State, N0),
  852        N is N0+1,
  853        nb_setarg(1, State, N),
  854        (   Import == all
  855        ->  verbose_autoload(M:all, FullFile),
  856            use_module(M:FullFile)
  857        ;   Import = import(Preds)
  858        ->  verbose_autoload(M:Preds, FullFile),
  859            use_module(M:FullFile, Preds)
  860        ),
  861        fail
  862    ;   true
  863    ),
  864    abolish(M:'$autoload'/3).
  865
  866
  867		 /*******************************
  868		 *          AUTOLOAD/2		*
  869		 *******************************/
  870
  871autoload(M:File) :-
  872    (   \+ autoload_in(M, explicit)
  873    ;   nb_current('$autoload_disabling', true)
  874    ),
  875    !,
  876    use_module(M:File).
  877autoload(M:File) :-
  878    '$must_be'(filespec, File),
  879    source_context(Context),
  880    (   current_autoload(M:File, _, import(all))
  881    ->  true
  882    ;   assert_autoload(M:'$autoload'(File, Context, all))
  883    ).
  884
  885autoload(M:File, Imports) :-
  886    (   \+ autoload_in(M, explicit)
  887    ;   nb_current('$autoload_disabling', true)
  888    ),
  889    !,
  890    use_module(M:File, Imports).
  891autoload(M:File, Imports0) :-
  892    '$must_be'(filespec, File),
  893    valid_imports(Imports0, Imports),
  894    source_context(Context),
  895    register_autoloads(Imports, M, File, Context),
  896    (   current_autoload(M:File, _, import(Imports))
  897    ->  true
  898    ;   assert_autoload(M:'$autoload'(File, Context, import(Imports)))
  899    ).
  900
  901source_context(Path:Line) :-
  902    source_location(Path, Line),
  903    !.
  904source_context(-).
  905
  906assert_autoload(Clause) :-
  907    '$initialization_context'(Source, Ctx),
  908    '$store_admin_clause2'(Clause, _Layout, Source, Ctx).
  909
  910valid_imports(Imports0, Imports) :-
  911    '$must_be'(list, Imports0),
  912    valid_import_list(Imports0, Imports).
  913
  914valid_import_list([], []).
  915valid_import_list([H0|T0], [H|T]) :-
  916    '$pi_head'(H0, Head),
  917    '$pi_head'(H, Head),
  918    valid_import_list(T0, T).
 register_autoloads(+ListOfPI, +Module, +File, +Context)
Put an autoload flag on all predicates declared using autoload/2 to prevent duplicates or the user defining the same predicate.
  925register_autoloads([], _, _, _).
  926register_autoloads([PI|T], Module, File, Context) :-
  927    PI = Name/Arity,
  928    functor(Head, Name, Arity),
  929    (   '$get_predicate_attribute'(Module:Head, autoload, 1)
  930    ->  (   current_autoload(Module:_File0, _Ctx0, import(Imports)),
  931            memberchk(PI, Imports)
  932        ->  '$permission_error'(redefine, imported_procedure, PI),
  933            fail
  934        ;   Done = true
  935        )
  936    ;   '$c_current_predicate'(_, Module:Head), % no auto-import
  937        '$get_predicate_attribute'(Module:Head, imported, From)
  938    ->  (   (   '$resolved_source_path'(File, FullFile)
  939            ->  true
  940            ;   '$resolve_source_path'(File, FullFile, [])
  941            ),
  942            module_property(From, file(FullFile))
  943        ->  Done = true
  944        ;   print_message(warning,
  945                          autoload(already_defined(Module:PI, From))),
  946            Done = true
  947        )
  948    ;   true
  949    ),
  950    (   Done == true
  951    ->  true
  952    ;   '$set_predicate_attribute'(Module:Head, autoload, 1)
  953    ),
  954    register_autoloads(T, Module, File, Context).
  955
  956pi_in_exports(PI, Exports) :-
  957    '$member'(E, Exports),
  958    canonical_pi(E, PI),
  959    !.
  960
  961current_autoload(M:File, Context, Term) :-
  962    '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1),
  963    M:'$autoload'(File, Context, Term).
  964
  965		 /*******************************
  966		 *            CHECK		*
  967		 *******************************/
 warn_autoload(+TargetModule, :PI) is det
Arguments:
PI- is of the shape LoadModule:Name/Arity.
  973warn_autoload(TargetModule, PI) :-
  974    current_prolog_flag(warn_autoload, true),
  975    \+ current_prolog_flag(xref, true),
  976    \+ nb_current('$autoload_warning', true),
  977    \+ nowarn_autoload(TargetModule, PI),
  978    '$pi_head'(PI, Head),
  979    source_file(Head, File),
  980    expansion_hook(P),
  981    source_file(P, File),
  982    !,
  983    setup_call_cleanup(
  984        b_setval('$autoload_warning', true),
  985        print_message(warning,
  986                      deprecated(autoload(TargetModule, File, PI, expansion))),
  987        nb_delete('$autoload_warning')).
  988warn_autoload(_, _).
  989
  990expansion_hook(user:goal_expansion(_,_)).
  991expansion_hook(user:goal_expansion(_,_,_,_)).
  992expansion_hook(system:goal_expansion(_,_)).
  993expansion_hook(system:goal_expansion(_,_,_,_)).
 nowarn_autoload(+TargetModule, +LoadModulePI) is semidet
True when LoadModule:'$nowarn_autoload'(PI,TargetModule) is defined and true.
To be done
- As is, these facts must be defined by the library being autoloaded. Possibly we want a specific autoload declaration. As all this only affects the Prolog libraries, we can always change this. One option might be this, where How is one of true, false or warning.

:- autoloadable(PI, How)

 1008nowarn_autoload(TargetModule, LoadModule:PI) :-
 1009    NoWarn = LoadModule:'$nowarn_autoload'(PI,TargetModule),
 1010    '$c_current_predicate'(_, NoWarn),
 1011    \+ '$get_predicate_attribute'(NoWarn, imported, _From),
 1012    call(NoWarn).
 1013
 1014
 1015                 /*******************************
 1016                 *             REQUIRE          *
 1017                 *******************************/
 require(:ListOfPredIndicators) is det
Register the predicates in ListOfPredIndicators for autoloading using autoload/2 if they are not system predicates.
 1024require(M:Spec) :-
 1025    (   is_list(Spec)
 1026    ->  List = Spec
 1027    ;   phrase(comma_list(Spec), List)
 1028    ), !,
 1029    require(List, M, FromLib),
 1030    keysort(FromLib, Sorted),
 1031    by_file(Sorted, Autoload),
 1032    forall('$member'(File-Import, Autoload),
 1033           autoload(M:File, Import)).
 1034require(_:Spec) :-
 1035    '$type_error'(list, Spec).
 1036
 1037require([],_, []).
 1038require([H|T], M, Needed) :-
 1039   '$pi_head'(H, Head),
 1040   (   '$get_predicate_attribute'(system:Head, defined, 1)
 1041   ->  require(T, M, Needed)
 1042   ;   '$pi_head'(Module:Name/Arity, M:Head),
 1043       (   '$find_library'(Module, Name, Arity, LoadModule, Library)
 1044       ->  (   current_predicate(LoadModule:Name/Arity)
 1045           ->  Module:import(LoadModule:Name/Arity),
 1046               require(T, M, Needed)
 1047           ;   Needed = [Library-H|More],
 1048               require(T, M, More)
 1049           )
 1050       ;   print_message(error, error(existence_error(procedure, Name/Arity), _)),
 1051           require(T, M, Needed)
 1052       )
 1053   ).
 1054
 1055by_file([], []).
 1056by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :-
 1057    on_path(File, Spec),
 1058    same_file(T0, File, PIs, T1),
 1059    by_file(T1, T).
 1060
 1061on_path(Library, library(Base)) :-
 1062    file_base_name(Library, Base),
 1063    findall(Path, plain_source(library(Base), Path), [Library]),
 1064    !.
 1065on_path(Library, Library).
 1066
 1067plain_source(Spec, Path) :-
 1068    absolute_file_name(Spec, PathExt,
 1069                       [ file_type(prolog),
 1070                         access(read),
 1071                         file_errors(fail),
 1072                         solutions(all)
 1073                       ]),
 1074    file_name_extension(Path, _, PathExt).
 1075
 1076same_file([File-PI|T0], File, [PI|PIs], T) :-
 1077    !,
 1078    same_file(T0, File, PIs, T).
 1079same_file(List, _, [], List).
 1080
 1081comma_list(Var) -->
 1082    { var(Var),
 1083      !,
 1084      '$instantiation_error'(Var)
 1085    }.
 1086comma_list((A,B)) -->
 1087    !,
 1088    comma_list(A),
 1089    comma_list(B).
 1090comma_list(A) -->
 1091    [A]