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)  2011-2020, VU University Amsterdam
    7                              CWI, 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(prolog_autoload,
   37          [ autoload_all/0,
   38            autoload_all/1                      % +Options
   39          ]).   40:- use_module(library(check), []).      % uses :- public predicates
   41
   42:- autoload(library(aggregate),[aggregate_all/3]).   43:- autoload(library(error),[must_be/2,existence_error/2]).   44:- autoload(library(option),[option/2,option/3]).   45:- autoload(library(prolog_codewalk),[prolog_walk_code/1]).   46
   47:- predicate_options(autoload_all/1, 1,
   48                     [ verbose(boolean),
   49                       undefined(oneof([ignore,error]))
   50                     ]).

Autoload all dependencies

The autoloader is there to smoothen program development. It liberates the programmer from finding the library that defines some particular predicate and including the proper use_module/1,2 directive in the sources. This is even better at the toplevel, where just using maplist/3 is way more comfortable than first having to load library(apply). In addition, it reduces the startup time of applications by only loading the necessary bits.

Of course, there is also a price. One is that it becomes less obvious from where some predicate is loaded and thus whether you have the right definition. The second issue is that it is harder to create a stand-alone executable because this executable, without access to the development system, can no longer rely on autoloading.

This library provides autoload_all/0 and autoload_all/1 to autoload all predicates that are referenced by the program. Now, this is in general not possible in Prolog because the language allows for constructing arbitrary goals and runtime and calling them (e.g., read(X), call(X)).

The implementation relies on code analysis of the bodies of all clauses and all initialization goals.

As of SWI-Prolog 8.1.22 the system provides autoload/1,2 directives that mitigate the possible ambiguity. */

   80:- thread_local
   81    autoloaded_count/1.
 autoload_all is det
 autoload_all(+Options) is det
Force all necessary autoloading to be done now. Options:
verbose(+Boolean)
If true (default false), report on the files loaded.
undefined(+Action)
Action defines what happens if the analysis finds a definitely undefined predicate. One of ignore or error. Default is ignore.
   95autoload_all :-
   96    autoload_all([]).
   97
   98autoload_all(Options) :-
   99    must_be(list, Options),
  100    statistics(cputime, T0),
  101    aggregate_all(count, source_file(_), OldFileCount),
  102    call_cleanup(
  103        autoload(0, Iterations, Options),
  104        check:collect_undef(Undef)),
  105    aggregate_all(count, source_file(_), NewFileCount),
  106    statistics(cputime, T1),
  107    Time is T1-T0,
  108    information_level(Level, Options),
  109    NewFiles is NewFileCount - OldFileCount,
  110    print_message(Level, autoload(completed(Iterations, Time, NewFiles))),
  111    report_undefined(Undef).
  112
  113autoload(Iteration0, Iterations, Options) :-
  114    statistics(cputime, T0),
  115    autoload_step(NewFiles, NewPreds, Options),
  116    statistics(cputime, T1),
  117    Time is T1-T0,
  118    succ(Iteration0, Iteration),
  119    (   NewFiles > 0
  120    ->  information_level(Level, Options),
  121        print_message(Level, autoload(reiterate(Iteration,
  122                                                NewFiles, NewPreds, Time))),
  123        autoload(Iteration, Iterations, Options)
  124    ;   Iterations = Iteration
  125    ).
  126
  127information_level(Level, Options) :-
  128    (   option(verbose(true), Options)
  129    ->  Level = informational
  130    ;   Level = silent
  131    ).
 autoload_step(-NewFiles, -NewPreds, +Options) is det
Scan through the program and autoload all undefined referenced predicates.
Arguments:
NewFiles- is unified to the number of files loaded
NewPreds- is unified to the number of predicates imported using the autoloader.
  142autoload_step(NewFiles, NewPreds, Options) :-
  143    option(verbose(Verbose), Options, false),
  144    walk_options(Options, WalkOptions),
  145    aggregate_all(count, source_file(_), OldFileCount),
  146    setup_call_cleanup(
  147        ( current_prolog_flag(autoload, OldAutoLoad),
  148          current_prolog_flag(verbose_autoload, OldVerbose),
  149          set_prolog_flag(autoload, true),
  150          set_prolog_flag(verbose_autoload, Verbose),
  151          assert_autoload_hook(Ref),
  152          asserta(autoloaded_count(0))
  153        ),
  154        prolog_walk_code(WalkOptions),
  155        ( retract(autoloaded_count(Count)),
  156          erase(Ref),
  157          set_prolog_flag(autoload, OldAutoLoad),
  158          set_prolog_flag(verbose_autoload, OldVerbose)
  159        )),
  160    aggregate_all(count, source_file(_), NewFileCount),
  161    NewPreds = Count,
  162    NewFiles is NewFileCount - OldFileCount.
  163
  164assert_autoload_hook(Ref) :-
  165    asserta((user:message_hook(autoload(Module:Name/Arity, Library), _, _) :-
  166                    autoloaded(Module:Name/Arity, Library)), Ref).
  167
  168:- public
  169    autoloaded/2.  170
  171autoloaded(_, _) :-
  172    retract(autoloaded_count(N)),
  173    succ(N, N2),
  174    asserta(autoloaded_count(N2)),
  175    fail.                                   % proceed with other hooks
 walk_options(+AutoloadOptions, -WalkOptions) is det
Construct the option list for the code walker. If we see an undefined predicate, we must collect these rather than printing them or immediately terminating with an exception. This reuses code from library(check).
  184walk_options([], []).
  185walk_options([verbose(V)|T0], [verbose(V)|T]) :-
  186    !,
  187    walk_options(T0, T).
  188walk_options([undefined(error)|T0],
  189             [ undefined(trace),
  190               on_trace(check:found_undef)
  191             | T
  192             ]) :-
  193    !,
  194    walk_options(T0, T).
  195walk_options([_|T0], T) :-
  196    walk_options(T0, T).
 report_undefined(+Undefined) is det
  203report_undefined([]) :-
  204    !.
  205report_undefined(Grouped) :-
  206    existence_error(procedures, Grouped).
  207
  208
  209                 /*******************************
  210                 *            MESSAGES          *
  211                 *******************************/
  212
  213:- multifile
  214    prolog:message//1,
  215    prolog:error_message//1.  216
  217prolog:message(autoload(reiterate(Iteration, NewFiles, NewPreds, Time))) -->
  218    [ 'Autoloader: iteration ~D resolved ~D predicates \c
  219          and loaded ~D files in ~3f seconds.  Restarting ...'-
  220      [Iteration, NewPreds, NewFiles, Time]
  221    ].
  222prolog:message(autoload(completed(Iterations, Time, NewFiles))) -->
  223    [ 'Autoloader: loaded ~D files in ~D iterations in ~3f seconds'-
  224      [NewFiles, Iterations, Time] ].
  225
  226prolog:error_message(existence_error(procedures, Grouped)) -->
  227    prolog:message(check(undefined_procedures, Grouped))