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
  180
  181		 /*******************************
  182		 *           RUN TESTS		*
  183		 *******************************/
 test_installation is semidet
 test_installation(+Options) is semidet
Run regression tests in the installed system. Requires the system to be build using
cmake -DINSTALL_TESTS=ON

Options processed:

packages(+Boolean)
When false, do not test the packages
package(+Package)
Only test package package.
  200test_installation :-
  201    test_installation([]).
  202
  203test_installation(Options) :-
  204    absolute_file_name(swi(test/test),
  205                       TestFile,
  206                       [ access(read),
  207                         file_errors(fail),
  208                         file_type(prolog)
  209                       ]),
  210    !,
  211    test_installation_run(TestFile, Options).
  212test_installation(_Options) :-
  213    print_message(warning, installation(testing(no_installed_tests))).
  214
  215test_installation_run(TestFile, Options) :-
  216    (   option(package(_), Options)
  217    ->  merge_options(Options,
  218                      [ core(false),
  219                        subdirs(false)
  220                      ], TestOptions)
  221    ;   merge_options(Options,
  222                      [ packages(true)
  223                      ], TestOptions)
  224    ),
  225    load_files(user:TestFile),
  226    current_prolog_flag(verbose, Old),
  227    setup_call_cleanup(
  228        set_prolog_flag(verbose, silent),
  229        user:test([], TestOptions),
  230        set_prolog_flag(verbose, Old)).
  231
  232check_component(Source) :-
  233    component(Source, Properties),
  234    !,
  235    check_component(Source, Properties.put(source,Source)).
  236
  237check_component(Source, Properties) :-
  238    compound(Source),
  239    !,
  240    check_source(Source, Properties).
  241check_component(Feature, Properties) :-
  242    print_message(informational, installation(checking(Feature))),
  243    (   call(Properties.test)
  244    ->  print_message(informational, installation(ok))
  245    ;   print_issue(installation(missing(Properties)))
  246    ).
  247
  248check_source(_Source, Properties) :-
  249    OS = Properties.get(os),
  250    \+ current_os(OS),
  251    !.
  252check_source(Source, Properties) :-
  253    exists_source(Source),
  254    !,
  255    print_message(informational, installation(loading(Source))),
  256    (   run_silent(( (   Pre = Properties.get(pre)
  257                     ->  call(Pre)
  258                     ;   true
  259                     ),
  260                     load_files(Source, [silent(true), if(not_loaded)])
  261                   ),
  262                   Properties.put(action, load))
  263    ->  test_component(Properties),
  264        print_message(informational, installation(ok)),
  265        check_features(Properties)
  266    ;   true
  267    ).
  268check_source(_Source, Properties) :-
  269    Properties.get(optional) == true,
  270    !,
  271    print_message(silent,
  272                  installation(optional_not_found(Properties))).
  273check_source(_Source, Properties) :-
  274    print_issue(installation(not_found(Properties))).
  275
  276current_os(unix)    :- current_prolog_flag(unix, true).
  277current_os(windows) :- current_prolog_flag(windows, true).
  278current_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.
  284test_component(Dict) :-
  285    Test = Dict.get(test),
  286    !,
  287    call(Test).
  288test_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.
  297check_features(Dict) :-
  298    Test = Dict.get(features),
  299    !,
  300    call(Test).
  301check_features(_).
 run_silent(:Goal, +Properties) is semidet
Succeed if Goal succeeds and does not print any errors or warnings.
  309run_silent(Goal, Properties) :-
  310    run_collect_messages(Goal, Result, Messages),
  311    (   Result == true,
  312        Messages == []
  313    ->  true
  314    ;   print_issue(installation(failed(Properties, Result, Messages))),
  315        fail
  316    ).
 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.
  328:- thread_local
  329    got_message/1.  330
  331run_collect_messages(Goal, Result, Messages) :-
  332    setup_call_cleanup(
  333        asserta((user:thread_message_hook(Term,Kind,Lines) :-
  334                    error_kind(Kind),
  335                    assertz(got_message(message(Term,Kind,Lines)))), Ref),
  336        (   catch(Goal, E, true)
  337        ->  (   var(E)
  338            ->  Result0 = true
  339            ;   Result0 = exception(E)
  340            )
  341        ;   Result0 = false
  342        ),
  343        erase(Ref)),
  344    findall(Msg, retract(got_message(Msg)), Messages),
  345    Result = Result0.
  346
  347error_kind(warning).
  348error_kind(error).
  349
  350
  351                 /*******************************
  352                 *         SPECIAL TESTS        *
  353                 *******************************/
 archive_features
Report features supported by library(archive).
  359archive_features :-
  360    tmp_file_stream(utf8, Name, Out),
  361    close(Out),
  362    findall(F, archive_filter(F, Name), Filters),
  363    print_message(informational, installation(archive(filters, Filters))),
  364    findall(F, archive_format(F, Name), Formats),
  365    print_message(informational, installation(archive(formats, Formats))),
  366    delete_file(Name).
  367
  368archive_filter(F, Name) :-
  369    a_filter(F),
  370    catch(archive_open(Name, A, [filter(F)]), E, true),
  371    (   var(E)
  372    ->  archive_close(A)
  373    ;   true
  374    ),
  375    \+ subsumes_term(error(domain_error(filter, _),_), E).
  376
  377archive_format(F, Name) :-
  378    a_format(F),
  379    catch(archive_open(Name, A, [format(F)]), E, true),
  380    (   var(E)
  381    ->  archive_close(A)
  382    ;   true
  383    ),
  384    \+ subsumes_term(error(domain_error(filter, _),_), E).
  385
  386a_filter(bzip2).
  387a_filter(compress).
  388a_filter(gzip).
  389a_filter(grzip).
  390a_filter(lrzip).
  391a_filter(lzip).
  392a_filter(lzma).
  393a_filter(lzop).
  394a_filter(none).
  395a_filter(rpm).
  396a_filter(uu).
  397a_filter(xz).
  398
  399a_format('7zip').
  400a_format(ar).
  401a_format(cab).
  402a_format(cpio).
  403a_format(empty).
  404a_format(gnutar).
  405a_format(iso9660).
  406a_format(lha).
  407a_format(mtree).
  408a_format(rar).
  409a_format(raw).
  410a_format(tar).
  411a_format(xar).
  412a_format(zip).
 pcre_features
  416pcre_features :-
  417    findall(X, pcre_missing(X), Missing),
  418    (   Missing == []
  419    ->  true
  420    ;   print_message(warning, installation(pcre_missing(Missing)))
  421    ).
  422
  423pcre_missing(X) :-
  424    pcre_must_have(X),
  425    Term =.. [X,true],
  426    \+ catch(re_config(Term), _, fail).
  427
  428pcre_must_have(utf8).
  429pcre_must_have(unicode_properties).
 jquery_file
Test whether jquery.js can be found
  435jquery_file :-
  436    setting(jquery:version, File),
  437    (   absolute_file_name(js(File), Path, [access(read), file_errors(fail)])
  438    ->  print_message(informational, installation(jquery(found(Path))))
  439    ;   print_message(warning, installation(jquery(not_found(File))))
  440    ).
 check_on_path
Validate that Prolog is installed in $PATH
  447check_on_path :-
  448    current_prolog_flag(executable, EXEFlag),
  449    prolog_to_os_filename(EXE, EXEFlag),
  450    file_base_name(EXE, Prog),
  451    absolute_file_name(EXE, AbsExe,
  452                       [ access(execute)
  453                       ]),
  454    prolog_to_os_filename(AbsExe, OsExe),
  455    (   absolute_file_name(path(Prog), OnPath,
  456                           [ access(execute),
  457                             file_errors(fail)
  458                           ])
  459    ->  (   same_file(EXE, OnPath)
  460        ->  true
  461        ;   absolute_file_name(path(Prog), OnPathAny,
  462                               [ access(execute),
  463                                 file_errors(fail),
  464                                 solutions(all)
  465                               ]),
  466            same_file(EXE, OnPathAny)
  467        ->  print_message(warning, installation(not_first_on_path(OsExe, OnPath)))
  468        ;   print_message(warning, installation(not_same_on_path(OsExe, OnPath)))
  469        )
  470    ;   print_message(warning, installation(not_on_path(OsExe, Prog)))
  471    ).
  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    )