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    atom_concat('*.', PlExt, Pattern).
  357
  358plfile_in_dir(Dir, Base, PlBase, File) :-
  359    file_name_extension(Base, pl, PlBase),
  360    atomic_list_concat([Dir, '/', PlBase], File).
  361
  362pattern_files([], _, []).
  363pattern_files([H|T], DirS, Files) :-
  364    atom_concat(DirS, H, P0),
  365    expand_file_name(P0, Files0),
  366    '$append'(Files0, Rest, Files),
  367    pattern_files(T, DirS, Rest).
  368
  369library_index_out_of_date(Index, _Files) :-
  370    \+ exists_file(Index),
  371    !.
  372library_index_out_of_date(Index, Files) :-
  373    time_file(Index, IndexTime),
  374    (   time_file('.', DotTime),
  375        DotTime > IndexTime
  376    ;   '$member'(File, Files),
  377        time_file(File, FileTime),
  378        FileTime > IndexTime
  379    ),
  380    !.
  381
  382
  383do_make_library_index(Index, Dir, Files) :-
  384    ensure_slash(Dir, DirS),
  385    catch(setup_call_cleanup(
  386              open(Index, write, Fd),
  387              ( print_message(informational, make(library_index(Dir))),
  388                index_header(Fd),
  389                index_files(Files, DirS, Fd)
  390              ),
  391              close(Fd)),
  392          E, index_error(E)).
  393
  394index_error(E) :-
  395    silent,
  396    E = error(permission_error(open, source_sink, _)),
  397    !.
  398index_error(E) :-
  399    print_message(error, E).
  400
  401
  402index_files([], _, _).
  403index_files([File|Files], DirS, Fd) :-
  404    catch(setup_call_cleanup(
  405              open(File, read, In),
  406              read(In, Term),
  407              close(In)),
  408          E, print_message(warning, E)),
  409    (   Term = (:- module(Module, Public)),
  410        is_list(Public)
  411    ->  atom_concat(DirS, Local, File),
  412        file_name_extension(Base, _, Local),
  413        forall(public_predicate(Public, Name/Arity),
  414               format(Fd, 'index((~k), ~k, ~k, ~k).~n',
  415                      [Name, Arity, Module, Base]))
  416    ;   true
  417    ),
  418    index_files(Files, DirS, Fd).
  419
  420public_predicate(Public, PI) :-
  421    '$member'(PI0, Public),
  422    canonical_pi(PI0, PI).
  423
  424canonical_pi(Var, _) :-
  425    var(Var), !, fail.
  426canonical_pi(Name/Arity, Name/Arity).
  427canonical_pi(Name//A0,   Name/Arity) :-
  428    Arity is A0 + 2.
  429
  430
  431index_header(Fd):-
  432    format(Fd, '/*  Creator: make/0~n~n', []),
  433    format(Fd, '    Purpose: Provide index for autoload~n', []),
  434    format(Fd, '*/~n~n', []).
  435
  436
  437                 /*******************************
  438                 *            EXTENDING         *
  439                 *******************************/
 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.

  456autoload_path(Alias) :-
  457    (   user:file_search_path(autoload, Alias)
  458    ->  true
  459    ;   assertz(user:file_search_path(autoload, Alias)),
  460        reload_library_index
  461    ).
  462
  463system:term_expansion((:- autoload_path(Alias)),
  464                      [ user:file_search_path(autoload, Alias),
  465                        (:- reload_library_index)
  466                      ])