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    (   current_prolog_flag(xref, Old)
  531    ->  true
  532    ;   Old = false
  533    ),
  534    setup_call_cleanup(
  535        set_prolog_flag(xref, true),
  536        snapshot(exports_(File, Module, Exports, Meta, Public)),
  537        set_prolog_flag(xref, Old)).
  538
  539exports_(File, Module, Exports, Meta, Public) :-
  540    State = state(true, _, [], [], []),
  541    (   '$source_term'(File,
  542                       _Read,_RLayout,
  543                       Term,_TermLayout,
  544                       _Stream,
  545                       [ syntax_errors(quiet)
  546                       ]),
  547        (   Term = (:- module(M,ModuleExports)),
  548            is_list(ModuleExports),
  549            arg(1, State, true)
  550        ->  nb_setarg(1, State, false),
  551            nb_setarg(2, State, M),
  552            nb_setarg(3, State, ModuleExports),
  553            fail
  554        ;   nb_setarg(1, State, false),
  555            fail
  556        ;   Term = (:- export(Export))
  557        ->  phrase(export_pi(Export), PIs),
  558            arg(3, State, E0),
  559            '$append'(E0, PIs, E1),
  560            nb_setarg(3, State, E1),
  561            fail
  562        ;   Term = (:- public(Public))
  563        ->  phrase(export_pi(Public), PIs),
  564            arg(5, State, E0),
  565            '$append'(E0, PIs, E1),
  566            nb_setarg(5, State, E1),
  567            fail
  568        ;   Term = (:- meta_predicate(Heads)),
  569            phrase(meta(Heads), M1),
  570            arg(4, State, M0),
  571            '$append'(M0, M1, M2),
  572            nb_setarg(4, State, M2)
  573        ;   Term = (:- use_foreign_library(Lib)),
  574            nonvar(Lib),
  575            arg(2, State, M),
  576            atom(M)
  577        ->  catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true),
  578            fail
  579        ;   Term = (:- Directive),
  580            nonvar(Directive)
  581        ->  fail
  582        ;   Term == []                          % Expansion for conditionals
  583        ->  fail
  584        ;   !
  585        )
  586    ;   true
  587    ),
  588    arg(2, State, Module),
  589    arg(3, State, Exports),
  590    arg(4, State, Meta),
  591    arg(5, State, Public).
  592
  593export_pi(Var) -->
  594    { var(Var) },
  595    !.
  596export_pi((A,B)) -->
  597    !,
  598    export_pi(A),
  599    export_pi(B).
  600export_pi(PI) -->
  601    { ground(PI) },
  602    [PI].
  603
  604meta(Var) -->
  605    { var(Var) },
  606    !.
  607meta((A,B)) -->
  608    !,
  609    meta(A),
  610    meta(B).
  611meta(Head) -->
  612    { callable(Head) },
  613    [Head].
  614
  615
  616                 /*******************************
  617                 *            EXTENDING         *
  618                 *******************************/
 autoload_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.

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

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