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)  2011-2012, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(prolog_autoload,
   36          [ autoload/0,
   37            autoload/1                          % +Options
   38          ]).   39:- use_module(library(option)).   40:- use_module(library(error)).   41:- use_module(library(aggregate)).   42:- use_module(library(prolog_codewalk)).   43
   44:- predicate_options(autoload/1, 1,
   45                     [ verbose(boolean),
   46                       undefined(oneof([ignore,error]))
   47                     ]).   48
   49/** <module> Autoload all dependencies
   50
   51The autoloader is there to smoothen   program  development. It liberates
   52the programmer from finding the  library   that  defines some particular
   53predicate and including  the  proper   use_module/1,2  directive  in the
   54sources. This is even better at the toplevel, where just using maplist/3
   55is way more comfortable than  first   having  to load library(apply). In
   56addition, it reduces the startup time   of  applications by only loading
   57the necessary bits.
   58
   59Of course, there is also a price. One   is  that it becomes less obvious
   60from where some predicate is loaded and  thus whether you have the right
   61definition.  The  second  issue  is  that  it  is  harder  to  create  a
   62stand-alone executable because this executable,   without  access to the
   63development system, can no longer rely  on autoloading. Finally, program
   64analysis becomes harder because the program may be incomplete.
   65
   66This  library  provides  autoload/0  and   autoload/1  to  autoload  all
   67predicates that are referenced by the program. Now, this is not possible
   68in Prolog because the language allows   for constructing arbitrary goals
   69and runtime and calling them (e.g.,  read(X),   call(X)).
   70
   71The classical version relied on  the predicate_property =undefined=. The
   72current version relies on code analysis of the bodies of all clauses and
   73all initialization goals.
   74*/
   75
   76:- thread_local
   77    autoloaded_count/1.   78
   79%!  autoload is det.
   80%!  autoload(+Options) is det.
   81%
   82%   Force all necessary autoloading to be done _now_.  Options:
   83%
   84%       * verbose(+Boolean)
   85%       If =true=, report on the files loaded.
   86%       * undefined(+Action)
   87%       Action defines what happens if the analysis finds a
   88%       definitely undefined predicate.  One of =ignore= or
   89%       =error=.
   90
   91autoload :-
   92    autoload([]).
   93
   94autoload(Options) :-
   95    must_be(list, Options),
   96    statistics(cputime, T0),
   97    aggregate_all(count, source_file(_), OldFileCount),
   98    autoload(0, Iterations, Options),
   99    aggregate_all(count, source_file(_), NewFileCount),
  100    statistics(cputime, T1),
  101    Time is T1-T0,
  102    information_level(Level, Options),
  103    NewFiles is NewFileCount - OldFileCount,
  104    print_message(Level, autoload(completed(Iterations, Time, NewFiles))).
  105
  106
  107autoload(Iteration0, Iterations, Options) :-
  108    statistics(cputime, T0),
  109    autoload_step(NewFiles, NewPreds, Options),
  110    statistics(cputime, T1),
  111    Time is T1-T0,
  112    succ(Iteration0, Iteration),
  113    (   NewFiles > 0
  114    ->  information_level(Level, Options),
  115        print_message(Level, autoload(reiterate(Iteration,
  116                                                NewFiles, NewPreds, Time))),
  117        autoload(Iteration, Iterations, Options)
  118    ;   Iterations = Iteration
  119    ).
  120
  121information_level(Level, Options) :-
  122    (   option(verbose(true), Options, true)
  123    ->  Level = informational
  124    ;   Level = silent
  125    ).
  126
  127%!  autoload_step(-NewFiles, -NewPreds, +Options) is det.
  128%
  129%   Scan through the program and   autoload all undefined referenced
  130%   predicates.
  131%
  132%   @param NewFiles is unified to the number of files loaded
  133%   @param NewPreds is unified to the number of predicates imported
  134%          using the autoloader.
  135
  136autoload_step(NewFiles, NewPreds, Options) :-
  137    option(verbose(Verbose), Options, true),
  138    aggregate_all(count, source_file(_), OldFileCount),
  139    setup_call_cleanup(
  140        ( current_prolog_flag(autoload, OldAutoLoad),
  141          current_prolog_flag(verbose_autoload, OldVerbose),
  142          set_prolog_flag(autoload, true),
  143          set_prolog_flag(verbose_autoload, Verbose),
  144          assert_autoload_hook(Ref),
  145          asserta(autoloaded_count(0))
  146        ),
  147        prolog_walk_code(Options),
  148        ( retract(autoloaded_count(Count)),
  149          erase(Ref),
  150          set_prolog_flag(autoload, OldAutoLoad),
  151          set_prolog_flag(verbose_autoload, OldVerbose)
  152        )),
  153    aggregate_all(count, source_file(_), NewFileCount),
  154    NewPreds = Count,
  155    NewFiles is NewFileCount - OldFileCount.
  156
  157assert_autoload_hook(Ref) :-
  158    asserta((user:message_hook(autoload(Module:Name/Arity, Library), _, _) :-
  159                    autoloaded(Module:Name/Arity, Library)), Ref).
  160
  161:- public
  162    autoloaded/2.  163
  164autoloaded(_, _) :-
  165    retract(autoloaded_count(N)),
  166    succ(N, N2),
  167    asserta(autoloaded_count(N2)),
  168    fail.                                   % proceed with other hooks
  169
  170
  171                 /*******************************
  172                 *            MESSAGES          *
  173                 *******************************/
  174
  175:- multifile
  176    prolog:message//1.  177
  178prolog:message(autoload(reiterate(Iteration, NewFiles, NewPreds, Time))) -->
  179    [ 'Autoloader: iteration ~D resolved ~D predicates \c
  180          and loaded ~D files in ~3f seconds.  Restarting ...'-
  181      [Iteration, NewFiles, NewPreds, Time]
  182    ].
  183prolog:message(autoload(completed(Iterations, Time, NewFiles))) -->
  184    [ 'Autoloader: loaded ~D files in ~D iterations in ~3f seconds'-
  185      [NewFiles, Iterations, Time] ]