View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker and Richard O'Keefe
    4    E-mail:        J.Wielemaker@cs.vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2014-2018, 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(check_installation,
   37          [ check_installation/0,
   38            check_installation/1,               % -Issues
   39            test_installation/0,
   40            test_installation/1                 % +Options
   41          ]).   42:- use_module(option).

Check installation issues and features

This library performs checks on the installed system to verify which optional components are available and whether all libaries that load shared objects/DLLs can be loaded. */

 component(?Component, -Features) is nondet
This predicate describes the test components. Features is a dict with the following components:
test:Goal
(Additional) test that must succeed for the component to be functional.
url:URL
URL with additional information, relative to http://www.swi-prolog.org/build/issues/. If not provided, the library file with extension .html is used.
optional:true
If the library does not exist, do not complain.
os:OS
One of windows, unix or linux. If present, the component is only checked for if we are running on a version of the specified operating system.
features:Goal
After successful evaluation that loading and basic operation of the component succeeds, run this to check additional features.
   74% Feature tests
   75component(gmp,
   76          _{ test:current_prolog_flag(bounded, false),
   77             url:'gmp.html'
   78           }).
   79% Packages that depend on foreign libraries
   80component(library(archive), _{features:archive_features}).
   81component(library(cgi), _{}).
   82component(library(crypt), _{}).
   83component(library(bdb), _{}).
   84component(library(double_metaphone), _{}).
   85component(library(filesex), _{}).
   86component(library(http/http_stream), _{}).
   87component(library(http/json), _{}).
   88component(library(http/jquery), _{features:jquery_file}).
   89component(library(isub), _{}).
   90component(library(jpl), _{}).
   91component(library(memfile), _{}).
   92component(library(odbc), _{}).
   93component(library(pce),
   94          _{pre:load_foreign_library(pce_principal:foreign(pl2xpce)),
   95            url:'xpce.html'}).
   96component(library(pcre), _{features:pcre_features}).
   97component(library(pdt_console), _{}).
   98component(library(porter_stem), _{}).
   99component(library(process), _{}).
  100component(library(protobufs), _{}).
  101component(library(editline), _{os:unix}).
  102component(library(readline), _{os:unix}).
  103component(library(readutil), _{}).
  104component(library(rlimit), _{os:unix}).
  105component(library(semweb/rdf_db), _{}).
  106component(library(semweb/rdf_ntriples), _{}).
  107component(library(semweb/turtle), _{}).
  108component(library(sgml), _{}).
  109component(library(sha), _{}).
  110component(library(snowball), _{}).
  111component(library(socket), _{}).
  112component(library(ssl), _{}).
  113component(library(crypto), _{}).
  114component(library(syslog), _{os:unix}).
  115component(library(table), _{}).
  116component(library(time), _{}).
  117component(library(tipc/tipc), _{os:linux}).
  118component(library(unicode), _{}).
  119component(library(uri), _{}).
  120component(library(uuid), _{}).
  121component(library(zlib), _{}).
  122component(library(yaml), _{}).
  123
  124issue_base('http://www.swi-prolog.org/build/issues/').
  125
  126:- thread_local
  127    issue/1.  128
  129:- meta_predicate
  130    run_silent(0, +).
 check_installation
Check features of the installed system. Performs the following tests:
  1. Test whether features that depend on optional libraries are present (e.g., unbounded arithmetic support)
  2. Test that all standard libraries that depend on foreign code are present.
  3. provides a test_installation predicate to run the tests at runtime if the system was built with -DINSTALL_TESTS

If issues are found it prints a diagnostic message with a link to a wiki page with additional information about the issue.

  147check_installation :-
  148    print_message(informational, installation(checking)),
  149    check_installation_(Issues),
  150    check_on_path,
  151    (   Issues == []
  152    ->  print_message(informational, installation(perfect))
  153    ;   length(Issues, Count),
  154        print_message(warning, installation(imperfect(Count)))
  155    ).
 check_installation(-Issues:list(pair)) is det
As check_installation/0, but additionally returns a list of Component-Problem pairs. Problem is one of optional_not_found (optional component is not present), not_found (component is not present) or failed (component is present but cannot be loaded).
  165check_installation(Issues) :-
  166    check_installation_(Issues0),
  167    maplist(public_issue, Issues0, Issues).
  168
  169public_issue(installation(Term), Source-Issue) :-
  170    functor(Term, Issue, _),
  171    arg(1, Term, Properties),
  172    Source = Properties.source.
  173
  174check_installation_(Issues) :-
  175    retractall(issue(_)),
  176    forall(component(Source, _Properties),
  177           check_component(Source)),
  178    findall(I, retract(issue(I)), Issues).
  179
  180check_component(Source) :-
  181    component(Source, Properties),
  182    !,
  183    check_component(Source, Properties.put(source,Source)).
  184
  185check_component(Source, Properties) :-
  186    compound(Source),
  187    !,
  188    check_source(Source, Properties).
  189check_component(Feature, Properties) :-
  190    print_message(informational, installation(checking(Feature))),
  191    (   call(Properties.test)
  192    ->  print_message(informational, installation(ok))
  193    ;   print_issue(installation(missing(Properties)))
  194    ).
  195
  196check_source(_Source, Properties) :-
  197    OS = Properties.get(os),
  198    \+ current_os(OS),
  199    !.
  200check_source(Source, Properties) :-
  201    exists_source(Source),
  202    !,
  203    print_message(informational, installation(loading(Source))),
  204    (   run_silent(( (   Pre = Properties.get(pre)
  205                     ->  call(Pre)
  206                     ;   true
  207                     ),
  208                     load_files(Source, [silent(true), if(not_loaded)])
  209                   ),
  210                   Properties.put(action, load))
  211    ->  test_component(Properties),
  212        print_message(informational, installation(ok)),
  213        check_features(Properties)
  214    ;   true
  215    ).
  216check_source(_Source, Properties) :-
  217    Properties.get(optional) == true,
  218    !,
  219    print_message(silent,
  220                  installation(optional_not_found(Properties))).
  221check_source(_Source, Properties) :-
  222    print_issue(installation(not_found(Properties))).
  223
  224current_os(unix)    :- current_prolog_flag(unix, true).
  225current_os(windows) :- current_prolog_flag(windows, true).
  226current_os(linux)   :- current_prolog_flag(arch, Arch), sub_atom(Arch, _, _, _, linux).
 test_component(+Properties) is semidet
Run additional tests to see whether the componnent really works.
  232test_component(Dict) :-
  233    Test = Dict.get(test),
  234    !,
  235    call(Test).
  236test_component(_).
 check_features(+Properties) is semidet
Check for additional features of the components.
See also
- check_component/1 should be used for checking that the component works.
  245check_features(Dict) :-
  246    Test = Dict.get(features),
  247    !,
  248    call(Test).
  249check_features(_).
 run_silent(:Goal, +Properties) is semidet
Succeed if Goal succeeds and does not print any errors or warnings.
  257run_silent(Goal, Properties) :-
  258    run_collect_messages(Goal, Result, Messages),
  259    (   Result == true,
  260        Messages == []
  261    ->  true
  262    ;   print_issue(installation(failed(Properties, Result, Messages))),
  263        fail
  264    ).
 run_collect_messages(Goal, Result, Messages) is det
Run Goal, unify Result with true, false or exception(Error) and messages with a list of generated error and warning messages. Each message is a term:
message(Term,Kind,Lines)
See also
- message_hook/3.
  276:- thread_local
  277    got_message/1.  278
  279run_collect_messages(Goal, Result, Messages) :-
  280    setup_call_cleanup(
  281        asserta((user:thread_message_hook(Term,Kind,Lines) :-
  282                    error_kind(Kind),
  283                    assertz(got_message(message(Term,Kind,Lines)))), Ref),
  284        (   catch(Goal, E, true)
  285        ->  (   var(E)
  286            ->  Result0 = true
  287            ;   Result0 = exception(E)
  288            )
  289        ;   Result0 = false
  290        ),
  291        erase(Ref)),
  292    findall(Msg, retract(got_message(Msg)), Messages),
  293    Result = Result0.
  294
  295error_kind(warning).
  296error_kind(error).
  297
  298
  299                 /*******************************
  300                 *         SPECIAL TESTS        *
  301                 *******************************/
 archive_features
Report features supported by library(archive).
  307archive_features :-
  308    tmp_file_stream(utf8, Name, Out),
  309    close(Out),
  310    findall(F, archive_filter(F, Name), Filters),
  311    print_message(informational, installation(archive(filters, Filters))),
  312    findall(F, archive_format(F, Name), Formats),
  313    print_message(informational, installation(archive(formats, Formats))),
  314    delete_file(Name).
  315
  316archive_filter(F, Name) :-
  317    a_filter(F),
  318    catch(archive_open(Name, A, [filter(F)]), E, true),
  319    (   var(E)
  320    ->  archive_close(A)
  321    ;   true
  322    ),
  323    \+ subsumes_term(error(domain_error(filter, _),_), E).
  324
  325archive_format(F, Name) :-
  326    a_format(F),
  327    catch(archive_open(Name, A, [format(F)]), E, true),
  328    (   var(E)
  329    ->  archive_close(A)
  330    ;   true
  331    ),
  332    \+ subsumes_term(error(domain_error(filter, _),_), E).
  333
  334a_filter(bzip2).
  335a_filter(compress).
  336a_filter(gzip).
  337a_filter(grzip).
  338a_filter(lrzip).
  339a_filter(lzip).
  340a_filter(lzma).
  341a_filter(lzop).
  342a_filter(none).
  343a_filter(rpm).
  344a_filter(uu).
  345a_filter(xz).
  346
  347a_format('7zip').
  348a_format(ar).
  349a_format(cab).
  350a_format(cpio).
  351a_format(empty).
  352a_format(gnutar).
  353a_format(iso9660).
  354a_format(lha).
  355a_format(mtree).
  356a_format(rar).
  357a_format(raw).
  358a_format(tar).
  359a_format(xar).
  360a_format(zip).
 pcre_features
  364pcre_features :-
  365    findall(X, pcre_missing(X), Missing),
  366    (   Missing == []
  367    ->  true
  368    ;   print_message(warning, installation(pcre_missing(Missing)))
  369    ).
  370
  371pcre_missing(X) :-
  372    pcre_must_have(X),
  373    Term =.. [X,true],
  374    \+ catch(re_config(Term), _, fail).
  375
  376pcre_must_have(utf8).
  377pcre_must_have(unicode_properties).
 jquery_file
Test whether jquery.js can be found
  383jquery_file :-
  384    setting(jquery:version, File),
  385    (   absolute_file_name(js(File), Path, [access(read), file_errors(fail)])
  386    ->  print_message(informational, installation(jquery(found(Path))))
  387    ;   print_message(warning, installation(jquery(not_found(File))))
  388    ).
 check_on_path
Validate that Prolog is installed in $PATH
  395check_on_path :-
  396    current_prolog_flag(executable, EXEFlag),
  397    prolog_to_os_filename(EXE, EXEFlag),
  398    file_base_name(EXE, Prog),
  399    absolute_file_name(EXE, AbsExe,
  400                       [ access(execute)
  401                       ]),
  402    prolog_to_os_filename(AbsExe, OsExe),
  403    (   absolute_file_name(path(Prog), OnPath,
  404                           [ access(execute),
  405                             file_errors(fail)
  406                           ])
  407    ->  (   same_file(EXE, OnPath)
  408        ->  true
  409        ;   absolute_file_name(path(Prog), OnPathAny,
  410                               [ access(execute),
  411                                 file_errors(fail),
  412                                 solutions(all)
  413                               ]),
  414            same_file(EXE, OnPathAny)
  415        ->  print_message(warning, installation(not_first_on_path(OsExe, OnPath)))
  416        ;   print_message(warning, installation(not_same_on_path(OsExe, OnPath)))
  417        )
  418    ;   print_message(warning, installation(not_on_path(OsExe, Prog)))
  419    ).
  420
  421
  422		 /*******************************
  423		 *           RUN TESTS		*
  424		 *******************************/
 test_installation is semidet
 test_installation(+Options) is semidet
Run regression tests in the installed system. Requires the system to be built using
cmake -DINSTALL_TESTS=ON

Options processed:

packages(+Boolean)
When false, do not test the packages
package(+Package)
Only test package package.
  441test_installation :-
  442    test_installation([]).
  443
  444test_installation(Options) :-
  445    absolute_file_name(swi(test/test),
  446                       TestFile,
  447                       [ access(read),
  448                         file_errors(fail),
  449                         file_type(prolog)
  450                       ]),
  451    !,
  452    test_installation_run(TestFile, Options).
  453test_installation(_Options) :-
  454    print_message(warning, installation(testing(no_installed_tests))).
  455
  456test_installation_run(TestFile, Options) :-
  457    (   option(package(_), Options)
  458    ->  merge_options(Options,
  459                      [ core(false),
  460                        subdirs(false)
  461                      ], TestOptions)
  462    ;   merge_options(Options,
  463                      [ packages(true)
  464                      ], TestOptions)
  465    ),
  466    load_files(user:TestFile),
  467    current_prolog_flag(verbose, Old),
  468    setup_call_cleanup(
  469        set_prolog_flag(verbose, silent),
  470        user:test([], TestOptions),
  471        set_prolog_flag(verbose, Old)).
  472
  473
  474                 /*******************************
  475                 *            MESSAGES          *
  476                 *******************************/
  477
  478:- multifile
  479    prolog:message//1.  480
  481print_issue(Term) :-
  482    assertz(issue(Term)),
  483    print_message(warning, Term).
  484
  485issue_url(Properties, URL) :-
  486    Local = Properties.get(url),
  487    !,
  488    issue_base(Base),
  489    atom_concat(Base, Local, URL).
  490issue_url(Properties, URL) :-
  491    Properties.get(source) = library(Segments),
  492    !,
  493    path_segments_atom(Segments, Base),
  494    file_name_extension(Base, html, URLFile),
  495    issue_base(Issues),
  496    atom_concat(Issues, URLFile, URL).
  497
  498prolog:message(installation(Message)) -->
  499    message(Message).
  500
  501message(checking) -->
  502    { current_prolog_flag(address_bits, Bits) },
  503    { current_prolog_flag(arch, Arch) },
  504    { current_prolog_flag(home, Home) },
  505    { current_prolog_flag(cpu_count, Cores) },
  506    [ 'Checking your SWI-Prolog kit for common issues ...'-[], nl, nl ],
  507    [ 'Version: ~`.t~24| '-[] ], '$messages':prolog_message(version), [nl],
  508    [ 'Address bits: ~`.t~24| ~d'-[Bits] ], [nl],
  509    [ 'Architecture: ~`.t~24| ~w'-[Arch] ], [nl],
  510    [ 'Installed at: ~`.t~24| ~w'-[Home] ], [nl],
  511    [ 'Cores: ~`.t~24| ~w'-[Cores] ], [nl],
  512    [ nl ].
  513message(perfect) -->
  514    [ nl, 'Congratulations, your kit seems sound and complete!'-[] ].
  515message(imperfect(N)) -->
  516    [ 'Found ~w issues.'-[N] ].
  517message(checking(Feature)) -->
  518    [ 'Checking ~w ...'-[Feature], flush ].
  519message(missing(Properties)) -->
  520    [ at_same_line, '~`.t~48| not present'-[] ],
  521    details(Properties).
  522message(loading(Source)) -->
  523    [ 'Loading ~q ...'-[Source], flush ].
  524message(ok) -->
  525    [ at_same_line, '~`.t~48| ok'-[] ].
  526message(optional_not_found(Properties)) -->
  527    [ 'Optional ~q ~`.t~48| not present'-[Properties.source] ].
  528message(not_found(Properties)) -->
  529    [ '~q ~`.t~48| NOT FOUND'-[Properties.source] ],
  530    details(Properties).
  531message(failed(Properties, false, [])) -->
  532    !,
  533    [ at_same_line, '~`.t~48| FAILED'-[] ],
  534    details(Properties).
  535message(failed(Properties, exception(Ex0), [])) -->
  536    !,
  537    { strip_stack(Ex0, Ex),
  538      message_to_string(Ex, Msg) },
  539    [ '~w'-[Msg] ],
  540    details(Properties).
  541message(failed(Properties, true, Messages)) -->
  542    [ at_same_line, '~`.t~48| FAILED'-[] ],
  543    explain(Messages),
  544    details(Properties).
  545message(archive(What, Names)) -->
  546    [ '  Supported ~w: '-[What] ],
  547    list_names(Names).
  548message(pcre_missing(Features)) -->
  549    [ 'Missing libpcre features: '-[] ],
  550    list_names(Features).
  551message(not_first_on_path(EXE, OnPath)) -->
  552    { public_executable(EXE, PublicEXE),
  553      file_base_name(EXE, Prog)
  554    },
  555    [ 'The first ~w on '-[Prog] ], 'PATH', [ ' is ~p, while '-[OnPath], nl ],
  556    [ 'this version is ~p.'-[PublicEXE] ].
  557message(not_same_on_path(EXE, OnPath)) -->
  558    { public_executable(EXE, PublicEXE),
  559      file_base_name(EXE, Prog)
  560    },
  561    [ 'The ~w on '-[Prog] ], 'PATH', [ ' is ~p, while '-[OnPath], nl ],
  562    [ 'this version is ~p.'-[PublicEXE] ].
  563message(not_on_path(EXE, Prog)) -->
  564    { public_bin_dir(EXE, Dir),
  565      prolog_to_os_filename(Dir, OSDir)
  566    },
  567    [ 'Could not find ~w on '-[Prog] ], 'PATH', [ '. '-[], nl ],
  568    [ 'You may wish to add ~p to '-[OSDir] ], 'PATH', [ '. '-[], nl ].
  569message(jquery(found(Path))) -->
  570    [ '  jQuery from ~w'-[Path] ].
  571message(jquery(not_found(File))) -->
  572    [ '  Cannot find jQuery (~w)'-[File] ].
  573message(testing(no_installed_tests)) -->
  574    [ '  Runtime testing is not enabled.', nl],
  575    [ '  Please recompile the system with INSTALL_TESTS enabled.' ].
  576
  577
  578public_executable(EXE, PublicProg) :-
  579    file_base_name(EXE, Prog),
  580    file_directory_name(EXE, ArchDir),
  581    file_directory_name(ArchDir, BinDir),
  582    file_directory_name(BinDir, Home),
  583    file_directory_name(Home, Lib),
  584    file_directory_name(Lib, Prefix),
  585    atomic_list_concat([Prefix, bin, Prog], /, PublicProg),
  586    exists_file(PublicProg),
  587    same_file(EXE, PublicProg),
  588    !.
  589public_executable(EXE, EXE).
  590
  591public_bin_dir(EXE, Dir) :-
  592    public_executable(EXE, PublicEXE),
  593    file_directory_name(PublicEXE, Dir).
  594
  595
  596
  597'PATH' -->
  598    { current_prolog_flag(windows, true) },
  599    !,
  600    [ '%PATH%'-[] ].
  601'PATH' -->
  602    [ '$PATH'-[] ].
  603
  604strip_stack(error(Error, context(prolog_stack(S), Msg)),
  605            error(Error, context(_, Msg))) :-
  606    nonvar(S).
  607strip_stack(Error, Error).
  608
  609details(Properties) -->
  610    { issue_url(Properties, URL), !
  611    },
  612    [ nl, 'See ~w'-[URL] ].
  613details(_) --> [].
  614
  615explain(Messages) -->
  616    { Messages = [message(error(shared_object(open, _Message), _), _, _)|_]
  617    },
  618    !,
  619    [nl],
  620    (   { current_prolog_flag(windows, true) }
  621    ->  [ 'Cannot load required DLL'-[] ]
  622    ;   [ 'Cannot load required shared library'-[] ]
  623    ).
  624explain(Messages) -->
  625    print_messages(Messages).
  626
  627print_messages([]) --> [].
  628print_messages([message(_Term, _Kind, Lines)|T]) -->
  629    Lines, [nl],
  630    print_messages(T).
  631
  632list_names([]) --> [].
  633list_names([H|T]) -->
  634    [ '~w'-[H] ],
  635    (   {T==[]}
  636    ->  []
  637    ;   [ ', '-[] ],
  638        list_names(T)
  639    )