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-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(.)).
  57
  58
  59%!  '$find_library'(+Module, +Name, +Arity, -LoadModule, -Library) is semidet.
  60%
  61%   Locate a predicate in the library. Name   and arity are the name
  62%   and arity of  the  predicate  searched   for.  `Module'  is  the
  63%   preferred target module. The return  values   are  the full path
  64%   name (excluding extension) of the library and module declared in
  65%   that file.
  66
  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    !.
  75
  76%!  '$in_library'(+Name, +Arity, -Path) is semidet.
  77%!  '$in_library'(-Name, -Arity, -Path) is nondet.
  78%
  79%   Is true if Name/Arity is in the autoload libraries.
  80
  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).
  91
  92%!  '$define_predicate'(:Head)
  93%
  94%   Make sure PredInd can be called. First  test if the predicate is
  95%   defined. If not, invoke the autoloader.
  96
  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.
 118
 119%!  '$update_library_index'
 120%
 121%   Called from make/0 to update the index   of the library for each
 122%   library directory that has a writable   index.  Note that in the
 123%   Windows  version  access_file/2  is  mostly   bogus.  We  assert
 124%   silent/0 to suppress error messages.
 125
 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).
 147
 148%!  writable_indexed_directory(-Dir) is nondet.
 149%
 150%   True when Dir is an indexed   library  directory with a writable
 151%   index, i.e., an index that can be updated.
 152
 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                ********************************/
 171
 172%!  reload_library_index
 173%
 174%   Reload the index on the next call
 175
 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(_)).
 183
 184
 185%!  load_library_index(?Name, ?Arity) is det.
 186%
 187%   Try to find Name/Arity  in  the   library.  If  the predicate is
 188%   there, we are happy. If not, we  check whether the set of loaded
 189%   libraries has changed and if so we reload the index.
 190
 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).
 228
 229
 230%!  index_file_name(-IndexFile, +Options) is nondet.
 231%
 232%   True if IndexFile is an autoload   index file. Options is passed
 233%   to  absolute_file_name/3.  This  predicate   searches  the  path
 234%   =autoload=.
 235%
 236%   @see file_search_path/2.
 237
 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                ********************************/
 284
 285%!  make_library_index(+Dir) is det.
 286%
 287%   Create an index for autoloading  from   the  directory  Dir. The
 288%   index  file  is  called  INDEX.pl.  In    Dir  contains  a  file
 289%   MKINDEX.pl, this file is loaded and we  assume that the index is
 290%   created by directives that appearin   this  file. Otherwise, all
 291%   source  files  are  scanned  for  their  module-header  and  all
 292%   exported predicates are added to the autoload index.
 293%
 294%   @see make_library_index/2
 295
 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).
 316
 317%!  make_library_index(+Dir, +Patterns:list(atom)) is det.
 318%
 319%   Create an autoload index INDEX.pl for  Dir by scanning all files
 320%   that match any of the file-patterns in Patterns. Typically, this
 321%   appears as a directive in MKINDEX.pl.  For example:
 322%
 323%     ==
 324%     :- make_library_index(., ['*.pl']).
 325%     ==
 326%
 327%   @see make_library_index/1.
 328
 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                 *******************************/
 440
 441%!  autoload_path(+Path) is det.
 442%
 443%   Add Path to the libraries that are  used by the autoloader. This
 444%   extends the search  path  =autoload=   and  reloads  the library
 445%   index.  For example:
 446%
 447%     ==
 448%     :- autoload_path(library(http)).
 449%     ==
 450%
 451%   If this call appears as a directive,  it is term-expanded into a
 452%   clause  for  user:file_search_path/2  and  a  directive  calling
 453%   reload_library_index/0. This keeps source information and allows
 454%   for removing this directive.
 455
 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                      ]).