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-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(0),
   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, []).
 $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.
   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    !.
 $in_library(+Name, +Arity, -Path) is semidet
$in_library(-Name, -Arity, -Path) is nondet
Is true if Name/Arity is in the autoload libraries.
  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).
 $define_predicate(:Head)
Make sure PredInd can be called. First test if the predicate is defined. If not, invoke the autoloader.
  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.
 $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.
  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).
 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.
  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                ********************************/
 reload_library_index
Reload the index on the next call
  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(_)).
 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.
  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    ).
 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.
  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').
 win_open_index(+Index, -Stream) is det
Open an 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                ********************************/
 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
  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).
 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.
  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).
 index_files(+Files, +Directory, +Out:stream) is det
Write index for Files in Directory to the stream Out.
  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', []).
 exports(+File, -Module, -Exports) is det
 exports(+File, -Module, -Exports, -Meta) is det
Get the exports from a library as a list of PIs. Exports are all exports of the module header (including op/3 terms) and encountered export/1 directives. Meta are all heads in meta_predicate/1 declarations.
  525:- public exports/3.                            % using by library(prolog_deps).
  526exports(File, Module, Exports) :-
  527    exports(File, Module, Exports, _Meta, _Public).
  528
  529exports(File, Module, Exports, Meta, Public) :-
  530    setup_call_cleanup(
  531        push_prolog_flag(xref, true),
  532        snapshot(exports_(File, Module, Exports, Meta, Public)),
  533        pop_prolog_flag(xref)).
  534
  535exports_(File, Module, Exports, Meta, Public) :-
  536    State = state(true, _, [], [], []),
  537    (   '$source_term'(File,
  538                       _Read,_RLayout,
  539                       Term,_TermLayout,
  540                       _Stream,
  541                       [ syntax_errors(quiet)
  542                       ]),
  543        (   Term = (:- module(M,ModuleExports)),
  544            is_list(ModuleExports),
  545            arg(1, State, true)
  546        ->  nb_setarg(1, State, false),
  547            nb_setarg(2, State, M),
  548            nb_setarg(3, State, ModuleExports),
  549            fail
  550        ;   nb_setarg(1, State, false),
  551            fail
  552        ;   Term = (:- export(Export))
  553        ->  phrase(export_pi(Export), PIs),
  554            arg(3, State, E0),
  555            '$append'(E0, PIs, E1),
  556            nb_setarg(3, State, E1),
  557            fail
  558        ;   Term = (:- public(Public))
  559        ->  phrase(export_pi(Public), PIs),
  560            arg(5, State, E0),
  561            '$append'(E0, PIs, E1),
  562            nb_setarg(5, State, E1),
  563            fail
  564        ;   Term = (:- meta_predicate(Heads)),
  565            phrase(meta(Heads), M1),
  566            arg(4, State, M0),
  567            '$append'(M0, M1, M2),
  568            nb_setarg(4, State, M2)
  569        ;   Term = (:- use_foreign_library(Lib)),
  570            nonvar(Lib),
  571            arg(2, State, M),
  572            atom(M)
  573        ->  catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true),
  574            fail
  575        ;   Term = (:- Directive),
  576            nonvar(Directive)
  577        ->  fail
  578        ;   Term == []                          % Expansion for conditionals
  579        ->  fail
  580        ;   !
  581        )
  582    ;   true
  583    ),
  584    arg(2, State, Module),
  585    arg(3, State, Exports),
  586    arg(4, State, Meta),
  587    arg(5, State, Public).
  588
  589export_pi(Var) -->
  590    { var(Var) },
  591    !.
  592export_pi((A,B)) -->
  593    !,
  594    export_pi(A),
  595    export_pi(B).
  596export_pi(PI) -->
  597    { ground(PI) },
  598    [PI].
  599
  600meta(Var) -->
  601    { var(Var) },
  602    !.
  603meta((A,B)) -->
  604    !,
  605    meta(A),
  606    meta(B).
  607meta(Head) -->
  608    { callable(Head) },
  609    [Head].
  610
  611
  612                 /*******************************
  613                 *            EXTENDING         *
  614                 *******************************/
 autoload_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.

  631autoload_path(Alias) :-
  632    (   user:file_search_path(autoload, Alias)
  633    ->  true
  634    ;   assertz(user:file_search_path(autoload, Alias)),
  635        reload_library_index
  636    ).
  637
  638system:term_expansion((:- autoload_path(Alias)),
  639                      [ user:file_search_path(autoload, Alias),
  640                        (:- reload_library_index)
  641                      ]).
  642
  643
  644		 /*******************************
  645		 *      RUNTIME AUTOLOADER	*
  646		 *******************************/
Provide PI by autoloading. This checks:
  656'$autoload'(PI) :-
  657    source_location(File, _Line),
  658    !,
  659    setup_call_cleanup(
  660        '$start_aux'(File, Context),
  661        '$autoload2'(PI),
  662        '$end_aux'(File, Context)).
  663'$autoload'(PI) :-
  664    '$autoload2'(PI).
  665
  666'$autoload2'(PI) :-
  667    setup_call_cleanup(
  668        leave_sandbox(Old),
  669        '$autoload3'(PI),
  670        restore_sandbox(Old)).
  671
  672leave_sandbox(Sandboxed) :-
  673    current_prolog_flag(sandboxed_load, Sandboxed),
  674    set_prolog_flag(sandboxed_load, false).
  675restore_sandbox(Sandboxed) :-
  676    set_prolog_flag(sandboxed_load, Sandboxed).
  677
  678'$autoload3'(PI) :-
  679    autoload_from(PI, LoadModule, FullFile),
  680    do_autoload(FullFile, PI, LoadModule).
 autoload_from(+PI, -LoadModule, -PlFile) is semidet
True when PI can be defined by loading File which is defined the module LoadModule.
  687autoload_from(Module:PI, LoadModule, FullFile) :-
  688    autoload_in(Module, explicit),
  689    current_autoload(Module:File, Ctx, import(Imports)),
  690    memberchk(PI, Imports),
  691    library_info(File, Ctx, FullFile, LoadModule, Exports),
  692    (   pi_in_exports(PI, Exports)
  693    ->  !
  694    ;   autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)),
  695        fail
  696    ).
  697autoload_from(Module:Name/Arity, LoadModule, FullFile) :-
  698    autoload_in(Module, explicit),
  699    PI = Name/Arity,
  700    current_autoload(Module:File, Ctx, all),
  701    library_info(File, Ctx, FullFile, LoadModule, Exports),
  702    pi_in_exports(PI, Exports).
  703autoload_from(Module:Name/Arity, LoadModule, Library) :-
  704    autoload_in(Module, general),
  705    '$find_library'(Module, Name, Arity, LoadModule, Library).
  706
  707:- public autoload_in/2.                        % used in syspred
  708
  709autoload_in(Module, How) :-
  710    current_prolog_flag(autoload, AutoLoad),
  711    autoload_in(AutoLoad, How, Module),
  712    !.
 autoload_in(+AutoloadFlag, +AutoloadMode, +TargetModule) is semidet
  716autoload_in(true,             _,        _).
  717autoload_in(explicit,         explicit, _).
  718autoload_in(user,             _,        user).
  719autoload_in(user_or_explicit, explicit, _).
  720autoload_in(user_or_explicit, _,        user).
 do_autoload(Library, :PI, +LoadModule) is det
Load File, importing PI into the qualified module. File is known to define LoadModule. There are three cases:
Arguments:
Library- is an absolute file name, either without extension or with the source (.pl) extension.
  739do_autoload(Library, Module:Name/Arity, LoadModule) :-
  740    functor(Head, Name, Arity),
  741    '$update_autoload_level'([autoload(true)], Old),
  742    verbose_autoload(Module:Name/Arity, Library),
  743    loadable_file(Library, File),
  744    '$compilation_mode'(OldComp, database),
  745    (   Module == LoadModule
  746    ->  ensure_loaded(Module:File)
  747    ;   (   '$c_current_predicate'(_, LoadModule:Head),
  748            '$get_predicate_attribute'(LoadModule:Head, defined, 1),
  749            \+ '$loading'(Library)
  750        ->  Module:import(LoadModule:Name/Arity)
  751        ;   use_module(Module:File, [Name/Arity])
  752        ),
  753        warn_autoload(Module, LoadModule:Name/Arity)
  754    ),
  755    '$set_compilation_mode'(OldComp),
  756    '$set_autoload_level'(Old),
  757    '$c_current_predicate'(_, Module:Head).
  758
  759loadable_file(PlFile, File) :-
  760    exists_file(PlFile), !,
  761    File = PlFile.
  762loadable_file(PlFile, Base) :-
  763    file_name_extension(Base, pl, PlFile),
  764    !.
  765loadable_file(File, File).
  766
  767verbose_autoload(PI, Library) :-
  768    current_prolog_flag(verbose_autoload, true),
  769    !,
  770    set_prolog_flag(verbose_autoload, false),
  771    print_message(informational, autoload(PI, Library)),
  772    set_prolog_flag(verbose_autoload, true).
  773verbose_autoload(PI, Library) :-
  774    print_message(silent, autoload(PI, Library)).
 autoload_call(:Goal)
Call Goal, optionally autoloading it first.
  780autoload_call(Goal) :-
  781    '$pi_head'(PI, Goal),
  782    (   current_predicate(PI)
  783    ->  true
  784    ;   '$autoload'(PI)
  785    ),
  786    call(Goal).
 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.
  794:- public                               % used from predicate_property/2
  795    autoloadable/2.  796
  797autoloadable(M:Head, FullFile) :-
  798    atom(M),
  799    current_module(M),
  800    autoload_in(M, explicit),
  801    (   callable(Head)
  802    ->  goal_name_arity(Head, Name, Arity),
  803        autoload_from(M:Name/Arity, _, FullFile)
  804    ;   findall((M:H)-F, autoloadable_2(M:H, F), Pairs),
  805        (   '$member'(M:Head-FullFile, Pairs)
  806        ;   current_autoload(M:File, Ctx, all),
  807            library_info(File, Ctx, FullFile, _, Exports),
  808            '$member'(PI, Exports),
  809            '$pi_head'(PI, Head),
  810            \+ memberchk(M:Head-_, Pairs)
  811        )
  812    ).
  813autoloadable(M:Head, FullFile) :-
  814    (   var(M)
  815    ->  autoload_in(any, general)
  816    ;   autoload_in(M, general)
  817    ),
  818    (   callable(Head)
  819    ->  goal_name_arity(Head, Name, Arity),
  820        (   '$find_library'(_, Name, Arity, _, FullFile)
  821        ->  true
  822        )
  823    ;   '$in_library'(Name, Arity, autoload),
  824        functor(Head, Name, Arity)
  825    ).
  826
  827
  828autoloadable_2(M:Head, FullFile) :-
  829    current_autoload(M:File, Ctx, import(Imports)),
  830    library_info(File, Ctx, FullFile, _LoadModule, _Exports),
  831    '$member'(PI, Imports),
  832    '$pi_head'(PI, Head).
  833
  834goal_name_arity(Head, Name, Arity) :-
  835    compound(Head),
  836    !,
  837    compound_name_arity(Head, Name, Arity).
  838goal_name_arity(Head, Head, 0).
 library_info(+Spec, +AutoloadContext, -FullFile, -Module, -Exports)
Find information about a library. Spec is the file specification as it appears in the autoload/1,2 call. AutoloadContext is a term File:Line, providing the location of the directive.
Arguments:
FullFile- is the source (.pl) file in canonical (absolute) notation.
Module- is the module defined in FullFile
Exports- is a list of predicate indicators.
  851library_info(Spec, _, FullFile, Module, Exports) :-
  852    '$resolved_source_path'(Spec, FullFile, []),
  853    !,
  854    (   \+ '$loading_file'(FullFile, _Queue, _LoadThread)
  855    ->  '$current_module'(Module, FullFile),
  856        '$module_property'(Module, exports(Exports))
  857    ;   library_info_from_file(FullFile, _, Module, Exports)
  858    ).
  859library_info(Spec, Context, FullFile, Module, Exports) :-
  860    (   Context = (Path:_Line)
  861    ->  Extra = [relative_to(Path)]
  862    ;   Extra = []
  863    ),
  864    (   absolute_file_name(Spec, AbsFile,
  865                           [ file_type(prolog),
  866                             access(read),
  867                             file_errors(fail)
  868                           | Extra
  869                           ])
  870    ->  library_info_from_file(AbsFile, FullFile, Module, Exports),
  871        '$register_resolved_source_path'(Spec, FullFile)
  872    ;   absolute_file_name(Spec, FullFile,
  873                           [ file_type(prolog),
  874                             solutions(all),
  875                             file_errors(fail)
  876                           | Extra
  877                           ]),
  878        source_file(FullFile),
  879        '$current_module'(Module, FullFile)
  880    ->  '$module_property'(Module, exports(Exports))
  881    ;   autoload_error(Context, no_file(Spec)),
  882        fail
  883    ).
  884
  885library_info_from_file(QlfFile, PlFile, Module, Exports) :-
  886    file_name_extension(_, qlf, QlfFile),
  887    !,
  888    '$qlf_module'(QlfFile, Info),
  889    _{module:Module, exports:Exports, file:PlFile} :< Info.
  890library_info_from_file(PlFile, PlFile, Module, Exports) :-
  891    setup_call_cleanup(
  892        '$set_source_module'(OldModule, system),
  893        setup_call_cleanup(
  894            '$open_source'(PlFile, In, State, [], []),
  895            '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream,
  896                            [PlFile], []),
  897            '$close_source'(State, true)),
  898        '$set_source_module'(OldModule)),
  899    (   Term = (:- module(Module, Exports))
  900    ->  !
  901    ;   nonvar(Term),
  902        skip_header(Term)
  903    ->  fail
  904    ;   '$domain_error'(module_header, Term)
  905    ).
  906
  907skip_header(begin_of_file).
  908
  909
  910:- dynamic printed/3.  911:- volatile printed/3.  912
  913autoload_error(Context, Error) :-
  914    suppress(Context, Error),
  915    !.
  916autoload_error(Context, Error) :-
  917    get_time(Now),
  918    assertz(printed(Context, Error, Now)),
  919    print_message(warning, error(autoload(Error), autoload(Context))).
  920
  921suppress(Context, Error) :-
  922    printed(Context, Error, Printed),
  923    get_time(Now),
  924    (   Now - Printed < 1
  925    ->  true
  926    ;   retractall(printed(Context, Error, _)),
  927        fail
  928    ).
  929
  930
  931		 /*******************************
  932		 *            CALLBACK		*
  933		 *******************************/
  934
  935:- public
  936    set_autoload/1.
 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.
  945set_autoload(FlagValue) :-
  946    current_prolog_flag(autoload, FlagValue),
  947    !.
  948set_autoload(FlagValue) :-
  949    \+ autoload_in(FlagValue, explicit, any),
  950    !,
  951    setup_call_cleanup(
  952        nb_setval('$autoload_disabling', true),
  953        materialize_autoload(Count),
  954        nb_delete('$autoload_disabling')),
  955    print_message(informational, autoload(disabled(Count))).
  956set_autoload(_).
  957
  958materialize_autoload(Count) :-
  959    State = state(0),
  960    forall(current_predicate(M:'$autoload'/3),
  961           materialize_autoload(M, State)),
  962    arg(1, State, Count).
  963
  964materialize_autoload(M, State) :-
  965    (   current_autoload(M:File, Context, Import),
  966        library_info(File, Context, PlFile, _LoadModule, _Exports),
  967        arg(1, State, N0),
  968        N is N0+1,
  969        nb_setarg(1, State, N),
  970        loadable_file(PlFile, LoadFile),
  971        (   Import == all
  972        ->  verbose_autoload(M:all, PlFile),
  973            use_module(M:LoadFile)
  974        ;   Import = import(Preds)
  975        ->  verbose_autoload(M:Preds, PlFile),
  976            use_module(M:LoadFile, Preds)
  977        ),
  978        fail
  979    ;   true
  980    ),
  981    abolish(M:'$autoload'/3).
  982
  983
  984		 /*******************************
  985		 *          AUTOLOAD/2		*
  986		 *******************************/
  987
  988autoload(M:File) :-
  989    (   \+ autoload_in(M, explicit)
  990    ;   nb_current('$autoload_disabling', true)
  991    ),
  992    !,
  993    use_module(M:File).
  994autoload(M:File) :-
  995    '$must_be'(filespec, File),
  996    source_context(Context),
  997    assert_autoload(M, File, Context, all).
  998
  999autoload(M:File, Imports) :-
 1000    (   \+ autoload_in(M, explicit)
 1001    ;   nb_current('$autoload_disabling', true)
 1002    ),
 1003    !,
 1004    use_module(M:File, Imports).
 1005autoload(M:File, Imports0) :-
 1006    '$must_be'(filespec, File),
 1007    valid_imports(Imports0, Imports),
 1008    source_context(Context),
 1009    register_autoloads(Imports, M, File, Context),
 1010    assert_autoload(M, File, Context, import(Imports)).
 1011
 1012source_context(Path:Line) :-
 1013    source_location(Path, Line),
 1014    !.
 1015source_context(-).
 assert_autoload(+Module, +File, +Context, +Imports) is det
Assert that Module can autoload predicates defines in Imports by loading File. Context provides the file owner of the declaration.
Arguments:
Imports- is either all or imports(List). That latter comes from an autoload/2 directive.
 1025assert_autoload(Module, File, _, Imports) :-
 1026    current_autoload(Module:File, _, Imports),
 1027    !.
 1028assert_autoload(Module, File, Context, Imports) :-
 1029    set_admin_properties(Module),
 1030    Clause = Module:'$autoload'(File, Context, Imports),
 1031    '$initialization_context'(Source, Ctx),
 1032    '$store_admin_clause2'(Clause, _Layout, Source, Ctx).
 1033
 1034set_admin_properties(Module) :-
 1035    predicate_property(Module:'$autoload'(_,_,_), discontiguous),
 1036    !.
 1037set_admin_properties(Module) :-
 1038    discontiguous(Module:'$autoload'/3).
 1039
 1040valid_imports(Imports0, Imports) :-
 1041    '$must_be'(list, Imports0),
 1042    valid_import_list(Imports0, Imports).
 1043
 1044valid_import_list([], []).
 1045valid_import_list([H0|T0], [H|T]) :-
 1046    '$pi_head'(H0, Head),
 1047    '$pi_head'(H, Head),
 1048    valid_import_list(T0, T).
 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.
Arguments:
File- is the first argument of autoload/1,2
 1057register_autoloads([], _, _, _).
 1058register_autoloads([PI|T], Module, File, Context) :-
 1059    PI = Name/Arity,
 1060    functor(Head, Name, Arity),
 1061    (   '$get_predicate_attribute'(Module:Head, autoload, 1)
 1062    ->  (   current_autoload(Module:_File0, _Ctx0, import(Imports)),
 1063            memberchk(PI, Imports)
 1064        ->  '$permission_error'(redefine, imported_procedure, PI),
 1065            fail
 1066        ;   Done = true
 1067        )
 1068    ;   '$c_current_predicate'(_, Module:Head), % no auto-import
 1069        '$get_predicate_attribute'(Module:Head, imported, From)
 1070    ->  (   (   '$resolved_source_path'(File, FullFile)
 1071            ->  true
 1072            ;   '$resolve_source_path'(File, FullFile, [])
 1073            ),
 1074            module_property(From, file(FullFile))
 1075        ->  Done = true
 1076        ;   print_message(warning,
 1077                          autoload(already_defined(Module:PI, From))),
 1078            Done = true
 1079        )
 1080    ;   true
 1081    ),
 1082    (   Done == true
 1083    ->  true
 1084    ;   '$set_predicate_attribute'(Module:Head, autoload, 1)
 1085    ),
 1086    register_autoloads(T, Module, File, Context).
 1087
 1088pi_in_exports(PI, Exports) :-
 1089    '$member'(E, Exports),
 1090    canonical_pi(E, PI),
 1091    !.
 1092
 1093canonical_pi(Var, _) :-
 1094    var(Var), !, fail.
 1095canonical_pi(Name/Arity, Name/Arity).
 1096canonical_pi(Name//A0,   Name/Arity) :-
 1097    Arity is A0 + 2.
 1098
 1099current_autoload(M:File, Context, Term) :-
 1100    '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1),
 1101    M:'$autoload'(File, Context, Term).
 1102
 1103		 /*******************************
 1104		 *            CHECK		*
 1105		 *******************************/
 warn_autoload(+TargetModule, :PI) is det
Arguments:
PI- is of the shape LoadModule:Name/Arity.
 1111warn_autoload(TargetModule, PI) :-
 1112    current_prolog_flag(warn_autoload, true),
 1113    \+ current_prolog_flag(xref, true),
 1114    \+ nb_current('$autoload_warning', true),
 1115    \+ nowarn_autoload(TargetModule, PI),
 1116    '$pi_head'(PI, Head),
 1117    source_file(Head, File),
 1118    '$source_defines_expansion'(File),
 1119    setup_call_cleanup(
 1120        b_setval('$autoload_warning', true),
 1121        print_message(warning,
 1122                      deprecated(autoload(TargetModule, File, PI, expansion))),
 1123        nb_delete('$autoload_warning')).
 1124warn_autoload(_, _).
 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)

 1139nowarn_autoload(TargetModule, LoadModule:PI) :-
 1140    NoWarn = LoadModule:'$nowarn_autoload'(PI,TargetModule),
 1141    '$c_current_predicate'(_, NoWarn),
 1142    \+ '$get_predicate_attribute'(NoWarn, imported, _From),
 1143    call(NoWarn).
 1144
 1145
 1146                 /*******************************
 1147                 *             REQUIRE          *
 1148                 *******************************/
 require(:ListOfPredIndicators) is det
Register the predicates in ListOfPredIndicators for autoloading using autoload/2 if they are not system predicates.
 1155require(M:Spec) :-
 1156    (   is_list(Spec)
 1157    ->  List = Spec
 1158    ;   phrase(comma_list(Spec), List)
 1159    ), !,
 1160    require(List, M, FromLib),
 1161    keysort(FromLib, Sorted),
 1162    by_file(Sorted, Autoload),
 1163    forall('$member'(File-Import, Autoload),
 1164           autoload(M:File, Import)).
 1165require(_:Spec) :-
 1166    '$type_error'(list, Spec).
 1167
 1168require([],_, []).
 1169require([H|T], M, Needed) :-
 1170   '$pi_head'(H, Head),
 1171   (   '$get_predicate_attribute'(system:Head, defined, 1)
 1172   ->  require(T, M, Needed)
 1173   ;   '$pi_head'(Module:Name/Arity, M:Head),
 1174       (   '$find_library'(Module, Name, Arity, LoadModule, Library)
 1175       ->  (   current_predicate(LoadModule:Name/Arity)
 1176           ->  Module:import(LoadModule:Name/Arity),
 1177               require(T, M, Needed)
 1178           ;   Needed = [Library-H|More],
 1179               require(T, M, More)
 1180           )
 1181       ;   print_message(error, error(existence_error(procedure, Name/Arity), _)),
 1182           require(T, M, Needed)
 1183       )
 1184   ).
 1185
 1186by_file([], []).
 1187by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :-
 1188    on_path(File, Spec),
 1189    same_file(T0, File, PIs, T1),
 1190    by_file(T1, T).
 1191
 1192on_path(Library, library(Base)) :-
 1193    file_base_name(Library, Base),
 1194    findall(Path, plain_source(library(Base), Path), [Library]),
 1195    !.
 1196on_path(Library, Library).
 1197
 1198plain_source(Spec, Path) :-
 1199    absolute_file_name(Spec, PathExt,
 1200                       [ file_type(prolog),
 1201                         access(read),
 1202                         file_errors(fail),
 1203                         solutions(all)
 1204                       ]),
 1205    file_name_extension(Path, _, PathExt).
 1206
 1207same_file([File-PI|T0], File, [PI|PIs], T) :-
 1208    !,
 1209    same_file(T0, File, PIs, T).
 1210same_file(List, _, [], List).
 1211
 1212comma_list(Var) -->
 1213    { var(Var),
 1214      !,
 1215      '$instantiation_error'(Var)
 1216    }.
 1217comma_list((A,B)) -->
 1218    !,
 1219    comma_list(A),
 1220    comma_list(B).
 1221comma_list(A) -->
 1222    [A]