View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  1985-2012, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module('$autoload',
   37          [ '$find_library'/5,
   38            '$in_library'/3,
   39            '$define_predicate'/1,
   40            '$update_library_index'/0,
   41            make_library_index/1,
   42            make_library_index/2,
   43            reload_library_index/0,
   44            autoload_path/1
   45          ]).   46
   47:- dynamic
   48    library_index/3,                % Head x Module x Path
   49    autoload_directories/1,         % List
   50    index_checked_at/1.             % Time
   51:- volatile
   52    library_index/3,
   53    autoload_directories/1,
   54    index_checked_at/1.   55
   56user:file_search_path(autoload, library(.)).
 $find_library(+Module, +Name, +Arity, -LoadModule, -Library) is semidet
Locate a predicate in the library. Name and arity are the name and arity of the predicate searched for. `Module' is the preferred target module. The return values are the full path name (excluding extension) of the library and module declared in that file.
   67'$find_library'(Module, Name, Arity, LoadModule, Library) :-
   68    load_library_index(Name, Arity),
   69    functor(Head, Name, Arity),
   70    (   library_index(Head, Module, Library),
   71        LoadModule = Module
   72    ;   library_index(Head, LoadModule, Library)
   73    ),
   74    !.
 $in_library(+Name, +Arity, -Path) is semidet
$in_library(-Name, -Arity, -Path) is nondet
Is true if Name/Arity is in the autoload libraries.
   81'$in_library'(Name, Arity, Path) :-
   82    atom(Name), integer(Arity),
   83    !,
   84    load_library_index(Name, Arity),
   85    functor(Head, Name, Arity),
   86    library_index(Head, _, Path).
   87'$in_library'(Name, Arity, Path) :-
   88    load_library_index(Name, Arity),
   89    library_index(Head, _, Path),
   90    functor(Head, Name, Arity).
 $define_predicate(:Head)
Make sure PredInd can be called. First test if the predicate is defined. If not, invoke the autoloader.
   97:- meta_predicate
   98    '$define_predicate'(:).   99
  100'$define_predicate'(Head) :-
  101    '$defined_predicate'(Head),
  102    !.
  103'$define_predicate'(Term) :-
  104    Term = Module:Head,
  105    (   compound(Head)
  106    ->  compound_name_arity(Head, Name, Arity)
  107    ;   Name = Head, Arity = 0
  108    ),
  109    '$undefined_procedure'(Module, Name, Arity, retry).
  110
  111
  112                /********************************
  113                *          UPDATE INDEX         *
  114                ********************************/
  115
  116:- thread_local
  117    silent/0.
 $update_library_index
Called from make/0 to update the index of the library for each library directory that has a writable index. Note that in the Windows version access_file/2 is mostly bogus. We assert silent/0 to suppress error messages.
  126'$update_library_index' :-
  127    setof(Dir, writable_indexed_directory(Dir), Dirs),
  128    !,
  129    setup_call_cleanup(
  130        asserta(silent, Ref),
  131        guarded_make_library_index(Dirs),
  132        erase(Ref)),
  133    (   flag('$modified_index', true, false)
  134    ->  reload_library_index
  135    ;   true
  136    ).
  137'$update_library_index'.
  138
  139guarded_make_library_index([]).
  140guarded_make_library_index([Dir|Dirs]) :-
  141    (   catch(make_library_index(Dir), E,
  142              print_message(error, E))
  143    ->  true
  144    ;   print_message(warning, goal_failed(make_library_index(Dir)))
  145    ),
  146    guarded_make_library_index(Dirs).
 writable_indexed_directory(-Dir) is nondet
True when Dir is an indexed library directory with a writable index, i.e., an index that can be updated.
  153writable_indexed_directory(Dir) :-
  154    index_file_name(IndexFile, [access([read,write])]),
  155    file_directory_name(IndexFile, Dir).
  156writable_indexed_directory(Dir) :-
  157    absolute_file_name(library('MKINDEX'),
  158                       [ file_type(prolog),
  159                         access(read),
  160                         solutions(all),
  161                         file_errors(fail)
  162                       ], MkIndexFile),
  163    file_directory_name(MkIndexFile, Dir),
  164    plfile_in_dir(Dir, 'INDEX', _, IndexFile),
  165    access_file(IndexFile, write).
  166
  167
  168                /********************************
  169                *           LOAD INDEX          *
  170                ********************************/
 reload_library_index
Reload the index on the next call
  176reload_library_index :-
  177    with_mutex('$autoload', clear_library_index).
  178
  179clear_library_index :-
  180    retractall(library_index(_, _, _)),
  181    retractall(autoload_directories(_)),
  182    retractall(index_checked_at(_)).
 load_library_index(?Name, ?Arity) is det
Try to find Name/Arity in the library. If the predicate is there, we are happy. If not, we check whether the set of loaded libraries has changed and if so we reload the index.
  191load_library_index(Name, Arity) :-
  192    atom(Name), integer(Arity),
  193    functor(Head, Name, Arity),
  194    library_index(Head, _, _),
  195    !.
  196load_library_index(_, _) :-
  197    notrace(with_mutex('$autoload', load_library_index_p)).
  198
  199load_library_index_p :-
  200    index_checked_at(Time),
  201    get_time(Now),
  202    Now-Time < 60,
  203    !.
  204load_library_index_p :-
  205    findall(Index, index_file_name(Index, [access(read)]), List0),
  206    list_set(List0, List),
  207    retractall(index_checked_at(_)),
  208    get_time(Now),
  209    assert(index_checked_at(Now)),
  210    (   autoload_directories(List)
  211    ->  true
  212    ;   retractall(library_index(_, _, _)),
  213        retractall(autoload_directories(_)),
  214        read_index(List),
  215        assert(autoload_directories(List))
  216    ).
  217
  218list_set([], R) :-                      % == list_to_set/2 from library(lists)
  219    closel(R).
  220list_set([H|T], R) :-
  221    memberchk(H, R),
  222    !,
  223    list_set(T, R).
  224
  225closel([]) :- !.
  226closel([_|T]) :-
  227    closel(T).
 index_file_name(-IndexFile, +Options) is nondet
True if IndexFile is an autoload index file. Options is passed to absolute_file_name/3. This predicate searches the path autoload.
See also
- file_search_path/2.
  238index_file_name(IndexFile, Options) :-
  239    absolute_file_name(autoload('INDEX'),
  240                       IndexFile,
  241                       [ file_type(prolog),
  242                         solutions(all),
  243                         file_errors(fail)
  244                       | Options
  245                       ]).
  246
  247read_index([]) :- !.
  248read_index([H|T]) :-
  249    !,
  250    read_index(H),
  251    read_index(T).
  252read_index(Index) :-
  253    print_message(silent, autoload(read_index(Dir))),
  254    file_directory_name(Index, Dir),
  255    setup_call_cleanup(
  256        '$push_input_context'(autoload_index),
  257        setup_call_cleanup(
  258            open(Index, read, In),
  259            read_index_from_stream(Dir, In),
  260            close(In)),
  261        '$pop_input_context').
  262
  263read_index_from_stream(Dir, In) :-
  264    repeat,
  265        read(In, Term),
  266        assert_index(Term, Dir),
  267    !.
  268
  269assert_index(end_of_file, _) :- !.
  270assert_index(index(Name, Arity, Module, File), Dir) :-
  271    !,
  272    functor(Head, Name, Arity),
  273    atomic_list_concat([Dir, '/', File], Path),
  274    assertz(library_index(Head, Module, Path)),
  275    fail.
  276assert_index(Term, Dir) :-
  277    print_message(error, illegal_autoload_index(Dir, Term)),
  278    fail.
  279
  280
  281                /********************************
  282                *       CREATE INDEX.pl         *
  283                ********************************/
 make_library_index(+Dir) is det
Create an index for autoloading from the directory Dir. The index file is called INDEX.pl. In Dir contains a file MKINDEX.pl, this file is loaded and we assume that the index is created by directives that appearin this file. Otherwise, all source files are scanned for their module-header and all exported predicates are added to the autoload index.
See also
- make_library_index/2
  296make_library_index(Dir0) :-
  297    forall(absolute_file_name(Dir0, Dir,
  298                              [ expand(true),
  299                                file_type(directory),
  300                                file_errors(fail),
  301                                solutions(all)
  302                              ]),
  303           make_library_index2(Dir)).
  304
  305make_library_index2(Dir) :-
  306    plfile_in_dir(Dir, 'MKINDEX', MkIndex, AbsMkIndex),
  307    access_file(AbsMkIndex, read),
  308    !,
  309    setup_call_cleanup(
  310        working_directory(OldDir, Dir),
  311        load_files(user:MkIndex, [silent(true)]),
  312        working_directory(_, OldDir)).
  313make_library_index2(Dir) :-
  314    findall(Pattern, source_file_pattern(Pattern), PatternList),
  315    make_library_index2(Dir, PatternList).
 make_library_index(+Dir, +Patterns:list(atom)) is det
Create an autoload index INDEX.pl for Dir by scanning all files that match any of the file-patterns in Patterns. Typically, this appears as a directive in MKINDEX.pl. For example:
:- make_library_index(., ['*.pl']).
See also
- make_library_index/1.
  329make_library_index(Dir0, Patterns) :-
  330    forall(absolute_file_name(Dir0, Dir,
  331                              [ expand(true),
  332                                file_type(directory),
  333                                file_errors(fail),
  334                                solutions(all)
  335                              ]),
  336           make_library_index2(Dir, Patterns)).
  337
  338make_library_index2(Dir, Patterns) :-
  339    plfile_in_dir(Dir, 'INDEX', _Index, AbsIndex),
  340    ensure_slash(Dir, DirS),
  341    pattern_files(Patterns, DirS, Files),
  342    (   library_index_out_of_date(AbsIndex, Files)
  343    ->  do_make_library_index(AbsIndex, DirS, Files),
  344        flag('$modified_index', _, true)
  345    ;   true
  346    ).
  347
  348ensure_slash(Dir, DirS) :-
  349    (   sub_atom(Dir, _, _, 0, /)
  350    ->  DirS = Dir
  351    ;   atom_concat(Dir, /, DirS)
  352    ).
  353
  354source_file_pattern(Pattern) :-
  355    user:prolog_file_type(PlExt, prolog),
  356    PlExt \== qlf,
  357    atom_concat('*.', PlExt, Pattern).
  358
  359plfile_in_dir(Dir, Base, PlBase, File) :-
  360    file_name_extension(Base, pl, PlBase),
  361    atomic_list_concat([Dir, '/', PlBase], File).
  362
  363pattern_files([], _, []).
  364pattern_files([H|T], DirS, Files) :-
  365    atom_concat(DirS, H, P0),
  366    expand_file_name(P0, Files0),
  367    '$append'(Files0, Rest, Files),
  368    pattern_files(T, DirS, Rest).
  369
  370library_index_out_of_date(Index, _Files) :-
  371    \+ exists_file(Index),
  372    !.
  373library_index_out_of_date(Index, Files) :-
  374    time_file(Index, IndexTime),
  375    (   time_file('.', DotTime),
  376        DotTime > IndexTime
  377    ;   '$member'(File, Files),
  378        time_file(File, FileTime),
  379        FileTime > IndexTime
  380    ),
  381    !.
  382
  383
  384do_make_library_index(Index, Dir, Files) :-
  385    ensure_slash(Dir, DirS),
  386    '$stage_file'(Index, StagedIndex),
  387    setup_call_catcher_cleanup(
  388        open(StagedIndex, write, Out),
  389        ( print_message(informational, make(library_index(Dir))),
  390          index_header(Out),
  391          index_files(Files, DirS, Out)
  392        ),
  393        Catcher,
  394        install_index(Out, Catcher, StagedIndex, Index)).
  395
  396install_index(Out, Catcher, StagedIndex, Index) :-
  397    catch(close(Out), Error, true),
  398    (   silent
  399    ->  OnError = silent
  400    ;   OnError = error
  401    ),
  402    (   var(Error)
  403    ->  TheCatcher = Catcher
  404    ;   TheCatcher = exception(Error)
  405    ),
  406    '$install_staged_file'(TheCatcher, StagedIndex, Index, OnError).
 index_files(+Files, +Directory, +Out:stream) is det
Write index for Files in Directory to the stream Out.
  412index_files([], _, _).
  413index_files([File|Files], DirS, Fd) :-
  414    catch(setup_call_cleanup(
  415              open(File, read, In),
  416              read(In, Term),
  417              close(In)),
  418          E, print_message(warning, E)),
  419    (   Term = (:- module(Module, Public)),
  420        is_list(Public)
  421    ->  atom_concat(DirS, Local, File),
  422        file_name_extension(Base, _, Local),
  423        forall(public_predicate(Public, Name/Arity),
  424               format(Fd, 'index((~k), ~k, ~k, ~k).~n',
  425                      [Name, Arity, Module, Base]))
  426    ;   true
  427    ),
  428    index_files(Files, DirS, Fd).
  429
  430public_predicate(Public, PI) :-
  431    '$member'(PI0, Public),
  432    canonical_pi(PI0, PI).
  433
  434canonical_pi(Var, _) :-
  435    var(Var), !, fail.
  436canonical_pi(Name/Arity, Name/Arity).
  437canonical_pi(Name//A0,   Name/Arity) :-
  438    Arity is A0 + 2.
  439
  440
  441index_header(Fd):-
  442    format(Fd, '/*  Creator: make/0~n~n', []),
  443    format(Fd, '    Purpose: Provide index for autoload~n', []),
  444    format(Fd, '*/~n~n', []).
  445
  446
  447                 /*******************************
  448                 *            EXTENDING         *
  449                 *******************************/
 autoload_path(+Path) is det
Add Path to the libraries that are used by the autoloader. This extends the search path autoload and reloads the library index. For example:
:- autoload_path(library(http)).

If this call appears as a directive, it is term-expanded into a clause for file_search_path/2 and a directive calling reload_library_index/0. This keeps source information and allows for removing this directive.

  466autoload_path(Alias) :-
  467    (   user:file_search_path(autoload, Alias)
  468    ->  true
  469    ;   assertz(user:file_search_path(autoload, Alias)),
  470        reload_library_index
  471    ).
  472
  473system:term_expansion((:- autoload_path(Alias)),
  474                      [ user:file_search_path(autoload, Alias),
  475                        (:- reload_library_index)
  476                      ])