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