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