View source with formatted 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-2023, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9                              SWI-Prolog Solutions b.v.
   10    All rights reserved.
   11
   12    Redistribution and use in source and binary forms, with or without
   13    modification, are permitted provided that the following conditions
   14    are met:
   15
   16    1. Redistributions of source code must retain the above copyright
   17       notice, this list of conditions and the following disclaimer.
   18
   19    2. Redistributions in binary form must reproduce the above copyright
   20       notice, this list of conditions and the following disclaimer in
   21       the documentation and/or other materials provided with the
   22       distribution.
   23
   24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   28    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   34    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35    POSSIBILITY OF SUCH DAMAGE.
   36*/
   37
   38:- module('$autoload',
   39          [ '$find_library'/5,
   40            '$in_library'/3,
   41            '$define_predicate'/1,
   42            '$update_library_index'/0,
   43            '$autoload'/1,
   44
   45            make_library_index/1,
   46            make_library_index/2,
   47            reload_library_index/0,
   48            autoload_path/1,
   49
   50            autoload/1,                         % +File
   51            autoload/2,                         % +File, +Imports
   52
   53            require/1				% +Predicates
   54          ]).   55
   56:- meta_predicate
   57    '$autoload'(:),
   58    autoload(:),
   59    autoload(:, +),
   60    require(:).   61
   62:- dynamic
   63    library_index/3,                % Head x Module x Path
   64    autoload_directories/1,         % List
   65    index_checked_at/1.             % Time
   66:- volatile
   67    library_index/3,
   68    autoload_directories/1,
   69    index_checked_at/1.   70
   71user:file_search_path(autoload, swi(library)).
   72user:file_search_path(autoload, pce(prolog/lib)).
   73user:file_search_path(autoload, app_config(lib)).
   74user:file_search_path(autoload, Dir) :-
   75    '$ext_library_directory'(Dir).
   76
   77:- create_prolog_flag(warn_autoload, false, []).   78
   79%!  '$find_library'(+Module, +Name, +Arity, -LoadModule, -Library) is semidet.
   80%
   81%   Locate a predicate in the library. Name   and arity are the name
   82%   and arity of  the  predicate  searched   for.  `Module'  is  the
   83%   preferred target module. The return  values   are  the full path
   84%   name (excluding extension) of the library and module declared in
   85%   that file.
   86
   87'$find_library'(Module, Name, Arity, LoadModule, Library) :-
   88    load_library_index(Name, Arity),
   89    functor(Head, Name, Arity),
   90    (   library_index(Head, Module, Library),
   91        LoadModule = Module
   92    ;   library_index(Head, LoadModule, Library)
   93    ),
   94    !.
   95
   96%!  '$in_library'(+Name, +Arity, -Path) is semidet.
   97%!  '$in_library'(-Name, -Arity, -Path) is nondet.
   98%
   99%   Is true if Name/Arity is in the autoload libraries.
  100
  101'$in_library'(Name, Arity, Path) :-
  102    atom(Name), integer(Arity),
  103    !,
  104    load_library_index(Name, Arity),
  105    functor(Head, Name, Arity),
  106    library_index(Head, _, Path).
  107'$in_library'(Name, Arity, Path) :-
  108    load_library_index(Name, Arity),
  109    library_index(Head, _, Path),
  110    functor(Head, Name, Arity).
  111
  112%!  '$define_predicate'(:Head)
  113%
  114%   Make sure PredInd can be called. First  test if the predicate is
  115%   defined. If not, invoke the autoloader.
  116
  117:- meta_predicate
  118    '$define_predicate'(:).  119
  120'$define_predicate'(Head) :-
  121    '$defined_predicate'(Head),
  122    !.
  123'$define_predicate'(Term) :-
  124    Term = Module:Head,
  125    (   compound(Head)
  126    ->  compound_name_arity(Head, Name, Arity)
  127    ;   Name = Head, Arity = 0
  128    ),
  129    '$undefined_procedure'(Module, Name, Arity, retry).
  130
  131
  132                /********************************
  133                *          UPDATE INDEX         *
  134                ********************************/
  135
  136:- thread_local
  137    silent/0.  138
  139%!  '$update_library_index'
  140%
  141%   Called from make/0 to update the index   of the library for each
  142%   library directory that has a writable   index.  Note that in the
  143%   Windows  version  access_file/2  is  mostly   bogus.  We  assert
  144%   silent/0 to suppress error messages.
  145
  146'$update_library_index' :-
  147    setof(Dir, writable_indexed_directory(Dir), Dirs),
  148    !,
  149    setup_call_cleanup(
  150        asserta(silent, Ref),
  151        guarded_make_library_index(Dirs),
  152        erase(Ref)),
  153    (   flag('$modified_index', true, false)
  154    ->  reload_library_index
  155    ;   true
  156    ).
  157'$update_library_index'.
  158
  159guarded_make_library_index([]).
  160guarded_make_library_index([Dir|Dirs]) :-
  161    (   catch(make_library_index(Dir), E,
  162              print_message(error, E))
  163    ->  true
  164    ;   print_message(warning, goal_failed(make_library_index(Dir)))
  165    ),
  166    guarded_make_library_index(Dirs).
  167
  168%!  writable_indexed_directory(-Dir) is nondet.
  169%
  170%   True when Dir is an indexed   library  directory with a writable
  171%   index, i.e., an index that can be updated.
  172
  173writable_indexed_directory(Dir) :-
  174    index_file_name(IndexFile, autoload('INDEX'), [access([read,write])]),
  175    file_directory_name(IndexFile, Dir).
  176writable_indexed_directory(Dir) :-
  177    absolute_file_name(library('MKINDEX'),
  178                       [ file_type(prolog),
  179                         access(read),
  180                         solutions(all),
  181                         file_errors(fail)
  182                       ], MkIndexFile),
  183    file_directory_name(MkIndexFile, Dir),
  184    plfile_in_dir(Dir, 'INDEX', _, IndexFile),
  185    access_file(IndexFile, write).
  186
  187
  188                /********************************
  189                *           LOAD INDEX          *
  190                ********************************/
  191
  192%!  reload_library_index
  193%
  194%   Reload the index on the next call
  195
  196reload_library_index :-
  197    context_module(M),
  198    reload_library_index(M).
  199
  200reload_library_index(M) :-
  201    with_mutex('$autoload', clear_library_index(M)).
  202
  203clear_library_index(M) :-
  204    retractall(M:library_index(_, _, _)),
  205    retractall(M:autoload_directories(_)),
  206    retractall(M:index_checked_at(_)).
  207
  208
  209%!  load_library_index(?Name, ?Arity) is det.
  210%!  load_library_index(?Name, ?Arity, :IndexSpec) is det.
  211%
  212%   Try to find Name/Arity  in  the   library.  If  the predicate is
  213%   there, we are happy. If not, we  check whether the set of loaded
  214%   libraries has changed and if so we reload the index.
  215
  216:- meta_predicate load_library_index(?, ?, :).  217:- public load_library_index/3.  218
  219load_library_index(Name, Arity) :-
  220    load_library_index(Name, Arity, autoload('INDEX')).
  221
  222load_library_index(Name, Arity, M:_Spec) :-
  223    atom(Name), integer(Arity),
  224    functor(Head, Name, Arity),
  225    M:library_index(Head, _, _),
  226    !.
  227load_library_index(_, _, Spec) :-
  228    notrace(with_mutex('$autoload', load_library_index_p(Spec))).
  229
  230load_library_index_p(M:_) :-
  231    M:index_checked_at(Time),
  232    get_time(Now),
  233    Now-Time < 60,
  234    !.
  235load_library_index_p(M:Spec) :-
  236    findall(Index, index_file_name(Index, Spec, [access(read)]), List0),
  237    '$list_to_set'(List0, List),
  238    retractall(M:index_checked_at(_)),
  239    get_time(Now),
  240    assert(M:index_checked_at(Now)),
  241    (   M:autoload_directories(List)
  242    ->  true
  243    ;   retractall(M:library_index(_, _, _)),
  244        retractall(M:autoload_directories(_)),
  245        read_index(List, M),
  246        assert(M:autoload_directories(List))
  247    ).
  248
  249%!  index_file_name(-IndexFile, +Spec, +Options) is nondet.
  250%
  251%   True if IndexFile is an autoload   index file. Options is passed
  252%   to  absolute_file_name/3.  This  predicate   searches  the  path
  253%   =autoload=.
  254%
  255%   @see file_search_path/2.
  256
  257index_file_name(IndexFile, FileSpec, Options) :-
  258    absolute_file_name(FileSpec,
  259                       IndexFile,
  260                       [ file_type(prolog),
  261                         solutions(all),
  262                         file_errors(fail)
  263                       | Options
  264                       ]).
  265
  266read_index([], _) :- !.
  267read_index([H|T], M) :-
  268    !,
  269    read_index(H, M),
  270    read_index(T, M).
  271read_index(Index, M) :-
  272    print_message(silent, autoload(read_index(Dir))),
  273    file_directory_name(Index, Dir),
  274    setup_call_cleanup(
  275        '$push_input_context'(autoload_index),
  276        setup_call_cleanup(
  277            open(Index, read, In),
  278            read_index_from_stream(Dir, In, M),
  279            close(In)),
  280        '$pop_input_context').
  281
  282read_index_from_stream(Dir, In, M) :-
  283    repeat,
  284        read(In, Term),
  285        assert_index(Term, Dir, M),
  286    !.
  287
  288assert_index(end_of_file, _, _) :- !.
  289assert_index(index(Name, Arity, Module, File), Dir, M) :-
  290    !,
  291    functor(Head, Name, Arity),
  292    atomic_list_concat([Dir, '/', File], Path),
  293    assertz(M:library_index(Head, Module, Path)),
  294    fail.
  295assert_index(Term, Dir, _) :-
  296    print_message(error, illegal_autoload_index(Dir, Term)),
  297    fail.
  298
  299
  300                /********************************
  301                *       CREATE INDEX.pl         *
  302                ********************************/
  303
  304%!  make_library_index(+Dir) is det.
  305%
  306%   Create an index for autoloading  from   the  directory  Dir. The
  307%   index  file  is  called  INDEX.pl.  In    Dir  contains  a  file
  308%   MKINDEX.pl, this file is loaded and we  assume that the index is
  309%   created by directives that appearin   this  file. Otherwise, all
  310%   source  files  are  scanned  for  their  module-header  and  all
  311%   exported predicates are added to the autoload index.
  312%
  313%   @see make_library_index/2
  314
  315make_library_index(Dir0) :-
  316    forall(absolute_file_name(Dir0, Dir,
  317                              [ expand(true),
  318                                file_type(directory),
  319                                file_errors(fail),
  320                                solutions(all)
  321                              ]),
  322           make_library_index2(Dir)).
  323
  324make_library_index2(Dir) :-
  325    plfile_in_dir(Dir, 'MKINDEX', _MkIndex, AbsMkIndex),
  326    access_file(AbsMkIndex, read),
  327    !,
  328    load_files(user:AbsMkIndex, [silent(true)]).
  329make_library_index2(Dir) :-
  330    findall(Pattern, source_file_pattern(Pattern), PatternList),
  331    make_library_index2(Dir, PatternList).
  332
  333%!  make_library_index(+Dir, +Patterns:list(atom)) is det.
  334%
  335%   Create an autoload index INDEX.pl for  Dir by scanning all files
  336%   that match any of the file-patterns in Patterns. Typically, this
  337%   appears as a directive in MKINDEX.pl.  For example:
  338%
  339%   ```
  340%   :- prolog_load_context(directory, Dir),
  341%      make_library_index(Dir, ['*.pl']).
  342%   ```
  343%
  344%   @see make_library_index/1.
  345
  346make_library_index(Dir0, Patterns) :-
  347    forall(absolute_file_name(Dir0, Dir,
  348                              [ expand(true),
  349                                file_type(directory),
  350                                file_errors(fail),
  351                                solutions(all)
  352                              ]),
  353           make_library_index2(Dir, Patterns)).
  354
  355make_library_index2(Dir, Patterns) :-
  356    plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex),
  357    ensure_slash(Dir, DirS),
  358    pattern_files(Patterns, DirS, Files),
  359    (   library_index_out_of_date(Dir, AbsIndex, Files)
  360    ->  do_make_library_index(AbsIndex, DirS, Files),
  361        set_flag('$modified_index', true)
  362    ;   true
  363    ).
  364
  365ensure_slash(Dir, DirS) :-
  366    (   sub_atom(Dir, _, _, 0, /)
  367    ->  DirS = Dir
  368    ;   atom_concat(Dir, /, DirS)
  369    ).
  370
  371source_file_pattern(Pattern) :-
  372    user:prolog_file_type(PlExt, prolog),
  373    PlExt \== qlf,
  374    atom_concat('*.', PlExt, Pattern).
  375
  376plfile_in_dir(Dir, Base, PlBase, File) :-
  377    file_name_extension(Base, pl, PlBase),
  378    atomic_list_concat([Dir, '/', PlBase], File).
  379
  380pattern_files([], _, []).
  381pattern_files([H|T], DirS, Files) :-
  382    atom_concat(DirS, H, P0),
  383    expand_file_name(P0, Files0),
  384    '$append'(Files0, Rest, Files),
  385    pattern_files(T, DirS, Rest).
  386
  387library_index_out_of_date(_Dir, Index, _Files) :-
  388    \+ exists_file(Index),
  389    !.
  390library_index_out_of_date(Dir, Index, Files) :-
  391    time_file(Index, IndexTime),
  392    (   time_file(Dir, DotTime),
  393        DotTime - IndexTime > 0.001             % compensate for jitter
  394    ;   '$member'(File, Files),                 % and rounding
  395        time_file(File, FileTime),
  396        FileTime - IndexTime > 0.001
  397    ),
  398    !.
  399
  400
  401do_make_library_index(Index, Dir, Files) :-
  402    ensure_slash(Dir, DirS),
  403    '$stage_file'(Index, StagedIndex),
  404    setup_call_catcher_cleanup(
  405        open(StagedIndex, write, Out),
  406        ( print_message(informational, make(library_index(Dir))),
  407          index_header(Out),
  408          index_files(Files, DirS, Out)
  409        ),
  410        Catcher,
  411        install_index(Out, Catcher, StagedIndex, Index)).
  412
  413install_index(Out, Catcher, StagedIndex, Index) :-
  414    catch(close(Out), Error, true),
  415    (   silent
  416    ->  OnError = silent
  417    ;   OnError = error
  418    ),
  419    (   var(Error)
  420    ->  TheCatcher = Catcher
  421    ;   TheCatcher = exception(Error)
  422    ),
  423    '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
  424
  425%!  index_files(+Files, +Directory, +Out:stream) is det.
  426%
  427%   Write index for Files in Directory to the stream Out.
  428
  429index_files([], _, _).
  430index_files([File|Files], DirS, Fd) :-
  431    (   catch(exports(File, Module, Public), E,
  432              print_message(warning, E)),
  433        nonvar(Module)
  434    ->  atom_concat(DirS, Local, File),
  435        file_name_extension(Base, _, Local),
  436        forall(public_predicate(Public, Name/Arity),
  437               format(Fd, 'index((~k), ~k, ~k, ~k).~n',
  438                      [Name, Arity, Module, Base]))
  439    ;   true
  440    ),
  441    index_files(Files, DirS, Fd).
  442
  443public_predicate(Public, PI) :-
  444    '$member'(PI0, Public),
  445    canonical_pi(PI0, PI).
  446
  447canonical_pi(Var, _) :-
  448    var(Var), !, fail.
  449canonical_pi(Name/Arity, Name/Arity).
  450canonical_pi(Name//A0,   Name/Arity) :-
  451    Arity is A0 + 2.
  452
  453
  454index_header(Fd):-
  455    format(Fd, '/*  Creator: make/0~n~n', []),
  456    format(Fd, '    Purpose: Provide index for autoload~n', []),
  457    format(Fd, '*/~n~n', []).
  458
  459%!  exports(+File, -Module, -Exports) is det.
  460%
  461%   Get the exports from a library as a list of PIs.
  462
  463:- public exports/3.                            % using by library(prolog_deps).
  464exports(File, Module, Exports) :-
  465    (   current_prolog_flag(xref, Old)
  466    ->  true
  467    ;   Old = false
  468    ),
  469    setup_call_cleanup(
  470        set_prolog_flag(xref, true),
  471        snapshot(exports_(File, Module, Exports)),
  472        set_prolog_flag(xref, Old)).
  473
  474exports_(File, Module, Exports) :-
  475    State = state(true, _, []),
  476    (   '$source_term'(File,
  477                       _Read,_RLayout,
  478                       Term,_TermLayout,
  479                       _Stream,
  480                       [ syntax_errors(quiet)
  481                       ]),
  482        (   Term = (:- module(M,Public)),
  483            is_list(Public),
  484            arg(1, State, true)
  485        ->  nb_setarg(1, State, false),
  486            nb_setarg(2, State, M),
  487            nb_setarg(3, State, Public),
  488            fail
  489        ;   nb_setarg(1, State, false),
  490            fail
  491        ;   Term = (:- export(PI)),
  492            ground(PI)
  493        ->  arg(3, State, E0),
  494            '$append'(E0, [PI], E1),
  495            nb_setarg(3, State, E1),
  496            fail
  497        ;   Term = (:- use_foreign_library(Lib)),
  498            nonvar(Lib),
  499            arg(2, State, M),
  500            atom(M)
  501        ->  catch('$syspreds':use_foreign_library_noi(M:Lib), error(_,_), true),
  502            fail
  503        ;   Term = (:- Directive),
  504            nonvar(Directive)
  505        ->  fail
  506        ;   !
  507        )
  508    ;   true
  509    ),
  510    arg(2, State, Module),
  511    arg(3, State, Exports).
  512
  513
  514                 /*******************************
  515                 *            EXTENDING         *
  516                 *******************************/
  517
  518%!  autoload_path(+Path) is det.
  519%
  520%   Add Path to the libraries that are  used by the autoloader. This
  521%   extends the search  path  =autoload=   and  reloads  the library
  522%   index.  For example:
  523%
  524%     ==
  525%     :- autoload_path(library(http)).
  526%     ==
  527%
  528%   If this call appears as a directive,  it is term-expanded into a
  529%   clause  for  user:file_search_path/2  and  a  directive  calling
  530%   reload_library_index/0. This keeps source information and allows
  531%   for removing this directive.
  532
  533autoload_path(Alias) :-
  534    (   user:file_search_path(autoload, Alias)
  535    ->  true
  536    ;   assertz(user:file_search_path(autoload, Alias)),
  537        reload_library_index
  538    ).
  539
  540system:term_expansion((:- autoload_path(Alias)),
  541                      [ user:file_search_path(autoload, Alias),
  542                        (:- reload_library_index)
  543                      ]).
  544
  545
  546		 /*******************************
  547		 *      RUNTIME AUTOLOADER	*
  548		 *******************************/
  549
  550%!  $autoload'(:PI) is semidet.
  551%
  552%   Provide PI by autoloading.  This checks:
  553%
  554%     - Explicit autoload/2 declarations
  555%     - Explicit autoload/1 declarations
  556%     - The library if current_prolog_flag(autoload, true) holds.
  557
  558'$autoload'(PI) :-
  559    source_location(File, _Line),
  560    !,
  561    setup_call_cleanup(
  562        '$start_aux'(File, Context),
  563        '$autoload2'(PI),
  564        '$end_aux'(File, Context)).
  565'$autoload'(PI) :-
  566    '$autoload2'(PI).
  567
  568'$autoload2'(PI) :-
  569    setup_call_cleanup(
  570        leave_sandbox(Old),
  571        '$autoload3'(PI),
  572        restore_sandbox(Old)).
  573
  574leave_sandbox(Sandboxed) :-
  575    current_prolog_flag(sandboxed_load, Sandboxed),
  576    set_prolog_flag(sandboxed_load, false).
  577restore_sandbox(Sandboxed) :-
  578    set_prolog_flag(sandboxed_load, Sandboxed).
  579
  580'$autoload3'(PI) :-
  581    autoload_from(PI, LoadModule, FullFile),
  582    do_autoload(FullFile, PI, LoadModule).
  583
  584%!  autoload_from(+PI, -LoadModule, -File) is semidet.
  585%
  586%   True when PI can be defined  by   loading  File which is defined the
  587%   module LoadModule.
  588
  589autoload_from(Module:PI, LoadModule, FullFile) :-
  590    autoload_in(Module, explicit),
  591    current_autoload(Module:File, Ctx, import(Imports)),
  592    memberchk(PI, Imports),
  593    library_info(File, Ctx, FullFile, LoadModule, Exports),
  594    (   pi_in_exports(PI, Exports)
  595    ->  !
  596    ;   autoload_error(Ctx, not_exported(PI, File, FullFile, Exports)),
  597        fail
  598    ).
  599autoload_from(Module:Name/Arity, LoadModule, FullFile) :-
  600    autoload_in(Module, explicit),
  601    PI = Name/Arity,
  602    current_autoload(Module:File, Ctx, all),
  603    library_info(File, Ctx, FullFile, LoadModule, Exports),
  604    pi_in_exports(PI, Exports).
  605autoload_from(Module:Name/Arity, LoadModule, Library) :-
  606    autoload_in(Module, general),
  607    '$find_library'(Module, Name, Arity, LoadModule, Library).
  608
  609:- public autoload_in/2.                        % used in syspred
  610
  611autoload_in(Module, How) :-
  612    current_prolog_flag(autoload, AutoLoad),
  613    autoload_in(AutoLoad, How, Module),
  614    !.
  615
  616%!  autoload_in(+AutoloadFlag, +AutoloadMode, +TargetModule) is semidet.
  617
  618autoload_in(true,             _,        _).
  619autoload_in(explicit,         explicit, _).
  620autoload_in(user,             _,        user).
  621autoload_in(user_or_explicit, explicit, _).
  622autoload_in(user_or_explicit, _,        user).
  623
  624
  625%!  do_autoload(+File, :PI, +LoadModule) is det.
  626%
  627%   Load File, importing PI into the qualified  module. File is known to
  628%   define LoadModule. There are three cases:
  629%
  630%     - The target is the autoload module itself.  Uncommon.
  631%     - We already loaded this module. Note that
  632%       '$get_predicate_attribute'/3 alone is not enough as it will
  633%       consider auto-import from `user`. '$c_current_predicate'/2
  634%       verifies the predicate really exists, but doesn't validate
  635%       that it is defined.
  636%     - We must load the module and import the target predicate.
  637
  638do_autoload(Library, Module:Name/Arity, LoadModule) :-
  639    functor(Head, Name, Arity),
  640    '$update_autoload_level'([autoload(true)], Old),
  641    verbose_autoload(Module:Name/Arity, Library),
  642    '$compilation_mode'(OldComp, database),
  643    (   Module == LoadModule
  644    ->  ensure_loaded(Module:Library)
  645    ;   (   '$c_current_predicate'(_, LoadModule:Head),
  646            '$get_predicate_attribute'(LoadModule:Head, defined, 1),
  647            \+ '$loading'(Library)
  648        ->  Module:import(LoadModule:Name/Arity)
  649        ;   use_module(Module:Library, [Name/Arity])
  650        ),
  651        warn_autoload(Module, LoadModule:Name/Arity)
  652    ),
  653    '$set_compilation_mode'(OldComp),
  654    '$set_autoload_level'(Old),
  655    '$c_current_predicate'(_, Module:Head).
  656
  657verbose_autoload(PI, Library) :-
  658    current_prolog_flag(verbose_autoload, true),
  659    !,
  660    set_prolog_flag(verbose_autoload, false),
  661    print_message(informational, autoload(PI, Library)),
  662    set_prolog_flag(verbose_autoload, true).
  663verbose_autoload(PI, Library) :-
  664    print_message(silent, autoload(PI, Library)).
  665
  666
  667%!  autoloadable(:Head, -File) is nondet.
  668%
  669%   True when Head can be  autoloaded   from  File.  This implements the
  670%   predicate_property/2 property autoload(File).  The   module  must be
  671%   instantiated.
  672
  673:- public                               % used from predicate_property/2
  674    autoloadable/2.  675
  676autoloadable(M:Head, FullFile) :-
  677    atom(M),
  678    current_module(M),
  679    autoload_in(M, explicit),
  680    (   callable(Head)
  681    ->  goal_name_arity(Head, Name, Arity),
  682        autoload_from(M:Name/Arity, _, FullFile)
  683    ;   findall((M:H)-F, autoloadable_2(M:H, F), Pairs),
  684        (   '$member'(M:Head-FullFile, Pairs)
  685        ;   current_autoload(M:File, Ctx, all),
  686            library_info(File, Ctx, FullFile, _, Exports),
  687            '$member'(PI, Exports),
  688            '$pi_head'(PI, Head),
  689            \+ memberchk(M:Head-_, Pairs)
  690        )
  691    ).
  692autoloadable(M:Head, FullFile) :-
  693    (   var(M)
  694    ->  autoload_in(any, general)
  695    ;   autoload_in(M, general)
  696    ),
  697    (   callable(Head)
  698    ->  goal_name_arity(Head, Name, Arity),
  699        (   '$find_library'(_, Name, Arity, _, FullFile)
  700        ->  true
  701        )
  702    ;   '$in_library'(Name, Arity, autoload),
  703        functor(Head, Name, Arity)
  704    ).
  705
  706
  707autoloadable_2(M:Head, FullFile) :-
  708    current_autoload(M:File, Ctx, import(Imports)),
  709    library_info(File, Ctx, FullFile, _LoadModule, _Exports),
  710    '$member'(PI, Imports),
  711    '$pi_head'(PI, Head).
  712
  713goal_name_arity(Head, Name, Arity) :-
  714    compound(Head),
  715    !,
  716    compound_name_arity(Head, Name, Arity).
  717goal_name_arity(Head, Head, 0).
  718
  719%!  library_info(+Spec, +AutoloadContext, -FullFile, -Module, -Exports)
  720%
  721%   Find information about a library.
  722
  723library_info(Spec, _, FullFile, Module, Exports) :-
  724    '$resolved_source_path'(Spec, FullFile, []),
  725    !,
  726    (   \+ '$loading_file'(FullFile, _Queue, _LoadThread)
  727    ->  '$current_module'(Module, FullFile),
  728        '$module_property'(Module, exports(Exports))
  729    ;   library_info_from_file(FullFile, Module, Exports)
  730    ).
  731library_info(Spec, Context, FullFile, Module, Exports) :-
  732    (   Context = (Path:_Line)
  733    ->  Extra = [relative_to(Path)]
  734    ;   Extra = []
  735    ),
  736    (   absolute_file_name(Spec, FullFile,
  737                           [ file_type(prolog),
  738                             access(read),
  739                             file_errors(fail)
  740                           | Extra
  741                           ])
  742    ->  '$register_resolved_source_path'(Spec, FullFile),
  743        library_info_from_file(FullFile, Module, Exports)
  744    ;   autoload_error(Context, no_file(Spec)),
  745        fail
  746    ).
  747
  748library_info_from_file(FullFile, Module, Exports) :-
  749    setup_call_cleanup(
  750        '$set_source_module'(OldModule, system),
  751        setup_call_cleanup(
  752            '$open_source'(FullFile, In, State, [], []),
  753            '$term_in_file'(In, _Read, _RLayout, Term, _TLayout, _Stream,
  754                            [FullFile], []),
  755            '$close_source'(State, true)),
  756        '$set_source_module'(OldModule)),
  757    (   Term = (:- module(Module, Exports))
  758    ->  !
  759    ;   nonvar(Term),
  760        skip_header(Term)
  761    ->  fail
  762    ;   '$domain_error'(module_header, Term)
  763    ).
  764
  765skip_header(begin_of_file).
  766
  767
  768:- dynamic printed/3.  769:- volatile printed/3.  770
  771autoload_error(Context, Error) :-
  772    suppress(Context, Error),
  773    !.
  774autoload_error(Context, Error) :-
  775    get_time(Now),
  776    assertz(printed(Context, Error, Now)),
  777    print_message(warning, error(autoload(Error), autoload(Context))).
  778
  779suppress(Context, Error) :-
  780    printed(Context, Error, Printed),
  781    get_time(Now),
  782    (   Now - Printed < 1
  783    ->  true
  784    ;   retractall(printed(Context, Error, _)),
  785        fail
  786    ).
  787
  788
  789		 /*******************************
  790		 *            CALLBACK		*
  791		 *******************************/
  792
  793:- public
  794    set_autoload/1.  795
  796%!  set_autoload(+Value) is det.
  797%
  798%   Hook called from set_prolog_flag/2 when  autoloading is switched. If
  799%   the desired value is `false` we   should  materialize all registered
  800%   requests for autoloading. We must do so before disabling autoloading
  801%   as loading the files may require autoloading.
  802
  803set_autoload(FlagValue) :-
  804    current_prolog_flag(autoload, FlagValue),
  805    !.
  806set_autoload(FlagValue) :-
  807    \+ autoload_in(FlagValue, explicit, any),
  808    !,
  809    setup_call_cleanup(
  810        nb_setval('$autoload_disabling', true),
  811        materialize_autoload(Count),
  812        nb_delete('$autoload_disabling')),
  813    print_message(informational, autoload(disabled(Count))).
  814set_autoload(_).
  815
  816materialize_autoload(Count) :-
  817    State = state(0),
  818    forall(current_predicate(M:'$autoload'/3),
  819           materialize_autoload(M, State)),
  820    arg(1, State, Count).
  821
  822materialize_autoload(M, State) :-
  823    (   current_autoload(M:File, Context, Import),
  824        library_info(File, Context, FullFile, _LoadModule, _Exports),
  825        arg(1, State, N0),
  826        N is N0+1,
  827        nb_setarg(1, State, N),
  828        (   Import == all
  829        ->  verbose_autoload(M:all, FullFile),
  830            use_module(M:FullFile)
  831        ;   Import = import(Preds)
  832        ->  verbose_autoload(M:Preds, FullFile),
  833            use_module(M:FullFile, Preds)
  834        ),
  835        fail
  836    ;   true
  837    ),
  838    abolish(M:'$autoload'/3).
  839
  840
  841		 /*******************************
  842		 *          AUTOLOAD/2		*
  843		 *******************************/
  844
  845autoload(M:File) :-
  846    (   \+ autoload_in(M, explicit)
  847    ;   nb_current('$autoload_disabling', true)
  848    ),
  849    !,
  850    use_module(M:File).
  851autoload(M:File) :-
  852    '$must_be'(filespec, File),
  853    source_context(Context),
  854    (   current_autoload(M:File, _, import(all))
  855    ->  true
  856    ;   assert_autoload(M:'$autoload'(File, Context, all))
  857    ).
  858
  859autoload(M:File, Imports) :-
  860    (   \+ autoload_in(M, explicit)
  861    ;   nb_current('$autoload_disabling', true)
  862    ),
  863    !,
  864    use_module(M:File, Imports).
  865autoload(M:File, Imports0) :-
  866    '$must_be'(filespec, File),
  867    valid_imports(Imports0, Imports),
  868    source_context(Context),
  869    register_autoloads(Imports, M, File, Context),
  870    (   current_autoload(M:File, _, import(Imports))
  871    ->  true
  872    ;   assert_autoload(M:'$autoload'(File, Context, import(Imports)))
  873    ).
  874
  875source_context(Path:Line) :-
  876    source_location(Path, Line),
  877    !.
  878source_context(-).
  879
  880assert_autoload(Clause) :-
  881    '$initialization_context'(Source, Ctx),
  882    '$store_admin_clause2'(Clause, _Layout, Source, Ctx).
  883
  884valid_imports(Imports0, Imports) :-
  885    '$must_be'(list, Imports0),
  886    valid_import_list(Imports0, Imports).
  887
  888valid_import_list([], []).
  889valid_import_list([H0|T0], [H|T]) :-
  890    '$pi_head'(H0, Head),
  891    '$pi_head'(H, Head),
  892    valid_import_list(T0, T).
  893
  894%!  register_autoloads(+ListOfPI, +Module, +File, +Context)
  895%
  896%   Put an `autoload` flag on all   predicates declared using autoload/2
  897%   to prevent duplicates or the user defining the same predicate.
  898
  899register_autoloads([], _, _, _).
  900register_autoloads([PI|T], Module, File, Context) :-
  901    PI = Name/Arity,
  902    functor(Head, Name, Arity),
  903    (   '$get_predicate_attribute'(Module:Head, autoload, 1)
  904    ->  (   current_autoload(Module:_File0, _Ctx0, import(Imports)),
  905            memberchk(PI, Imports)
  906        ->  '$permission_error'(redefine, imported_procedure, PI),
  907            fail
  908        ;   Done = true
  909        )
  910    ;   '$c_current_predicate'(_, Module:Head), % no auto-import
  911        '$get_predicate_attribute'(Module:Head, imported, From)
  912    ->  (   (   '$resolved_source_path'(File, FullFile)
  913            ->  true
  914            ;   '$resolve_source_path'(File, FullFile, [])
  915            ),
  916            module_property(From, file(FullFile))
  917        ->  Done = true
  918        ;   print_message(warning,
  919                          autoload(already_defined(Module:PI, From))),
  920            Done = true
  921        )
  922    ;   true
  923    ),
  924    (   Done == true
  925    ->  true
  926    ;   '$set_predicate_attribute'(Module:Head, autoload, 1)
  927    ),
  928    register_autoloads(T, Module, File, Context).
  929
  930pi_in_exports(PI, Exports) :-
  931    '$member'(E, Exports),
  932    canonical_pi(E, PI),
  933    !.
  934
  935current_autoload(M:File, Context, Term) :-
  936    '$get_predicate_attribute'(M:'$autoload'(_,_,_), defined, 1),
  937    M:'$autoload'(File, Context, Term).
  938
  939		 /*******************************
  940		 *            CHECK		*
  941		 *******************************/
  942
  943warn_autoload(TargetModule, PI) :-
  944    current_prolog_flag(warn_autoload, true),
  945    \+ current_prolog_flag(xref, true),
  946    \+ nb_current('$autoload_warning', true),
  947    '$pi_head'(PI, Head),
  948    source_file(Head, File),
  949    expansion_hook(P),
  950    source_file(P, File),
  951    !,
  952    setup_call_cleanup(
  953        b_setval('$autoload_warning', true),
  954        print_message(warning,
  955                      deprecated(autoload(TargetModule, File, PI, expansion))),
  956        nb_delete('$autoload_warning')).
  957warn_autoload(_, _).
  958
  959expansion_hook(user:goal_expansion(_,_)).
  960expansion_hook(user:goal_expansion(_,_,_,_)).
  961expansion_hook(system:goal_expansion(_,_)).
  962expansion_hook(system:goal_expansion(_,_,_,_)).
  963
  964
  965                 /*******************************
  966                 *             REQUIRE          *
  967                 *******************************/
  968
  969%!  require(:ListOfPredIndicators) is det.
  970%
  971%   Register the predicates  in   ListOfPredIndicators  for  autoloading
  972%   using autoload/2 if they are not system predicates.
  973
  974require(M:Spec) :-
  975    (   is_list(Spec)
  976    ->  List = Spec
  977    ;   phrase(comma_list(Spec), List)
  978    ), !,
  979    require(List, M, FromLib),
  980    keysort(FromLib, Sorted),
  981    by_file(Sorted, Autoload),
  982    forall('$member'(File-Import, Autoload),
  983           autoload(M:File, Import)).
  984require(_:Spec) :-
  985    '$type_error'(list, Spec).
  986
  987require([],_, []).
  988require([H|T], M, Needed) :-
  989   '$pi_head'(H, Head),
  990   (   '$get_predicate_attribute'(system:Head, defined, 1)
  991   ->  require(T, M, Needed)
  992   ;   '$pi_head'(Module:Name/Arity, M:Head),
  993       (   '$find_library'(Module, Name, Arity, LoadModule, Library)
  994       ->  (   current_predicate(LoadModule:Name/Arity)
  995           ->  Module:import(LoadModule:Name/Arity),
  996               require(T, M, Needed)
  997           ;   Needed = [Library-H|More],
  998               require(T, M, More)
  999           )
 1000       ;   print_message(error, error(existence_error(procedure, Name/Arity), _)),
 1001           require(T, M, Needed)
 1002       )
 1003   ).
 1004
 1005by_file([], []).
 1006by_file([File-PI|T0], [Spec-[PI|PIs]|T]) :-
 1007    on_path(File, Spec),
 1008    same_file(T0, File, PIs, T1),
 1009    by_file(T1, T).
 1010
 1011on_path(Library, library(Base)) :-
 1012    file_base_name(Library, Base),
 1013    findall(Path, plain_source(library(Base), Path), [Library]),
 1014    !.
 1015on_path(Library, Library).
 1016
 1017plain_source(Spec, Path) :-
 1018    absolute_file_name(Spec, PathExt,
 1019                       [ file_type(prolog),
 1020                         access(read),
 1021                         file_errors(fail),
 1022                         solutions(all)
 1023                       ]),
 1024    file_name_extension(Path, _, PathExt).
 1025
 1026same_file([File-PI|T0], File, [PI|PIs], T) :-
 1027    !,
 1028    same_file(T0, File, PIs, T).
 1029same_file(List, _, [], List).
 1030
 1031comma_list(Var) -->
 1032    { var(Var),
 1033      !,
 1034      '$instantiation_error'(Var)
 1035    }.
 1036comma_list((A,B)) -->
 1037    !,
 1038    comma_list(A),
 1039    comma_list(B).
 1040comma_list(A) -->
 1041    [A]