View source with formatted 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-2026, VU University Amsterdam
    7                              CWI, Amsterdam
    8			      SWI-Prolog Solutions b.v.
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(check_installation,
   38          [ check_installation/0,
   39            check_installation/1,               % -Issues
   40            check_config_files/0,
   41            update_config_files/0,
   42            test_installation/0,
   43            test_installation/1                 % +Options
   44          ]).   45:- autoload(library(apply), [maplist/2, maplist/3]).   46:- autoload(library(archive), [archive_open/3, archive_close/1]).   47:- autoload(library(lists), [append/3, member/2]).   48:- autoload(library(occurs), [sub_term/2]).   49:- autoload(library(option), [option/2, merge_options/3]).   50:- autoload(library(prolog_source), [path_segments_atom/2]).   51:- use_module(library(settings), [setting/2]).   52:- autoload(library(dcg/high_order), [sequence//2, sequence/4]).   53:- autoload(library(error), [must_be/2]).   54
   55
   56/** <module> Check installation issues and features
   57
   58This library performs checks on  the   installed  system to verify which
   59optional components are available and  whether  all  libraries that load
   60shared objects/DLLs can be loaded.
   61*/
   62
   63%!  component(?Component, -Features) is nondet.
   64%
   65%   This predicate describes the test components. Features is a dict
   66%   with the following components:
   67%
   68%     - test:Goal
   69%     (Additional) test that must succeed for the component to be
   70%     functional.
   71%     - url:URL
   72%     URL with additional information, relative to
   73%     =|http://www.swi-prolog.org/build/issues/|=.  If not provided,
   74%     the library file with extension =|.html|= is used.
   75%     - optional:true
   76%     If the library does not exist, do not complain.
   77%     - os:OS
   78%     One of =windows=, =unix= or =linux=. If present, the component
   79%     is only checked for if we are running on a version of the
   80%     specified operating system.
   81%     - features:Goal
   82%     After successful evaluation that loading and basic operation
   83%     of the component succeeds, run this to check additional
   84%     features.
   85
   86% Feature tests
   87component(tcmalloc,
   88          #{ optional:true,
   89             test:test_tcmalloc,
   90             url:'tcmalloc.html',
   91             os:linux
   92           }).
   93component(gmp,
   94          #{ test:current_prolog_flag(bounded, false),
   95             url:'gmp.html'
   96           }).
   97% Packages that depend on foreign libraries
   98component(library(archive), #{features:archive_features}).
   99component(library(cgi), #{}).
  100component(library(crypt), #{}).
  101component(library(bdb), #{}).
  102component(library(double_metaphone), #{}).
  103component(library(editline), #{}).
  104component(library(filesex), #{}).
  105component(library(http/http_stream), #{}).
  106component(library(json), #{}).
  107component(library(http/jquery), #{features:jquery_file}).
  108component(library(isub), #{}).
  109component(library(janus), #{features:python_version}).
  110component(library(jpl), #{}).
  111component(library(memfile), #{}).
  112component(library(odbc), #{}).
  113component(library(pce),
  114          #{pre:use_foreign_library(pce_principal:foreign(pl2xpce)),
  115            url:'xpce.html'}).
  116component(library(pcre), #{features:pcre_features}).
  117component(library(pdt_console), #{}).
  118component(library(porter_stem), #{}).
  119component(library(process), #{}).
  120component(library(protobufs), #{}).
  121component(library(readutil), #{}).
  122component(library(rlimit), #{os:unix}).
  123component(library(semweb/rdf_db), #{}).
  124component(library(semweb/rdf_ntriples), #{}).
  125component(library(semweb/turtle), #{}).
  126component(library(sgml), #{}).
  127component(library(sha), #{}).
  128component(library(snowball), #{}).
  129component(library(socket), #{}).
  130component(library(ssl), #{features:ssl_version}).
  131component(library(sweep_link), #{features:sweep_emacs_module}).
  132component(library(crypto), #{}).
  133component(library(syslog), #{os:unix}).
  134component(library(table), #{}).
  135component(library(time), #{}).
  136component(library(tipc/tipc), #{os:linux}).
  137component(library(unicode), #{}).
  138component(library(uri), #{}).
  139component(library(uuid), #{}).
  140component(library(yaml), #{}).
  141component(library(zlib), #{}).
  142
  143issue_base('http://www.swi-prolog.org/build/issues/').
  144
  145:- thread_local
  146    issue/1.  147
  148:- meta_predicate
  149    run_silent(0, +).  150
  151%!  check_installation is det.
  152%
  153%   Check features of the installed   system. Performs the following
  154%   tests:
  155%
  156%     1. Test whether features that depend on optional libraries
  157%        are present (e.g., unbounded arithmetic support)
  158%     2. Test that all standard libraries that depend on foreign
  159%        code are present.
  160%     3. provides a test_installation predicate to run the tests
  161%        at runtime if the system was built with -DINSTALL_TESTS
  162%
  163%   If issues are found it prints a   diagnostic message with a link
  164%   to a wiki page with additional information about the issue.
  165
  166check_installation :-
  167    print_message(informational, installation(checking)),
  168    check_installation_(InstallIssues),
  169    check_on_path,
  170    check_config_files(ConfigIssues),
  171    check_autoload,
  172    maplist(print_message(warning), ConfigIssues),
  173    append(InstallIssues, ConfigIssues, Issues),
  174    (   Issues == []
  175    ->  print_message(informational, installation(perfect))
  176    ;   length(Issues, Count),
  177        print_message(warning, installation(imperfect(Count)))
  178    ).
  179
  180%!  check_installation(-Issues:list(pair)) is det.
  181%
  182%   As check_installation/0, but additionally  returns   a  list  of
  183%   Component-Problem pairs. Problem is  one of `optional_not_found`
  184%   (optional component is not present),   `not_found` (component is
  185%   not present) or `failed` (component  is   present  but cannot be
  186%   loaded).
  187
  188check_installation(Issues) :-
  189    check_installation_(Issues0),
  190    maplist(public_issue, Issues0, Issues).
  191
  192public_issue(installation(Term), Source-Issue) :-
  193    functor(Term, Issue, _),
  194    arg(1, Term, Properties),
  195    Source = Properties.source.
  196
  197check_installation_(Issues) :-
  198    retractall(issue(_)),
  199    forall(component(Source, _Properties),
  200           check_component(Source)),
  201    findall(I, retract(issue(I)), Issues).
  202
  203check_component(Source) :-
  204    component(Source, Properties),
  205    !,
  206    check_component(Source, Properties.put(source,Source)).
  207
  208check_component(_Source, Properties) :-
  209    OS = Properties.get(os),
  210    \+ current_os(OS),
  211    !.
  212check_component(Source, Properties) :-
  213    compound(Source),
  214    !,
  215    check_source(Source, Properties).
  216check_component(Feature, Properties) :-
  217    print_message(informational, installation(checking(Feature))),
  218    (   call(Properties.test)
  219    ->  print_message(informational, installation(ok))
  220    ;   print_issue(installation(missing(Properties)))
  221    ).
  222
  223check_source(Source, Properties) :-
  224    exists_source(Source),
  225    !,
  226    print_message(informational, installation(loading(Source))),
  227    (   run_silent(( (   Pre = Properties.get(pre)
  228                     ->  call(Pre)
  229                     ;   true
  230                     ),
  231                     load_files(Source, [silent(true), if(true)])
  232                   ),
  233                   Properties.put(action, load))
  234    ->  test_component(Properties),
  235        print_message(informational, installation(ok)),
  236        check_features(Properties)
  237    ;   true
  238    ).
  239check_source(_Source, Properties) :-
  240    Properties.get(optional) == true,
  241    !,
  242    print_message(silent,
  243                  installation(optional_not_found(Properties))).
  244check_source(_Source, Properties) :-
  245    print_issue(installation(not_found(Properties))).
  246
  247current_os(unix)    :- current_prolog_flag(unix, true).
  248current_os(windows) :- current_prolog_flag(windows, true).
  249current_os(linux)   :- current_prolog_flag(arch, Arch),
  250                       sub_atom(Arch, _, _, _, linux).
  251
  252%!  test_component(+Properties) is semidet.
  253%
  254%   Run additional tests to see whether the component really works.
  255
  256test_component(Dict) :-
  257    Test = Dict.get(test),
  258    !,
  259    call(Test).
  260test_component(_).
  261
  262%!  check_features(+Properties) is semidet.
  263%
  264%   Check for additional features of the components.
  265%
  266%   @see check_component/1 should be used for checking that the
  267%   component works.
  268
  269check_features(Dict) :-
  270    Test = Dict.get(features),
  271    !,
  272    catch(Test, Error,
  273          ( print_message(warning, Error),
  274            fail)).
  275check_features(_).
  276
  277
  278%!  run_silent(:Goal, +Properties) is semidet.
  279%
  280%   Succeed if Goal succeeds  and  does   not  print  any  errors or
  281%   warnings.
  282
  283run_silent(Goal, Properties) :-
  284    run_collect_messages(Goal, Result, Messages),
  285    (   Result == true,
  286        Messages == []
  287    ->  true
  288    ;   print_issue(installation(failed(Properties, Result, Messages))),
  289        fail
  290    ).
  291
  292%!  run_collect_messages(Goal, Result, Messages) is det.
  293%
  294%   Run Goal, unify Result with  =true=, =false= or exception(Error)
  295%   and  messages  with  a  list  of  generated  error  and  warning
  296%   messages. Each message is a term:
  297%
  298%       message(Term,Kind,Lines)
  299%
  300%   @see message_hook/3.
  301
  302:- thread_local
  303    got_message/1.  304
  305run_collect_messages(Goal, Result, Messages) :-
  306    setup_call_cleanup(
  307        asserta((user:thread_message_hook(Term,Kind,Lines) :-
  308                    error_kind(Kind),
  309                    assertz(got_message(message(Term,Kind,Lines)))), Ref),
  310        (   catch(Goal, E, true)
  311        ->  (   var(E)
  312            ->  Result0 = true
  313            ;   Result0 = exception(E)
  314            )
  315        ;   Result0 = false
  316        ),
  317        erase(Ref)),
  318    findall(Msg, retract(got_message(Msg)), Messages),
  319    Result = Result0.
  320
  321error_kind(warning).
  322error_kind(error).
  323
  324
  325                 /*******************************
  326                 *         SPECIAL TESTS        *
  327                 *******************************/
  328
  329%!  test_tcmalloc
  330
  331:- if(current_predicate(malloc_property/1)).  332test_tcmalloc :-
  333    malloc_property('generic.current_allocated_bytes'(Bytes)),
  334    Bytes > 1 000 000.
  335:- else.  336test_tcmalloc :-
  337    fail.
  338:- endif.  339
  340%!  archive_features
  341%
  342%   Report features supported by library(archive).
  343
  344archive_features :-
  345    tmp_file_stream(utf8, Name, Out),
  346    close(Out),
  347    findall(F, archive_filter(F, Name), Filters),
  348    print_message(informational, installation(archive(filters, Filters))),
  349    findall(F, archive_format(F, Name), Formats),
  350    print_message(informational, installation(archive(formats, Formats))),
  351    delete_file(Name).
  352
  353archive_filter(F, Name) :-
  354    a_filter(F),
  355    catch(archive_open(Name, A, [filter(F)]), E, true),
  356    (   var(E)
  357    ->  archive_close(A)
  358    ;   true
  359    ),
  360    \+ subsumes_term(error(domain_error(filter, _),_), E).
  361
  362archive_format(F, Name) :-
  363    a_format(F),
  364    catch(archive_open(Name, A, [format(F)]), E, true),
  365    (   var(E)
  366    ->  archive_close(A)
  367    ;   true
  368    ),
  369    \+ subsumes_term(error(domain_error(format, _),_), E).
  370
  371a_filter(bzip2).
  372a_filter(compress).
  373a_filter(gzip).
  374a_filter(grzip).
  375a_filter(lrzip).
  376a_filter(lzip).
  377a_filter(lzma).
  378a_filter(lzop).
  379a_filter(none).
  380a_filter(rpm).
  381a_filter(uu).
  382a_filter(xz).
  383
  384a_format('7zip').
  385a_format(ar).
  386a_format(cab).
  387a_format(cpio).
  388a_format(empty).
  389a_format(gnutar).
  390a_format(iso9660).
  391a_format(lha).
  392a_format(mtree).
  393a_format(rar).
  394a_format(raw).
  395a_format(tar).
  396a_format(xar).
  397a_format(zip).
  398
  399%!  pcre_features
  400
  401pcre_features :-
  402    findall(X, pcre_missing(X), Missing),
  403    (   Missing == []
  404    ->  true
  405    ;   print_message(warning, installation(pcre_missing(Missing)))
  406    ),
  407    (   re_config(compiled_widths(Widths)),
  408        1 =:= Widths /\ 1
  409    ->  true
  410    ;   print_message(warning, installation(pcre_missing('8-bit support')))
  411    ).
  412
  413pcre_missing(X) :-
  414    pcre_must_have(X),
  415    Term =.. [X,true],
  416    \+ catch(re_config(Term), _, fail).
  417
  418pcre_must_have(unicode).
  419
  420%!  ssl_version
  421
  422ssl_version :-
  423    current_prolog_flag(ssl_library_version, SSLVersion),
  424    print_message(informational, installation(version(SSLVersion))).
  425
  426
  427%!  jquery_file
  428%
  429%   Test whether jquery.js can be found
  430
  431jquery_file :-
  432    setting(jquery:version, File),
  433    (   absolute_file_name(js(File), Path, [access(read), file_errors(fail)])
  434    ->  print_message(informational, installation(jquery(found(Path))))
  435    ;   print_message(warning, installation(jquery(not_found(File))))
  436    ).
  437
  438sweep_emacs_module :-
  439    with_output_to(string(S), write_sweep_module_location),
  440    split_string(S, "\n", "\n", [VersionInfo|Modules]),
  441    must_be(oneof(["V 1"]), VersionInfo),
  442    (   maplist(check_sweep_lib, Modules)
  443    ->  print_message(informational, installation(sweep(found(Modules))))
  444    ;   print_message(warning, installation(sweep(not_found(Modules))))
  445    ).
  446
  447check_sweep_lib(Line) :-
  448    sub_atom(Line, B, _, A, ' '),
  449    sub_atom(Line, 0, B, _, Type),
  450    must_be(oneof(['L', 'M']), Type),
  451    sub_atom(Line, _, A, 0, Lib),
  452    exists_file(Lib).
  453
  454python_version :-
  455    py_call(sys:version, Version),
  456    print_message(informational, installation(janus(Version))).
  457
  458
  459%!  check_on_path
  460%
  461%   Validate that Prolog is installed in   $PATH.  Only performed if the
  462%   running executable is  a  normal   executable  file,  assuming  some
  463%   special installation such as the WASM version otherwise.
  464
  465check_on_path :-
  466    current_prolog_flag(executable, EXEFlag),
  467    prolog_to_os_filename(EXE, EXEFlag),
  468    file_base_name(EXE, Prog),
  469    absolute_file_name(EXE, AbsExe,
  470                       [ access(execute),
  471                         file_errors(fail)
  472                       ]),
  473    !,
  474    prolog_to_os_filename(AbsExe, OsExe),
  475    (   absolute_file_name(path(Prog), OnPath,
  476                           [ access(execute),
  477                             file_errors(fail)
  478                           ])
  479    ->  (   same_file(EXE, OnPath)
  480        ->  true
  481        ;   absolute_file_name(path(Prog), OnPathAny,
  482                               [ access(execute),
  483                                 file_errors(fail),
  484                                 solutions(all)
  485                               ]),
  486            same_file(EXE, OnPathAny)
  487        ->  print_message(warning, installation(not_first_on_path(OsExe, OnPath)))
  488        ;   print_message(warning, installation(not_same_on_path(OsExe, OnPath)))
  489        )
  490    ;   print_message(warning, installation(not_on_path(OsExe, Prog)))
  491    ).
  492check_on_path.
  493
  494
  495		 /*******************************
  496		 *           RUN TESTS		*
  497		 *******************************/
  498
  499%!  test_installation is semidet.
  500%!  test_installation(+Options) is semidet.
  501%
  502%   Run regression tests in the installed system. Requires the system to
  503%   be built using
  504%
  505%	cmake -DINSTALL_TESTS=ON
  506%
  507%   Options processed:
  508%
  509%     - packages(+Boolean)
  510%       When `false`, do not test the packages
  511%     - package(+Package)
  512%       Only test package package.
  513%
  514%   When  running  this  predicate  the   _working  directory_  must  be
  515%   writeable and allow for writing  executable   files.  This is due to
  516%   tests  for  file  system  interaction    and  tests  for  generating
  517%   stand-alone executables. Note also that due to its side effects, the
  518%   predicate should not be invoked twice in the same session.
  519
  520test_installation :-
  521    test_installation([]).
  522
  523test_installation(Options) :-
  524    absolute_file_name(swi(test/test),
  525                       TestFile,
  526                       [ access(read),
  527                         file_errors(fail),
  528                         file_type(prolog)
  529                       ]),
  530    !,
  531    test_installation_run(TestFile, Options).
  532test_installation(_Options) :-
  533    print_message(warning, installation(testing(no_installed_tests))).
  534
  535test_installation_run(TestFile, Options) :-
  536    (   option(package(_), Options)
  537    ->  merge_options(Options,
  538                      [ core(false),
  539                        subdirs(false)
  540                      ], TestOptions)
  541    ;   merge_options(Options,
  542                      [ packages(true)
  543                      ], TestOptions)
  544    ),
  545    load_files(user:TestFile),
  546    current_prolog_flag(verbose, Old),
  547    setup_call_cleanup(
  548        set_prolog_flag(verbose, silent),
  549        user:test([], TestOptions),
  550        set_prolog_flag(verbose, Old)).
  551
  552
  553                 /*******************************
  554                 *            MESSAGES          *
  555                 *******************************/
  556
  557:- multifile
  558    prolog:message//1.  559
  560print_issue(Term) :-
  561    assertz(issue(Term)),
  562    print_message(warning, Term).
  563
  564issue_url(Properties, URL) :-
  565    Local = Properties.get(url),
  566    !,
  567    issue_base(Base),
  568    atom_concat(Base, Local, URL).
  569issue_url(Properties, URL) :-
  570    Properties.get(source) = library(Segments),
  571    !,
  572    path_segments_atom(Segments, Base),
  573    file_name_extension(Base, html, URLFile),
  574    issue_base(Issues),
  575    atom_concat(Issues, URLFile, URL).
  576
  577prolog:message(installation(Message)) -->
  578    message(Message).
  579
  580message(checking) -->
  581    { current_prolog_flag(address_bits, Bits) },
  582    { current_prolog_flag(arch, Arch) },
  583    { current_prolog_flag(home, Home) },
  584    { current_prolog_flag(cpu_count, Cores) },
  585    { current_prolog_flag(build_type, BuildType) -> true ; BuildType = "?" },
  586    [ 'Checking your SWI-Prolog kit for common issues ...'-[], nl, nl ],
  587    [ 'Version: ~`.t~24| '-[] ], '$messages':prolog_message(version), [nl],
  588    [ 'CMake Build type: ~`.t~24| ~w'-[BuildType] ], [nl],
  589    [ 'Address bits: ~`.t~24| ~d'-[Bits] ], [nl],
  590    [ 'Architecture: ~`.t~24| ~w'-[Arch] ], [nl],
  591    [ 'Installed at: ~`.t~24| ~w'-[Home] ], [nl],
  592    [ 'Cores: ~`.t~24| ~w'-[Cores] ], [nl],
  593    [ nl ].
  594message(perfect) -->
  595    [ nl, 'Congratulations, your kit seems sound and complete!'-[] ].
  596message(imperfect(N)) -->
  597    [ 'Found ~w issues.'-[N] ].
  598message(checking(Feature)) -->
  599    [ 'Checking ~w ...'-[Feature], flush ].
  600message(missing(Properties)) -->
  601    [ at_same_line, '~`.t~48| not present'-[] ],
  602    details(Properties).
  603message(loading(Source)) -->
  604    [ 'Loading ~q ...'-[Source], flush ].
  605message(ok) -->
  606    [ at_same_line, '~`.t~48| ok'-[] ].
  607message(optional_not_found(Properties)) -->
  608    [ 'Optional ~q ~`.t~48| not present'-[Properties.source] ].
  609message(not_found(Properties)) -->
  610    [ '~q ~`.t~48| NOT FOUND'-[Properties.source] ],
  611    details(Properties).
  612message(failed(Properties, false, [])) -->
  613    !,
  614    [ at_same_line, '~`.t~48| FAILED'-[] ],
  615    details(Properties).
  616message(failed(Properties, exception(Ex0), [])) -->
  617    !,
  618    { strip_stack(Ex0, Ex),
  619      message_to_string(Ex, Msg) },
  620    [ '~w'-[Msg] ],
  621    details(Properties).
  622message(failed(Properties, true, Messages)) -->
  623    [ at_same_line, '~`.t~48| FAILED'-[] ],
  624    explain(Messages),
  625    details(Properties).
  626message(archive(What, Names)) -->
  627    [ '  Supported ~w: '-[What] ],
  628    list_names(Names).
  629message(pcre_missing(Features)) -->
  630    [ 'Missing libpcre features: '-[] ],
  631    list_names(Features).
  632message(not_first_on_path(EXE, OnPath)) -->
  633    { public_executable(EXE, PublicEXE),
  634      file_base_name(EXE, Prog)
  635    },
  636    [ 'The first ~w on '-[Prog] ], 'PATH', [ ' is ~p, while '-[OnPath], nl ],
  637    [ 'this version is ~p.'-[PublicEXE] ].
  638message(not_same_on_path(EXE, OnPath)) -->
  639    { public_executable(EXE, PublicEXE),
  640      file_base_name(EXE, Prog)
  641    },
  642    [ 'The ~w on '-[Prog] ], 'PATH', [ ' is ~p, while '-[OnPath], nl ],
  643    [ 'this version is ~p.'-[PublicEXE] ].
  644message(not_on_path(EXE, Prog)) -->
  645    { public_bin_dir(EXE, Dir),
  646      prolog_to_os_filename(Dir, OSDir)
  647    },
  648    [ 'Could not find ~w on '-[Prog] ], 'PATH', [ '. '-[], nl ],
  649    [ 'You may wish to add ~p to '-[OSDir] ], 'PATH', [ '. '-[], nl ].
  650message(jquery(found(Path))) -->
  651    [ '  jQuery from ~w'-[Path] ].
  652message(jquery(not_found(File))) -->
  653    [ '  Cannot find jQuery (~w)'-[File] ].
  654message(sweep(found(Paths))) -->
  655    [ '  GNU-Emacs plugin loads'-[] ],
  656    sequence(list_file, Paths).
  657message(sweep(not_found(Paths))) -->
  658    [ '  Could not find all GNU-Emacs libraries'-[] ],
  659    sequence(list_file, Paths).
  660message(testing(no_installed_tests)) -->
  661    [ '  Runtime testing is not enabled.', nl],
  662    [ '  Please recompile the system with INSTALL_TESTS enabled.' ].
  663message(janus(Version)) -->
  664    [ '  Python version ~w'-[Version] ].
  665message(ambiguous_autoload(PI, Paths)) -->
  666    [ 'The predicate ~p can be autoloaded from multiple libraries:'-[PI]],
  667    sequence(list_file, Paths).
  668message(version(Version)) -->
  669    [ '  Using ~w'-[Version] ].
  670message(lib_version(What, Version)) -->
  671    [ '  Using library ~w version ~w'-[What, Version] ].
  672
  673public_executable(EXE, PublicProg) :-
  674    file_base_name(EXE, Prog),
  675    file_directory_name(EXE, ArchDir),
  676    file_directory_name(ArchDir, BinDir),
  677    file_directory_name(BinDir, Home),
  678    file_directory_name(Home, Lib),
  679    file_directory_name(Lib, Prefix),
  680    atomic_list_concat([Prefix, bin, Prog], /, PublicProg),
  681    exists_file(PublicProg),
  682    same_file(EXE, PublicProg),
  683    !.
  684public_executable(EXE, EXE).
  685
  686public_bin_dir(EXE, Dir) :-
  687    public_executable(EXE, PublicEXE),
  688    file_directory_name(PublicEXE, Dir).
  689
  690
  691
  692'PATH' -->
  693    { current_prolog_flag(windows, true) },
  694    !,
  695    [ '%PATH%'-[] ].
  696'PATH' -->
  697    [ '$PATH'-[] ].
  698
  699strip_stack(error(Error, context(prolog_stack(S), Msg)),
  700            error(Error, context(_, Msg))) :-
  701    nonvar(S).
  702strip_stack(Error, Error).
  703
  704details(Properties) -->
  705    { issue_url(Properties, URL), !
  706    },
  707    [ nl, 'See '-[], url(URL) ].
  708details(_) --> [].
  709
  710explain(Messages) -->
  711    { shared_object_error(Messages) },
  712    !,
  713    [nl],
  714    (   { current_prolog_flag(windows, true) }
  715    ->  [ 'Cannot load required DLL'-[] ]
  716    ;   [ 'Cannot load required shared library'-[] ]
  717    ).
  718explain(Messages) -->
  719    print_messages(Messages).
  720
  721shared_object_error(Messages) :-
  722    sub_term(Term, Messages),
  723    subsumes_term(error(shared_object(open, _Message), _), Term),
  724    !.
  725
  726print_messages([]) --> [].
  727print_messages([message(_Term, _Kind, Lines)|T]) -->
  728    Lines, [nl],
  729    print_messages(T).
  730
  731list_names([]) --> [].
  732list_names([H|T]) -->
  733    [ '~w'-[H] ],
  734    (   {T==[]}
  735    ->  []
  736    ;   [ ', '-[] ],
  737        list_names(T)
  738    ).
  739
  740list_file(File) -->
  741    [ nl, '    '-[], url(File) ].
  742
  743
  744		 /*******************************
  745		 *          CONFIG FILES	*
  746		 *******************************/
  747
  748%!  check_config_files
  749%
  750%   Examines the locations of config files.  The config files have moved
  751%   in version 8.1.15
  752
  753check_config_files :-
  754    check_config_files(Issues),
  755    maplist(print_message(warning), Issues).
  756
  757check_config_files(Issues) :-
  758    findall(Issue, check_config_file(Issue), Issues).
  759
  760check_config_file(config(Id, move(Type, OldFile, NewFile))) :-
  761    old_config(Type, Id, OldFile),
  762    access_file(OldFile, exist),
  763    \+ ( new_config(Type, Id, NewFile),
  764         access_file(NewFile, exist)
  765       ),
  766    once(new_config(Type, Id, NewFile)).
  767check_config_file(config(Id, different(Type, OldFile, NewFile))) :-
  768    old_config(Type, Id, OldFile),
  769    access_file(OldFile, exist),
  770    new_config(Type, Id, NewFile),
  771    access_file(NewFile, exist),
  772    \+ same_file(OldFile, NewFile).
  773
  774%!  update_config_files
  775%
  776%   Move config files from their old location to  the new if the file or
  777%   directory exists in the old location but not in the new.
  778
  779update_config_files :-
  780    old_config(Type, Id, OldFile),
  781    access_file(OldFile, exist),
  782    \+ ( new_config(Type, Id, NewFile),
  783         access_file(NewFile, exist)
  784       ),
  785    (   new_config(Type, Id, NewFile),
  786        \+ same_file(OldFile, NewFile),
  787        create_parent_dir(NewFile)
  788    ->  catch(rename_file(OldFile, NewFile), E,
  789              print_message(warning, E)),
  790        print_message(informational, config(Id, moved(Type, OldFile, NewFile)))
  791    ),
  792    fail.
  793update_config_files.
  794
  795old_config(file, init, File) :-
  796    current_prolog_flag(windows, true),
  797    win_folder(appdata, Base),
  798    atom_concat(Base, '/SWI-Prolog/swipl.ini', File).
  799old_config(file, init, File) :-
  800    expand_file_name('~/.swiplrc', [File]).
  801old_config(directory, lib, Dir) :-
  802    expand_file_name('~/lib/prolog', [Dir]).
  803old_config(directory, xpce, Dir) :-
  804    expand_file_name('~/.xpce', [Dir]).
  805old_config(directory, history, Dir) :-
  806    expand_file_name('~/.swipl-dir-history', [Dir]).
  807old_config(directory, pack, Dir) :-
  808    (   catch(expand_file_name('~/lib/swipl/pack', [Dir]), _, fail)
  809    ;   absolute_file_name(swi(pack), Dir,
  810                           [ file_type(directory), solutions(all) ])
  811    ).
  812
  813new_config(file, init, File) :-
  814    absolute_file_name(user_app_config('init.pl'), File,
  815                       [ solutions(all) ]).
  816new_config(directory, lib, Dir) :-
  817    config_dir(user_app_config(lib), Dir).
  818new_config(directory, xpce, Dir) :-
  819    config_dir(user_app_config(xpce), Dir).
  820new_config(directory, history, Dir) :-
  821    config_dir(user_app_config('dir-history'), Dir).
  822new_config(directory, pack, Dir) :-
  823    config_dir([app_data(pack), swi(pack)], Dir).
  824
  825config_dir(Aliases, Dir) :-
  826    is_list(Aliases),
  827    !,
  828    (   member(Alias, Aliases),
  829        absolute_file_name(Alias, Dir,
  830                           [ file_type(directory), solutions(all) ])
  831    *-> true
  832    ;   member(Alias, Aliases),
  833        absolute_file_name(Alias, Dir,
  834                           [ solutions(all) ])
  835    ).
  836config_dir(Alias, Dir) :-
  837    (   absolute_file_name(Alias, Dir,
  838                           [ file_type(directory), solutions(all) ])
  839    *-> true
  840    ;   absolute_file_name(Alias, Dir,
  841                           [ solutions(all) ])
  842    ).
  843
  844create_parent_dir(NewFile) :-
  845    file_directory_name(NewFile, Dir),
  846    create_parent_dir_(Dir).
  847
  848create_parent_dir_(Dir) :-
  849    exists_directory(Dir),
  850    '$my_file'(Dir),
  851    !.
  852create_parent_dir_(Dir) :-
  853    file_directory_name(Dir, Parent),
  854    Parent \== Dir,
  855    create_parent_dir_(Parent),
  856    make_directory(Dir).
  857
  858prolog:message(config(Id, Issue)) -->
  859    [ 'Config: '-[] ],
  860    config_description(Id),
  861    config_issue(Issue).
  862
  863config_description(init) -->
  864    [ '(user initialization file) '-[], nl ].
  865config_description(lib) -->
  866    [ '(user library) '-[], nl ].
  867config_description(pack) -->
  868    [ '(add-ons) '-[], nl ].
  869config_description(history) -->
  870    [ '(command line history) '-[], nl ].
  871config_description(xpce) -->
  872    [ '(gui) '-[], nl ].
  873
  874config_issue(move(Type, Old, New)) -->
  875    [ '  found ~w "~w"'-[Type, Old], nl ],
  876    [ '  new location is "~w"'-[New] ].
  877config_issue(moved(Type, Old, New)) -->
  878    [ '  found ~w "~w"'-[Type, Old], nl ],
  879    [ '  moved to new location "~w"'-[New] ].
  880config_issue(different(Type, Old, New)) -->
  881    [ '  found different ~w "~w"'-[Type, Old], nl ],
  882    [ '  new location is "~w"'-[New] ].
  883
  884		 /*******************************
  885		 *         AUTO LOADING		*
  886		 *******************************/
  887
  888%!  check_autoload
  889%
  890%   Find possible ambiguous predicates in the autoload index.
  891
  892check_autoload :-
  893    findall(Name/Arity, '$in_library'(Name, Arity, _Path), PIs),
  894    msort(PIs, Sorted),
  895    clumped(Sorted, Clumped),
  896    sort(2, >=, Clumped, ClumpedS),
  897    ambiguous_autoload(ClumpedS).
  898
  899ambiguous_autoload([PI-N|T]) :-
  900    N > 1,
  901    !,
  902    warn_ambiguous_autoload(PI),
  903    ambiguous_autoload(T).
  904ambiguous_autoload(_).
  905
  906warn_ambiguous_autoload(PI) :-
  907    PI = Name/Arity,
  908    findall(PlFile,
  909            ( '$in_library'(Name, Arity, File),
  910              file_name_extension(File, pl, PlFile)
  911            ), PlFiles),
  912    print_message(warning, installation(ambiguous_autoload(PI, PlFiles)))