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            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, :, 2, _LoadModule, _Library) :-
   88    !, fail.
   89'$find_library'(Module, Name, Arity, LoadModule, Library) :-
   90    load_library_index(Name, Arity),
   91    functor(Head, Name, Arity),
   92    (   library_index(Head, Module, Library),
   93        LoadModule = Module
   94    ;   library_index(Head, LoadModule, Library)
   95    ),
   96    !.
 $in_library(+Name, +Arity, -Path) is semidet
$in_library(-Name, -Arity, -Path) is nondet
Is true if Name/Arity is in the autoload libraries.
  103'$in_library'(Name, Arity, Path) :-
  104    atom(Name), integer(Arity),
  105    !,
  106    Name/Arity \= (:)/2,
  107    load_library_index(Name, Arity),
  108    functor(Head, Name, Arity),
  109    library_index(Head, _, Path).
  110'$in_library'(Name, Arity, Path) :-
  111    load_library_index(Name, Arity),
  112    library_index(Head, _, Path),
  113    Head \= _:_,
  114    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.
  121:- meta_predicate
  122    '$define_predicate'(:).  123
  124'$define_predicate'(Head) :-
  125    '$defined_predicate'(Head),
  126    !.
  127'$define_predicate'(Term) :-
  128    Term = Module:Head,
  129    (   compound(Head)
  130    ->  compound_name_arity(Head, Name, Arity)
  131    ;   Name = Head, Arity = 0
  132    ),
  133    '$undefined_procedure'(Module, Name, Arity, retry).
  134
  135
  136                /********************************
  137                *          UPDATE INDEX         *
  138                ********************************/
  139
  140:- thread_local
  141    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.
  155'$update_library_index'(Options) :-
  156    setof(Dir, writable_indexed_directory(Dir, Options), Dirs),
  157    !,
  158    setup_call_cleanup(
  159        asserta(silent, Ref),
  160        guarded_make_library_index(Dirs),
  161        erase(Ref)),
  162    (   flag('$modified_index', true, false)
  163    ->  reload_library_index
  164    ;   true
  165    ).
  166'$update_library_index'(_).
  167
  168guarded_make_library_index([]).
  169guarded_make_library_index([Dir|Dirs]) :-
  170    (   catch(make_library_index(Dir), E,
  171              print_message(error, E))
  172    ->  true
  173    ;   print_message(warning, goal_failed(make_library_index(Dir)))
  174    ),
  175    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.
  182writable_indexed_directory(Dir, Options) :-
  183    current_prolog_flag(home, Home),
  184    writable_indexed_directory(Dir),
  185    (   sub_atom(Dir, 0, _, _, Home)
  186    ->  '$option'(system(true), Options, false)
  187    ;   '$option'(user(true), Options, true)
  188    ).
  189
  190writable_indexed_directory(Dir) :-
  191    index_file_name(IndexFile, autoload('INDEX'), [access([read,write])]),
  192    file_directory_name(IndexFile, Dir).
  193writable_indexed_directory(Dir) :-
  194    absolute_file_name(library('MKINDEX'),
  195                       [ file_type(prolog),
  196                         access(read),
  197                         solutions(all),
  198                         file_errors(fail)
  199                       ], MkIndexFile),
  200    file_directory_name(MkIndexFile, Dir),
  201    plfile_in_dir(Dir, 'INDEX', _, IndexFile),
  202    access_file(IndexFile, write).
  203
  204
  205                /********************************
  206                *           LOAD INDEX          *
  207                ********************************/
 reload_library_index
Reload the index on the next call
  213reload_library_index :-
  214    context_module(M),
  215    reload_library_index(M).
  216
  217reload_library_index(M) :-
  218    with_mutex('$autoload', clear_library_index(M)).
  219
  220clear_library_index(M) :-
  221    retractall(M:library_index(_, _, _)),
  222    retractall(M:autoload_directories(_)),
  223    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.
  233:- meta_predicate load_library_index(?, ?, :).  234:- public load_library_index/3.  235
  236load_library_index(Name, Arity) :-
  237    load_library_index(Name, Arity, autoload('INDEX')).
  238
  239load_library_index(Name, Arity, M:_Spec) :-
  240    atom(Name), integer(Arity),
  241    functor(Head, Name, Arity),
  242    M:library_index(Head, _, _),
  243    !.
  244load_library_index(_, _, Spec) :-
  245    notrace(with_mutex('$autoload', load_library_index_p(Spec))).
  246
  247load_library_index_p(M:_) :-
  248    M:index_checked_at(Time),
  249    get_time(Now),
  250    Now-Time < 60,
  251    !.
  252load_library_index_p(M:Spec) :-
  253    findall(Index, index_file_name(Index, Spec, [access(read)]), List0),
  254    '$list_to_set'(List0, List),
  255    retractall(M:index_checked_at(_)),
  256    get_time(Now),
  257    assert(M:index_checked_at(Now)),
  258    (   M:autoload_directories(List)
  259    ->  true
  260    ;   retractall(M:library_index(_, _, _)),
  261        retractall(M:autoload_directories(_)),
  262        read_index(List, M),
  263        assert(M:autoload_directories(List))
  264    ).
 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.
  274index_file_name(IndexFile, FileSpec, Options) :-
  275    absolute_file_name(FileSpec,
  276                       IndexFile,
  277                       [ file_type(prolog),
  278                         solutions(all),
  279                         file_errors(fail)
  280                       | Options
  281                       ]).
  282
  283read_index([], _) :- !.
  284read_index([H|T], M) :-
  285    !,
  286    read_index(H, M),
  287    read_index(T, M).
  288read_index(Index, M) :-
  289    print_message(silent, autoload(read_index(Dir))),
  290    file_directory_name(Index, Dir),
  291    setup_call_cleanup(
  292        '$push_input_context'(autoload_index),
  293        setup_call_cleanup(
  294            open(Index, read, In),
  295            read_index_from_stream(Dir, In, M),
  296            close(In)),
  297        '$pop_input_context').
  298
  299read_index_from_stream(Dir, In, M) :-
  300    repeat,
  301        read(In, Term),
  302        assert_index(Term, Dir, M),
  303    !.
  304
  305assert_index(end_of_file, _, _) :- !.
  306assert_index(index(Term, Module, File), Dir, M) :-
  307    !,
  308    atomic_list_concat([Dir, '/', File], Path),
  309    assertz(M:library_index(Term, Module, Path)),
  310    fail.
  311assert_index(index(Name, Arity, Module, File), Dir, M) :-
  312    !,                                          % Read old index format
  313    functor(Head, Name, Arity),
  314    head_meta_any(Head),
  315    assert_index(index(Head, Module, File), Dir, M).
  316assert_index(Term, Dir, _) :-
  317    print_message(error, illegal_autoload_index(Dir, Term)),
  318    fail.
  319
  320
  321                /********************************
  322                *       CREATE INDEX.pl         *
  323                ********************************/
 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
  336make_library_index(Dir0) :-
  337    forall(absolute_file_name(Dir0, Dir,
  338                              [ expand(true),
  339                                file_type(directory),
  340                                file_errors(fail),
  341                                solutions(all)
  342                              ]),
  343           make_library_index2(Dir)).
  344
  345make_library_index2(Dir) :-
  346    plfile_in_dir(Dir, 'MKINDEX', _MkIndex, AbsMkIndex),
  347    access_file(AbsMkIndex, read),
  348    !,
  349    load_files(user:AbsMkIndex, [silent(true)]).
  350make_library_index2(Dir) :-
  351    findall(Pattern, source_file_pattern(Pattern), PatternList),
  352    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.
  367make_library_index(Dir0, Patterns) :-
  368    forall(absolute_file_name(Dir0, Dir,
  369                              [ expand(true),
  370                                file_type(directory),
  371                                file_errors(fail),
  372                                solutions(all)
  373                              ]),
  374           make_library_index2(Dir, Patterns)).
  375
  376make_library_index2(Dir, Patterns) :-
  377    plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex),
  378    ensure_slash(Dir, DirS),
  379    pattern_files(Patterns, DirS, Files),
  380    (   library_index_out_of_date(Dir, AbsIndex, Files)
  381    ->  do_make_library_index(AbsIndex, DirS, Files),
  382        set_flag('$modified_index', true)
  383    ;   true
  384    ).
  385
  386ensure_slash(Dir, DirS) :-
  387    (   sub_atom(Dir, _, _, 0, /)
  388    ->  DirS = Dir
  389    ;   atom_concat(Dir, /, DirS)
  390    ).
  391
  392source_file_pattern(Pattern) :-
  393    user:prolog_file_type(PlExt, prolog),
  394    PlExt \== qlf,
  395    atom_concat('*.', PlExt, Pattern).
  396
  397plfile_in_dir(Dir, Base, PlBase, File) :-
  398    file_name_extension(Base, pl, PlBase),
  399    atomic_list_concat([Dir, '/', PlBase], File).
  400
  401pattern_files([], _, []).
  402pattern_files([H|T], DirS, Files) :-
  403    atom_concat(DirS, H, P0),
  404    expand_file_name(P0, Files0),
  405    '$append'(Files0, Rest, Files),
  406    pattern_files(T, DirS, Rest).
  407
  408library_index_out_of_date(_Dir, Index, _Files) :-
  409    \+ exists_file(Index),
  410    !.
  411library_index_out_of_date(Dir, Index, Files) :-
  412    time_file(Index, IndexTime),
  413    (   time_file(Dir, DotTime),
  414        DotTime - IndexTime > 0.001             % compensate for jitter
  415    ;   '$member'(File, Files),                 % and rounding
  416        time_file(File, FileTime),
  417        FileTime - IndexTime > 0.001
  418    ),
  419    !.
  420
  421
  422do_make_library_index(Index, Dir, Files) :-
  423    ensure_slash(Dir, DirS),
  424    '$stage_file'(Index, StagedIndex),
  425    setup_call_catcher_cleanup(
  426        open(StagedIndex, write, Out),
  427        ( print_message(informational, make(library_index(Dir))),
  428          index_header(Out),
  429          index_files(Files, DirS, Out)
  430        ),
  431        Catcher,
  432        install_index(Out, Catcher, StagedIndex, Index)).
  433
  434install_index(Out, Catcher, StagedIndex, Index) :-
  435    catch(close(Out), Error, true),
  436    (   silent
  437    ->  OnError = silent
  438    ;   OnError = error
  439    ),
  440    (   var(Error)
  441    ->  TheCatcher = Catcher
  442    ;   TheCatcher = exception(Error)
  443    ),
  444    '$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.
  450index_files([], _, _).
  451index_files([File|Files], DirS, Fd) :-
  452    (   catch(exports(File, Module, Exports, Meta, Public), E,
  453              print_message(warning, E)),
  454        nonvar(Module)
  455    ->  atom_concat(DirS, Local, File),
  456        file_name_extension(Base, _, Local),
  457        forall(index_term(Exports, Meta, Public, Term),
  458               format(Fd, 'index(~k, ~k, ~k).~n',
  459                      [Term, Module, Base]))
  460    ;   true
  461    ),
  462    index_files(Files, DirS, Fd).
  463
  464index_term(Exports, Meta, _Public, Term) :-
  465    '$member'(Export, Exports),
  466    ground(Export),
  467    export_term(Export, Meta, Term).
  468index_term(_Exports, _Meta, Publics, (public):Head) :-
  469    '$member'(Public, Publics),
  470    '$pi_head'(Public, Head).
  471
  472export_term(Op, _Meta, Term) :-
  473    Op = op(_Pri,_Type,_Name),
  474    !,
  475    Term = op:Op.
  476export_term(PI, Meta, Head) :-
  477    '$pi_head'(PI, Head),
  478    (   '$member'(Head, Meta)
  479    ->  true
  480    ;   head_meta_any(Head)
  481    ).
  482
  483head_meta_any(Head) :-
  484    (   atom(Head)
  485    ->  true
  486    ;   compound_name_arguments(Head, _, Args),
  487        meta_any(Args)
  488    ).
  489
  490meta_any([]).
  491meta_any([?|T]) :-
  492    meta_any(T).
  493
  494index_header(Fd):-
  495    format(Fd, '/*  Creator: make/0~n~n', []),
  496    format(Fd, '    Purpose: Provide index for autoload~n', []),
  497    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.
  507:- public exports/3.                            % using by library(prolog_deps).
  508exports(File, Module, Exports) :-
  509    exports(File, Module, Exports, _Meta, _Public).
  510
  511exports(File, Module, Exports, Meta, Public) :-
  512    (   current_prolog_flag(xref, Old)
  513    ->  true
  514    ;   Old = false
  515    ),
  516    setup_call_cleanup(
  517        set_prolog_flag(xref, true),
  518        snapshot(exports_(File, Module, Exports, Meta, Public)),
  519        set_prolog_flag(xref, Old)).
  520
  521exports_(File, Module, Exports, Meta, Public) :-
  522    State = state(true, _, [], [], []),
  523    (   '$source_term'(File,
  524                       _Read,_RLayout,
  525                       Term,_TermLayout,
  526                       _Stream,
  527                       [ syntax_errors(quiet)
  528                       ]),
  529        (   Term = (:- module(M,ModuleExports)),
  530            is_list(ModuleExports),
  531            arg(1, State, true)
  532        ->  nb_setarg(1, State, false),
  533            nb_setarg(2, State, M),
  534            nb_setarg(3, State, ModuleExports),
  535            fail
  536        ;   nb_setarg(1, State, false),
  537            fail
  538        ;   Term = (:- export(Export))
  539        ->  phrase(export_pi(Export), PIs),
  540            arg(3, State, E0),
  541            '$append'(E0, PIs, E1),
  542            nb_setarg(3, State, E1),
  543            fail
  544        ;   Term = (:- public(Public))
  545        ->  phrase(export_pi(Public), PIs),
  546            arg(5, State, E0),
  547            '$append'(E0, PIs, E1),
  548            nb_setarg(5, State, E1),
  549            fail
  550        ;   Term = (:- meta_predicate(Heads)),
  551            phrase(meta(Heads), M1),
  552            arg(4, State, M0),
  553            '$append'(M0, M1, M2),
  554            nb_setarg(4, State, M2)
  555        ;   Term = (:- use_foreign_library(Lib)),
  556            nonvar(Lib),
  557            arg(2, State, M),
  558            atom(M)
  559        ->  catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true),
  560            fail
  561        ;   Term = (:- Directive),
  562            nonvar(Directive)
  563        ->  fail
  564        ;   Term == []                          % Expansion for conditionals
  565        ->  fail
  566        ;   !
  567        )
  568    ;   true
  569    ),
  570    arg(2, State, Module),
  571    arg(3, State, Exports),
  572    arg(4, State, Meta),
  573    arg(5, State, Public).
  574
  575export_pi(Var) -->
  576    { var(Var) },
  577    !.
  578export_pi((A,B)) -->
  579    !,
  580    export_pi(A),
  581    export_pi(B).
  582export_pi(PI) -->
  583    { ground(PI) },
  584    [PI].
  585
  586meta(Var) -->
  587    { var(Var) },
  588    !.
  589meta((A,B)) -->
  590    !,
  591    meta(A),
  592    meta(B).
  593meta(Head) -->
  594    { callable(Head) },
  595    [Head].
  596
  597
  598                 /*******************************
  599                 *            EXTENDING         *
  600                 *******************************/
 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.

  617autoload_path(Alias) :-
  618    (   user:file_search_path(autoload, Alias)
  619    ->  true
  620    ;   assertz(user:file_search_path(autoload, Alias)),
  621        reload_library_index
  622    ).
  623
  624system:term_expansion((:- autoload_path(Alias)),
  625                      [ user:file_search_path(autoload, Alias),
  626                        (:- reload_library_index)
  627                      ]).
  628
  629
  630		 /*******************************
  631		 *      RUNTIME AUTOLOADER	*
  632		 *******************************/
Provide PI by autoloading. This checks:
  642'$autoload'(PI) :-
  643    source_location(File, _Line),
  644    !,
  645    setup_call_cleanup(
  646        '$start_aux'(File, Context),
  647        '$autoload2'(PI),
  648        '$end_aux'(File, Context)).
  649'$autoload'(PI) :-
  650    '$autoload2'(PI).
  651
  652'$autoload2'(PI) :-
  653    setup_call_cleanup(
  654        leave_sandbox(Old),
  655        '$autoload3'(PI),
  656        restore_sandbox(Old)).
  657
  658leave_sandbox(Sandboxed) :-
  659    current_prolog_flag(sandboxed_load, Sandboxed),
  660    set_prolog_flag(sandboxed_load, false).
  661restore_sandbox(Sandboxed) :-
  662    set_prolog_flag(sandboxed_load, Sandboxed).
  663
  664'$autoload3'(PI) :-
  665    autoload_from(PI, LoadModule, FullFile),
  666    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.
  673autoload_from(Module:PI, LoadModule, FullFile) :-
  674    autoload_in(Module, explicit),
  675    current_autoload(Module:File, Ctx, import(Imports)),
  676    memberchk(PI, Imports),
  677    library_info(File, Ctx, FullFile, LoadModule, Exports),
  678    (   pi_in_exports(PI, Exports)
  679    ->  !
  680    ;   autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)),
  681        fail
  682    ).
  683autoload_from(Module:Name/Arity, LoadModule, FullFile) :-
  684    autoload_in(Module, explicit),
  685    PI = Name/Arity,
  686    current_autoload(Module:File, Ctx, all),
  687    library_info(File, Ctx, FullFile, LoadModule, Exports),
  688    pi_in_exports(PI, Exports).
  689autoload_from(Module:Name/Arity, LoadModule, Library) :-
  690    autoload_in(Module, general),
  691    '$find_library'(Module, Name, Arity, LoadModule, Library).
  692
  693:- public autoload_in/2.                        % used in syspred
  694
  695autoload_in(Module, How) :-
  696    current_prolog_flag(autoload, AutoLoad),
  697    autoload_in(AutoLoad, How, Module),
  698    !.
 autoload_in(+AutoloadFlag, +AutoloadMode, +TargetModule) is semidet
  702autoload_in(true,             _,        _).
  703autoload_in(explicit,         explicit, _).
  704autoload_in(user,             _,        user).
  705autoload_in(user_or_explicit, explicit, _).
  706autoload_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.
  725do_autoload(Library, Module:Name/Arity, LoadModule) :-
  726    functor(Head, Name, Arity),
  727    '$update_autoload_level'([autoload(true)], Old),
  728    verbose_autoload(Module:Name/Arity, Library),
  729    loadable_file(Library, File),
  730    '$compilation_mode'(OldComp, database),
  731    (   Module == LoadModule
  732    ->  ensure_loaded(Module:File)
  733    ;   (   '$c_current_predicate'(_, LoadModule:Head),
  734            '$get_predicate_attribute'(LoadModule:Head, defined, 1),
  735            \+ '$loading'(Library)
  736        ->  Module:import(LoadModule:Name/Arity)
  737        ;   use_module(Module:File, [Name/Arity])
  738        ),
  739        warn_autoload(Module, LoadModule:Name/Arity)
  740    ),
  741    '$set_compilation_mode'(OldComp),
  742    '$set_autoload_level'(Old),
  743    '$c_current_predicate'(_, Module:Head).
  744
  745loadable_file(PlFile, File) :-
  746    exists_file(PlFile), !,
  747    File = PlFile.
  748loadable_file(PlFile, Base) :-
  749    file_name_extension(Base, pl, PlFile),
  750    !.
  751loadable_file(File, File).
  752
  753verbose_autoload(PI, Library) :-
  754    current_prolog_flag(verbose_autoload, true),
  755    !,
  756    set_prolog_flag(verbose_autoload, false),
  757    print_message(informational, autoload(PI, Library)),
  758    set_prolog_flag(verbose_autoload, true).
  759verbose_autoload(PI, Library) :-
  760    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.
  769:- public                               % used from predicate_property/2
  770    autoloadable/2.  771
  772autoloadable(M:Head, FullFile) :-
  773    atom(M),
  774    current_module(M),
  775    autoload_in(M, explicit),
  776    (   callable(Head)
  777    ->  goal_name_arity(Head, Name, Arity),
  778        autoload_from(M:Name/Arity, _, FullFile)
  779    ;   findall((M:H)-F, autoloadable_2(M:H, F), Pairs),
  780        (   '$member'(M:Head-FullFile, Pairs)
  781        ;   current_autoload(M:File, Ctx, all),
  782            library_info(File, Ctx, FullFile, _, Exports),
  783            '$member'(PI, Exports),
  784            '$pi_head'(PI, Head),
  785            \+ memberchk(M:Head-_, Pairs)
  786        )
  787    ).
  788autoloadable(M:Head, FullFile) :-
  789    (   var(M)
  790    ->  autoload_in(any, general)
  791    ;   autoload_in(M, general)
  792    ),
  793    (   callable(Head)
  794    ->  goal_name_arity(Head, Name, Arity),
  795        (   '$find_library'(_, Name, Arity, _, FullFile)
  796        ->  true
  797        )
  798    ;   '$in_library'(Name, Arity, autoload),
  799        functor(Head, Name, Arity)
  800    ).
  801
  802
  803autoloadable_2(M:Head, FullFile) :-
  804    current_autoload(M:File, Ctx, import(Imports)),
  805    library_info(File, Ctx, FullFile, _LoadModule, _Exports),
  806    '$member'(PI, Imports),
  807    '$pi_head'(PI, Head).
  808
  809goal_name_arity(Head, Name, Arity) :-
  810    compound(Head),
  811    !,
  812    compound_name_arity(Head, Name, Arity).
  813goal_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.
  826library_info(Spec, _, FullFile, Module, Exports) :-
  827    '$resolved_source_path'(Spec, FullFile, []),
  828    !,
  829    (   \+ '$loading_file'(FullFile, _Queue, _LoadThread)
  830    ->  '$current_module'(Module, FullFile),
  831        '$module_property'(Module, exports(Exports))
  832    ;   library_info_from_file(FullFile, _, Module, Exports)
  833    ).
  834library_info(Spec, Context, FullFile, Module, Exports) :-
  835    (   Context = (Path:_Line)
  836    ->  Extra = [relative_to(Path)]
  837    ;   Extra = []
  838    ),
  839    (   absolute_file_name(Spec, AbsFile,
  840                           [ file_type(prolog),
  841                             access(read),
  842                             file_errors(fail)
  843                           | Extra
  844                           ])
  845    ->  library_info_from_file(AbsFile, FullFile, Module, Exports),
  846        '$register_resolved_source_path'(Spec, FullFile)
  847    ;   absolute_file_name(Spec, FullFile,
  848                           [ file_type(prolog),
  849                             solutions(all),
  850                             file_errors(fail)
  851                           | Extra
  852                           ]),
  853        source_file(FullFile),
  854        '$current_module'(Module, FullFile)
  855    ->  '$module_property'(Module, exports(Exports))
  856    ;   autoload_error(Context, no_file(Spec)),
  857        fail
  858    ).
  859
  860library_info_from_file(QlfFile, PlFile, Module, Exports) :-
  861    file_name_extension(_, qlf, QlfFile),
  862    !,
  863    '$qlf_module'(QlfFile, Info),
  864    _{module:Module, exports:Exports, file:PlFile} :< Info.
  865library_info_from_file(PlFile, PlFile, Module, Exports) :-
  866    setup_call_cleanup(
  867        '$set_source_module'(OldModule, system),
  868        setup_call_cleanup(
  869            '$open_source'(PlFile, In, State, [], []),
  870            '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream,
  871                            [PlFile], []),
  872            '$close_source'(State, true)),
  873        '$set_source_module'(OldModule)),
  874    (   Term = (:- module(Module, Exports))
  875    ->  !
  876    ;   nonvar(Term),
  877        skip_header(Term)
  878    ->  fail
  879    ;   '$domain_error'(module_header, Term)
  880    ).
  881
  882skip_header(begin_of_file).
  883
  884
  885:- dynamic printed/3.  886:- volatile printed/3.  887
  888autoload_error(Context, Error) :-
  889    suppress(Context, Error),
  890    !.
  891autoload_error(Context, Error) :-
  892    get_time(Now),
  893    assertz(printed(Context, Error, Now)),
  894    print_message(warning, error(autoload(Error), autoload(Context))).
  895
  896suppress(Context, Error) :-
  897    printed(Context, Error, Printed),
  898    get_time(Now),
  899    (   Now - Printed < 1
  900    ->  true
  901    ;   retractall(printed(Context, Error, _)),
  902        fail
  903    ).
  904
  905
  906		 /*******************************
  907		 *            CALLBACK		*
  908		 *******************************/
  909
  910:- public
  911    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.
  920set_autoload(FlagValue) :-
  921    current_prolog_flag(autoload, FlagValue),
  922    !.
  923set_autoload(FlagValue) :-
  924    \+ autoload_in(FlagValue, explicit, any),
  925    !,
  926    setup_call_cleanup(
  927        nb_setval('$autoload_disabling', true),
  928        materialize_autoload(Count),
  929        nb_delete('$autoload_disabling')),
  930    print_message(informational, autoload(disabled(Count))).
  931set_autoload(_).
  932
  933materialize_autoload(Count) :-
  934    State = state(0),
  935    forall(current_predicate(M:'$autoload'/3),
  936           materialize_autoload(M, State)),
  937    arg(1, State, Count).
  938
  939materialize_autoload(M, State) :-
  940    (   current_autoload(M:File, Context, Import),
  941        library_info(File, Context, PlFile, _LoadModule, _Exports),
  942        arg(1, State, N0),
  943        N is N0+1,
  944        nb_setarg(1, State, N),
  945        loadable_file(PlFile, LoadFile),
  946        (   Import == all
  947        ->  verbose_autoload(M:all, PlFile),
  948            use_module(M:LoadFile)
  949        ;   Import = import(Preds)
  950        ->  verbose_autoload(M:Preds, PlFile),
  951            use_module(M:LoadFile, Preds)
  952        ),
  953        fail
  954    ;   true
  955    ),
  956    abolish(M:'$autoload'/3).
  957
  958
  959		 /*******************************
  960		 *          AUTOLOAD/2		*
  961		 *******************************/
  962
  963autoload(M:File) :-
  964    (   \+ autoload_in(M, explicit)
  965    ;   nb_current('$autoload_disabling', true)
  966    ),
  967    !,
  968    use_module(M:File).
  969autoload(M:File) :-
  970    '$must_be'(filespec, File),
  971    source_context(Context),
  972    assert_autoload(M, File, Context, all).
  973
  974autoload(M:File, Imports) :-
  975    (   \+ autoload_in(M, explicit)
  976    ;   nb_current('$autoload_disabling', true)
  977    ),
  978    !,
  979    use_module(M:File, Imports).
  980autoload(M:File, Imports0) :-
  981    '$must_be'(filespec, File),
  982    valid_imports(Imports0, Imports),
  983    source_context(Context),
  984    register_autoloads(Imports, M, File, Context),
  985    assert_autoload(M, File, Context, import(Imports)).
  986
  987source_context(Path:Line) :-
  988    source_location(Path, Line),
  989    !.
  990source_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.
 1000assert_autoload(Module, File, _, Imports) :-
 1001    current_autoload(Module:File, _, Imports),
 1002    !.
 1003assert_autoload(Module, File, Context, Imports) :-
 1004    set_admin_properties(Module),
 1005    Clause = Module:'$autoload'(File, Context, Imports),
 1006    '$initialization_context'(Source, Ctx),
 1007    '$store_admin_clause2'(Clause, _Layout, Source, Ctx).
 1008
 1009set_admin_properties(Module) :-
 1010    predicate_property(Module:'$autoload'(_,_,_), discontiguous),
 1011    !.
 1012set_admin_properties(Module) :-
 1013    discontiguous(Module:'$autoload'/3).
 1014
 1015valid_imports(Imports0, Imports) :-
 1016    '$must_be'(list, Imports0),
 1017    valid_import_list(Imports0, Imports).
 1018
 1019valid_import_list([], []).
 1020valid_import_list([H0|T0], [H|T]) :-
 1021    '$pi_head'(H0, Head),
 1022    '$pi_head'(H, Head),
 1023    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
 1032register_autoloads([], _, _, _).
 1033register_autoloads([PI|T], Module, File, Context) :-
 1034    PI = Name/Arity,
 1035    functor(Head, Name, Arity),
 1036    (   '$get_predicate_attribute'(Module:Head, autoload, 1)
 1037    ->  (   current_autoload(Module:_File0, _Ctx0, import(Imports)),
 1038            memberchk(PI, Imports)
 1039        ->  '$permission_error'(redefine, imported_procedure, PI),
 1040            fail
 1041        ;   Done = true
 1042        )
 1043    ;   '$c_current_predicate'(_, Module:Head), % no auto-import
 1044        '$get_predicate_attribute'(Module:Head, imported, From)
 1045    ->  (   (   '$resolved_source_path'(File, FullFile)
 1046            ->  true
 1047            ;   '$resolve_source_path'(File, FullFile, [])
 1048            ),
 1049            module_property(From, file(FullFile))
 1050        ->  Done = true
 1051        ;   print_message(warning,
 1052                          autoload(already_defined(Module:PI, From))),
 1053            Done = true
 1054        )
 1055    ;   true
 1056    ),
 1057    (   Done == true
 1058    ->  true
 1059    ;   '$set_predicate_attribute'(Module:Head, autoload, 1)
 1060    ),
 1061    register_autoloads(T, Module, File, Context).
 1062
 1063pi_in_exports(PI, Exports) :-
 1064    '$member'(E, Exports),
 1065    canonical_pi(E, PI),
 1066    !.
 1067
 1068canonical_pi(Var, _) :-
 1069    var(Var), !, fail.
 1070canonical_pi(Name/Arity, Name/Arity).
 1071canonical_pi(Name//A0,   Name/Arity) :-
 1072    Arity is A0 + 2.
 1073
 1074current_autoload(M:File, Context, Term) :-
 1075    '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1),
 1076    M:'$autoload'(File, Context, Term).
 1077
 1078		 /*******************************
 1079		 *            CHECK		*
 1080		 *******************************/
 warn_autoload(+TargetModule, :PI) is det
Arguments:
PI- is of the shape LoadModule:Name/Arity.
 1086warn_autoload(TargetModule, PI) :-
 1087    current_prolog_flag(warn_autoload, true),
 1088    \+ current_prolog_flag(xref, true),
 1089    \+ nb_current('$autoload_warning', true),
 1090    \+ nowarn_autoload(TargetModule, PI),
 1091    '$pi_head'(PI, Head),
 1092    source_file(Head, File),
 1093    '$source_defines_expansion'(File),
 1094    setup_call_cleanup(
 1095        b_setval('$autoload_warning', true),
 1096        print_message(warning,
 1097                      deprecated(autoload(TargetModule, File, PI, expansion))),
 1098        nb_delete('$autoload_warning')).
 1099warn_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)

 1114nowarn_autoload(TargetModule, LoadModule:PI) :-
 1115    NoWarn = LoadModule:'$nowarn_autoload'(PI,TargetModule),
 1116    '$c_current_predicate'(_, NoWarn),
 1117    \+ '$get_predicate_attribute'(NoWarn, imported, _From),
 1118    call(NoWarn).
 1119
 1120
 1121                 /*******************************
 1122                 *             REQUIRE          *
 1123                 *******************************/
 require(:ListOfPredIndicators) is det
Register the predicates in ListOfPredIndicators for autoloading using autoload/2 if they are not system predicates.
 1130require(M:Spec) :-
 1131    (   is_list(Spec)
 1132    ->  List = Spec
 1133    ;   phrase(comma_list(Spec), List)
 1134    ), !,
 1135    require(List, M, FromLib),
 1136    keysort(FromLib, Sorted),
 1137    by_file(Sorted, Autoload),
 1138    forall('$member'(File-Import, Autoload),
 1139           autoload(M:File, Import)).
 1140require(_:Spec) :-
 1141    '$type_error'(list, Spec).
 1142
 1143require([],_, []).
 1144require([H|T], M, Needed) :-
 1145   '$pi_head'(H, Head),
 1146   (   '$get_predicate_attribute'(system:Head, defined, 1)
 1147   ->  require(T, M, Needed)
 1148   ;   '$pi_head'(Module:Name/Arity, M:Head),
 1149       (   '$find_library'(Module, Name, Arity, LoadModule, Library)
 1150       ->  (   current_predicate(LoadModule:Name/Arity)
 1151           ->  Module:import(LoadModule:Name/Arity),
 1152               require(T, M, Needed)
 1153           ;   Needed = [Library-H|More],
 1154               require(T, M, More)
 1155           )
 1156       ;   print_message(error, error(existence_error(procedure, Name/Arity), _)),
 1157           require(T, M, Needed)
 1158       )
 1159   ).
 1160
 1161by_file([], []).
 1162by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :-
 1163    on_path(File, Spec),
 1164    same_file(T0, File, PIs, T1),
 1165    by_file(T1, T).
 1166
 1167on_path(Library, library(Base)) :-
 1168    file_base_name(Library, Base),
 1169    findall(Path, plain_source(library(Base), Path), [Library]),
 1170    !.
 1171on_path(Library, Library).
 1172
 1173plain_source(Spec, Path) :-
 1174    absolute_file_name(Spec, PathExt,
 1175                       [ file_type(prolog),
 1176                         access(read),
 1177                         file_errors(fail),
 1178                         solutions(all)
 1179                       ]),
 1180    file_name_extension(Path, _, PathExt).
 1181
 1182same_file([File-PI|T0], File, [PI|PIs], T) :-
 1183    !,
 1184    same_file(T0, File, PIs, T).
 1185same_file(List, _, [], List).
 1186
 1187comma_list(Var) -->
 1188    { var(Var),
 1189      !,
 1190      '$instantiation_error'(Var)
 1191    }.
 1192comma_list((A,B)) -->
 1193    !,
 1194    comma_list(A),
 1195    comma_list(B).
 1196comma_list(A) -->
 1197    [A]