1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: jan@swi-prolog.org 5 WWW: https://www.swi-prolog.org 6 Copyright (c) 2012-2024, 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(prolog_pack, 38 [ pack_list_installed/0, 39 pack_info/1, % +Name 40 pack_list/1, % +Keyword 41 pack_list/2, % +Query, +Options 42 pack_search/1, % +Keyword 43 pack_install/1, % +Name 44 pack_install/2, % +Name, +Options 45 pack_install_local/3, % :Spec, +Dir, +Options 46 pack_upgrade/1, % +Name 47 pack_rebuild/1, % +Name 48 pack_rebuild/0, % All packages 49 pack_remove/1, % +Name 50 pack_remove/2, % +Name, +Options 51 pack_publish/2, % +URL, +Options 52 pack_property/2 % ?Name, ?Property 53 ]). 54:- use_module(library(apply)). 55:- use_module(library(error)). 56:- use_module(library(option)). 57:- use_module(library(readutil)). 58:- use_module(library(lists)). 59:- use_module(library(filesex)). 60:- use_module(library(xpath)). 61:- use_module(library(settings)). 62:- use_module(library(uri)). 63:- use_module(library(dcg/basics)). 64:- use_module(library(dcg/high_order)). 65:- use_module(library(http/http_open)). 66:- use_module(library(json)). 67:- use_module(library(http/http_client), []). 68:- use_module(library(debug), [assertion/1]). 69:- use_module(library(pairs), 70 [pairs_keys/2, map_list_to_pairs/3, pairs_values/2]). 71:- autoload(library(git)). 72:- autoload(library(sgml)). 73:- autoload(library(sha)). 74:- autoload(library(build/tools)). 75:- autoload(library(ansi_term), [ansi_format/3]). 76:- autoload(library(pprint), [print_term/2]). 77:- autoload(library(prolog_versions), [require_version/3, cmp_versions/3]). 78:- autoload(library(ugraphs), [vertices_edges_to_ugraph/3, ugraph_layers/2]). 79:- autoload(library(process), [process_which/2]). 80:- autoload(library(aggregate), [aggregate_all/3]). 81 82:- meta_predicate 83 pack_install_local(, , ).
98 /******************************* 99 * CONSTANTS * 100 *******************************/ 101 102:- setting(server, atom, 'https://www.swi-prolog.org/pack/', 103 'Server to exchange pack information'). 104 105 106 /******************************* 107 * LOCAL DECLARATIONS * 108 *******************************/ 109 110:- op(900, xfx, @). % Token@Version 111 112:- meta_predicate det_if(,). 113 114 /******************************* 115 * PACKAGE INFO * 116 *******************************/
123current_pack(Pack) :- 124 current_pack(Pack, _). 125 126current_pack(Pack, Dir) :- 127 '$pack':pack(Pack, Dir).
134pack_list_installed :-
135 pack_list('', [installed(true)]),
136 validate_dependencies.142pack_info(Name) :- 143 pack_info(info, Name). 144 145pack_info(Level, Name) :- 146 must_be(atom, Name), 147 findall(Info, pack_info(Name, Level, Info), Infos0), 148 ( Infos0 == [] 149 -> print_message(warning, pack(no_pack_installed(Name))), 150 fail 151 ; true 152 ), 153 findall(Def, pack_default(Level, Infos, Def), Defs), 154 append(Infos0, Defs, Infos1), 155 sort(Infos1, Infos), 156 show_info(Name, Infos, [info(Level)]). 157 158 159show_info(_Name, _Properties, Options) :- 160 option(silent(true), Options), 161 !. 162show_info(_Name, _Properties, Options) :- 163 option(show_info(false), Options), 164 !. 165show_info(Name, Properties, Options) :- 166 option(info(list), Options), 167 !, 168 memberchk(title(Title), Properties), 169 memberchk(version(Version), Properties), 170 format('i ~w@~w ~28|- ~w~n', [Name, Version, Title]). 171show_info(Name, Properties, _) :- 172 !, 173 print_property_value('Package'-'~w', [Name]), 174 findall(Term, pack_level_info(info, Term, _, _), Terms), 175 maplist(print_property(Properties), Terms). 176 177print_property(_, nl) :- 178 !, 179 format('~n'). 180print_property(Properties, Term) :- 181 findall(Term, member(Term, Properties), Terms), 182 Terms \== [], 183 !, 184 pack_level_info(_, Term, LabelFmt, _Def), 185 ( LabelFmt = Label-FmtElem 186 -> true 187 ; Label = LabelFmt, 188 FmtElem = '~w' 189 ), 190 multi_valued(Terms, FmtElem, FmtList, Values), 191 atomic_list_concat(FmtList, ', ', Fmt), 192 print_property_value(Label-Fmt, Values). 193print_property(_, _). 194 195multi_valued([H], LabelFmt, [LabelFmt], Values) :- 196 !, 197 H =.. [_|Values]. 198multi_valued([H|T], LabelFmt, [LabelFmt|LT], Values) :- 199 H =.. [_|VH], 200 append(VH, MoreValues, Values), 201 multi_valued(T, LabelFmt, LT, MoreValues). 202 203 204pvalue_column(31). 205print_property_value(Prop-Fmt, Values) :- 206 !, 207 pvalue_column(C), 208 ansi_format(comment, '% ~w:~t~*|', [Prop, C]), 209 ansi_format(code, Fmt, Values), 210 ansi_format([], '~n', []). 211 212pack_info(Name, Level, Info) :- 213 '$pack':pack(Name, BaseDir), 214 pack_dir_info(BaseDir, Level, Info). 215 216pack_dir_info(BaseDir, Level, Info) :- 217 ( Info = directory(BaseDir) 218 ; pack_info_term(BaseDir, Info) 219 ), 220 pack_level_info(Level, Info, _Format, _Default). 221 222:- public pack_level_info/4. % used by web-server 223 224pack_level_info(_, title(_), 'Title', '<no title>'). 225pack_level_info(_, version(_), 'Installed version', '<unknown>'). 226pack_level_info(info, automatic(_), 'Automatic (dependency only)', -). 227pack_level_info(info, directory(_), 'Installed in directory', -). 228pack_level_info(info, link(_), 'Installed as link to'-'~w', -). 229pack_level_info(info, built(_,_), 'Built on'-'~w for SWI-Prolog ~w', -). 230pack_level_info(info, author(_, _), 'Author'-'~w <~w>', -). 231pack_level_info(info, maintainer(_, _), 'Maintainer'-'~w <~w>', -). 232pack_level_info(info, packager(_, _), 'Packager'-'~w <~w>', -). 233pack_level_info(info, home(_), 'Home page', -). 234pack_level_info(info, download(_), 'Download URL', -). 235pack_level_info(_, provides(_), 'Provides', -). 236pack_level_info(_, requires(_), 'Requires', -). 237pack_level_info(_, conflicts(_), 'Conflicts with', -). 238pack_level_info(_, replaces(_), 'Replaces packages', -). 239pack_level_info(info, library(_), 'Provided libraries', -). 240pack_level_info(info, autoload(_), 'Autoload', -). 241 242pack_default(Level, Infos, Def) :- 243 pack_level_info(Level, ITerm, _Format, Def), 244 Def \== (-), 245 \+ memberchk(ITerm, Infos).
251pack_info_term(BaseDir, Info) :- 252 directory_file_path(BaseDir, 'pack.pl', InfoFile), 253 catch( 254 term_in_file(valid_term(pack_info_term), InfoFile, Info), 255 error(existence_error(source_sink, InfoFile), _), 256 ( print_message(error, pack(no_meta_data(BaseDir))), 257 fail 258 )). 259pack_info_term(BaseDir, library(Lib)) :- 260 atom_concat(BaseDir, '/prolog/', LibDir), 261 atom_concat(LibDir, '*.pl', Pattern), 262 expand_file_name(Pattern, Files), 263 maplist(atom_concat(LibDir), Plain, Files), 264 convlist(base_name, Plain, Libs), 265 member(Lib, Libs), 266 Lib \== 'INDEX'. 267pack_info_term(BaseDir, autoload(true)) :- 268 atom_concat(BaseDir, '/prolog/INDEX.pl', IndexFile), 269 exists_file(IndexFile). 270pack_info_term(BaseDir, automatic(Boolean)) :- 271 once(pack_status_dir(BaseDir, automatic(Boolean))). 272pack_info_term(BaseDir, built(Arch, Prolog)) :- 273 pack_status_dir(BaseDir, built(Arch, Prolog, _How)). 274pack_info_term(BaseDir, link(Dest)) :- 275 read_link(BaseDir, _, Dest). 276 277base_name(File, Base) :- 278 file_name_extension(Base, pl, File).
call(Valid, Term) is true.284:- meta_predicate 285 term_in_file(, , ). 286 287term_in_file(Valid, File, Term) :- 288 exists_file(File), 289 setup_call_cleanup( 290 open(File, read, In, [encoding(utf8)]), 291 term_in_stream(Valid, In, Term), 292 close(In)). 293 294term_in_stream(Valid, In, Term) :- 295 repeat, 296 read_term(In, Term0, []), 297 ( Term0 == end_of_file 298 -> !, fail 299 ; Term = Term0, 300 call(Valid, Term0) 301 ). 302 303:- meta_predicate 304 valid_term(,). 305 306valid_term(Type, Term) :- 307 Term =.. [Name|Args], 308 same_length(Args, Types), 309 Decl =.. [Name|Types], 310 ( call(Type, Decl) 311 -> maplist(valid_info_arg, Types, Args) 312 ; print_message(warning, pack(invalid_term(Type, Term))), 313 fail 314 ). 315 316valid_info_arg(Type, Arg) :- 317 must_be(Type, Arg).
324pack_info_term(name(atom)). % Synopsis 325pack_info_term(title(atom)). 326pack_info_term(keywords(list(atom))). 327pack_info_term(description(list(atom))). 328pack_info_term(version(version)). 329pack_info_term(author(atom, email_or_url_or_empty)). % Persons 330pack_info_term(maintainer(atom, email_or_url)). 331pack_info_term(packager(atom, email_or_url)). 332pack_info_term(pack_version(nonneg)). % Package convention version 333pack_info_term(home(atom)). % Home page 334pack_info_term(download(atom)). % Source 335pack_info_term(provides(atom)). % Dependencies 336pack_info_term(requires(dependency)). 337pack_info_term(conflicts(dependency)). % Conflicts with package 338pack_info_term(replaces(atom)). % Replaces another package 339pack_info_term(autoload(boolean)). % Default installation options 340 341:- multifile 342 error:has_type/2. 343 344errorhas_type(version, Version) :- 345 atom(Version), 346 is_version(Version). 347errorhas_type(email_or_url, Address) :- 348 atom(Address), 349 ( sub_atom(Address, _, _, _, @) 350 -> true 351 ; uri_is_global(Address) 352 ). 353errorhas_type(email_or_url_or_empty, Address) :- 354 ( Address == '' 355 -> true 356 ; error:has_type(email_or_url, Address) 357 ). 358errorhas_type(dependency, Value) :- 359 is_dependency(Value). 360 361is_version(Version) :- 362 split_string(Version, ".", "", Parts), 363 maplist(number_string, _, Parts). 364 365is_dependency(Var) :- 366 var(Var), 367 !, 368 fail. 369is_dependency(Token) :- 370 atom(Token), 371 !. 372is_dependency(Term) :- 373 compound(Term), 374 compound_name_arguments(Term, Op, [Token,Version]), 375 atom(Token), 376 cmp(Op, _), 377 is_version(Version), 378 !. 379is_dependency(PrologToken) :- 380 is_prolog_token(PrologToken). 381 382cmp(<, @<). 383cmp(=<, @=<). 384cmp(==, ==). 385cmp(>=, @>=). 386cmp(>, @>). 387 388 389 /******************************* 390 * SEARCH * 391 *******************************/
Options processed:
installed(true).false, do not contact the server. This implies
installed(true). Otherwise, use the given pack server.
Hint: ?- pack_list(''). lists all known packages.
The predicates pack_list/1 and pack_search/1 are synonyms. Both
contact the package server at https://www.swi-prolog.org to find
available packages. Contacting the server can be avoided using the
server(false) option.
433pack_list(Query) :- 434 pack_list(Query, []). 435 436pack_search(Query) :- 437 pack_list(Query, []). 438 439pack_list(Query, Options) :- 440 ( option(installed(true), Options) 441 ; option(outdated(true), Options) 442 ; option(server(false), Options) 443 ), 444 !, 445 local_search(Query, Local), 446 maplist(arg(1), Local, Packs), 447 ( option(server(false), Options) 448 -> Hits = [] 449 ; query_pack_server(info(Packs), true(Hits), Options) 450 ), 451 list_hits(Hits, Local, Options). 452pack_list(Query, Options) :- 453 query_pack_server(search(Query), Result, Options), 454 ( Result == false 455 -> ( local_search(Query, Packs), 456 Packs \== [] 457 -> forall(member(pack(Pack, Stat, Title, Version, _), Packs), 458 format('~w ~w@~w ~28|- ~w~n', 459 [Stat, Pack, Version, Title])) 460 ; print_message(warning, pack(search_no_matches(Query))) 461 ) 462 ; Result = true(Hits), % Hits = list(pack(Name, p, Title, Version, URL)) 463 local_search(Query, Local), 464 list_hits(Hits, Local, []) 465 ). 466 467list_hits(Hits, Local, Options) :- 468 append(Hits, Local, All), 469 sort(All, Sorted), 470 join_status(Sorted, Packs0), 471 include(filtered(Options), Packs0, Packs), 472 maplist(list_hit(Options), Packs). 473 474filtered(Options, pack(_,Tag,_,_,_)) :- 475 option(outdated(true), Options), 476 !, 477 Tag == 'U'. 478filtered(_, _). 479 480list_hit(_Options, pack(Pack, Tag, Title, Version, _URL)) => 481 list_tag(Tag), 482 ansi_format(code, '~w', [Pack]), 483 format('@'), 484 list_version(Tag, Version), 485 format('~35|- ', []), 486 ansi_format(comment, '~w~n', [Title]). 487 488list_tag(Tag) :- 489 tag_color(Tag, Color), 490 ansi_format(Color, '~w ', [Tag]). 491 492list_version(Tag, VersionI-VersionS) => 493 tag_color(Tag, Color), 494 ansi_format(Color, '~w', [VersionI]), 495 ansi_format(bold, '(~w)', [VersionS]). 496list_version(_Tag, Version) => 497 ansi_format([], '~w', [Version]). 498 499tag_color('U', warning) :- !. 500tag_color('A', comment) :- !. 501tag_color(_, []).
pack(Name, Status, Version, URL). If
the versions do not match, Version is
VersionInstalled-VersionRemote and similar for thee URL.510join_status([], []). 511join_status([ pack(Pack, i, Title, Version, URL), 512 pack(Pack, p, Title, Version, _) 513 | T0 514 ], 515 [ pack(Pack, Tag, Title, Version, URL) 516 | T 517 ]) :- 518 !, 519 ( pack_status(Pack, automatic(true)) 520 -> Tag = a 521 ; Tag = i 522 ), 523 join_status(T0, T). 524join_status([ pack(Pack, i, Title, VersionI, URLI), 525 pack(Pack, p, _, VersionS, URLS) 526 | T0 527 ], 528 [ pack(Pack, Tag, Title, VersionI-VersionS, URLI-URLS) 529 | T 530 ]) :- 531 !, 532 version_sort_key(VersionI, VDI), 533 version_sort_key(VersionS, VDS), 534 ( VDI @< VDS 535 -> Tag = 'U' 536 ; Tag = 'A' 537 ), 538 join_status(T0, T). 539join_status([ pack(Pack, i, Title, VersionI, URL) 540 | T0 541 ], 542 [ pack(Pack, l, Title, VersionI, URL) 543 | T 544 ]) :- 545 !, 546 join_status(T0, T). 547join_status([H|T0], [H|T]) :- 548 join_status(T0, T).
554local_search(Query, Packs) :- 555 findall(Pack, matching_installed_pack(Query, Pack), Packs). 556 557matching_installed_pack(Query, pack(Pack, i, Title, Version, URL)) :- 558 current_pack(Pack), 559 findall(Term, 560 ( pack_info(Pack, _, Term), 561 search_info(Term) 562 ), Info), 563 ( sub_atom_icasechk(Pack, _, Query) 564 -> true 565 ; memberchk(title(Title), Info), 566 sub_atom_icasechk(Title, _, Query) 567 ), 568 option(title(Title), Info, '<no title>'), 569 option(version(Version), Info, '<no version>'), 570 option(download(URL), Info, '<no download url>'). 571 572search_info(title(_)). 573search_info(version(_)). 574search_info(download(_)). 575 576 577 /******************************* 578 * INSTALL * 579 *******************************/
http(s) URL of an archive file name. This URL may contain a
star (*) for the version. In this case pack_install/1 asks
for the directory content and selects the latest version.file:// URL'.', in which case a relative symlink is created to the
current directory (all other options for Spec make a copy
of the files). Installation using a symlink is normally
used during development of a pack.
Processes the options below. Default options as would be used by
pack_install/1 are used to complete the provided Options. Note that
pack_install/2 can be used through the SWI-Prolog command line app
pack as below. Most of the options of this predicate are available
as command line options.
swipl pack install <name>
Options:
true, install in the XDG common application data path,
making the pack accessible to everyone. If false, install in
the XDG user application data path, making the pack accessible
for the current user only. If the option is absent, use the
first existing and writable directory. If that doesn't exist
find locations where it can be created and prompt the user to do
so.true (default false), do not perform any checks on SSL
certificates when downloading using https.true (default false), suppress informational progress
messages.true (default false), upgrade package if it is already
installed.if_absent (default, do nothing if the directory with foreign
resources exists), make (run make) or true (run `make
distclean` followed by the default configure and build steps).true (default), run the pack tests.true (default false unless URL ends with .git),
assume the URL is a GIT repository.'1.5' is the
same as >=('1.5').'HEAD'.-DCMAKE_BUILD_TYPE=Type.
Default is the build type of Prolog or Release.true (default), register packages as downloaded after
performing the download. This contacts the server with the
meta-data of each pack that was downloaded. The server will
either register the location as a new version or increment
the download count. The server stores the IP address of the
client. Subsequent downloads of the same version from the
same IP address are ignored.prolog_pack:server, by default set to
https://www.swi-prolog.org/pack/
Non-interactive installation can be established using the option
interactive(false). It is adviced to install from a particular
trusted URL instead of the plain pack name for unattented
operation.
679pack_install(Spec) :- 680 pack_default_options(Spec, Pack, [], Options), 681 pack_install(Pack, [pack(Pack)|Options]). 682 683pack_install(Specs, Options) :- 684 is_list(Specs), 685 !, 686 maplist(pack_options(Options), Specs, Pairs), 687 pack_install_dir(PackTopDir, Options), 688 pack_install_set(Pairs, PackTopDir, Options). 689pack_install(Spec, Options) :- 690 pack_default_options(Spec, Pack, Options, DefOptions), 691 ( option(already_installed(Installed), DefOptions) 692 -> print_message(informational, pack(already_installed(Installed))) 693 ; merge_options(Options, DefOptions, PackOptions), 694 pack_install_dir(PackTopDir, PackOptions), 695 pack_install_set([Pack-PackOptions], PackTopDir, Options) 696 ). 697 698pack_options(Options, Spec, Pack-PackOptions) :- 699 pack_default_options(Spec, Pack, Options, DefOptions), 700 merge_options(Options, DefOptions, PackOptions).
url(URL) option. Determine whether
the URL is a GIT repository, get the version and pack from the
URL.git(true)
and adds the URL as option.packs.pl
file.'.'. Create a symlink to make the current dir
accessible as a pack.726pack_default_options(_Spec, Pack, OptsIn, Options) :- % (1) 727 option(already_installed(pack(Pack,_Version)), OptsIn), 728 !, 729 Options = OptsIn. 730pack_default_options(_Spec, Pack, OptsIn, Options) :- % (2) 731 option(url(URL), OptsIn), 732 !, 733 ( option(git(_), OptsIn) 734 -> Options = OptsIn 735 ; git_url(URL, Pack) 736 -> Options = [git(true)|OptsIn] 737 ; Options = OptsIn 738 ), 739 ( nonvar(Pack) 740 -> true 741 ; option(pack(Pack), Options) 742 -> true 743 ; pack_version_file(Pack, _Version, URL) 744 ). 745pack_default_options(Archive, Pack, OptsIn, Options) :- % (3) 746 must_be(atom, Archive), 747 \+ uri_is_global(Archive), 748 expand_file_name(Archive, [File]), 749 exists_file(File), 750 !, 751 ( pack_version_file(Pack, Version, File) 752 -> uri_file_name(FileURL, File), 753 merge_options([url(FileURL), version(Version)], OptsIn, Options) 754 ; domain_error(pack_file_name, Archive) 755 ). 756pack_default_options(URL, Pack, OptsIn, Options) :- % (4) 757 git_url(URL, Pack), 758 !, 759 merge_options([git(true), url(URL)], OptsIn, Options). 760pack_default_options(FileURL, Pack, _, Options) :- % (5) 761 uri_file_name(FileURL, Dir), 762 exists_directory(Dir), 763 pack_info_term(Dir, name(Pack)), 764 !, 765 ( pack_info_term(Dir, version(Version)) 766 -> uri_file_name(DirURL, Dir), 767 Options = [url(DirURL), version(Version)] 768 ; throw(error(existence_error(key, version, Dir),_)) 769 ). 770pack_default_options('.', Pack, OptsIn, Options) :- % (6) 771 pack_info_term('.', name(Pack)), 772 !, 773 working_directory(Dir, Dir), 774 ( pack_info_term(Dir, version(Version)) 775 -> uri_file_name(DirURL, Dir), 776 NewOptions = [url(DirURL), version(Version) | Options1], 777 ( current_prolog_flag(windows, true) 778 -> Options1 = [] 779 ; Options1 = [link(true), rebuild(make)] 780 ), 781 merge_options(NewOptions, OptsIn, Options) 782 ; throw(error(existence_error(key, version, Dir),_)) 783 ). 784pack_default_options(URL, Pack, OptsIn, Options) :- % (7) 785 pack_version_file(Pack, Version, URL), 786 download_url(URL), 787 !, 788 available_download_versions(URL, Available, Options), 789 Available = [URLVersion-LatestURL|_], 790 NewOptions = [url(LatestURL)|VersionOptions], 791 version_options(Version, URLVersion, Available, VersionOptions), 792 merge_options(NewOptions, OptsIn, Options). 793pack_default_options(Pack, Pack, Options, Options) :- % (8) 794 \+ uri_is_global(Pack). 795 796version_options(Version, Version, _, [version(Version)]) :- !. 797version_options(Version, _, Available, [versions(Available)]) :- 798 sub_atom(Version, _, _, _, *), 799 !. 800version_options(_, _, _, []).
pack_directory(+PackDir)
Use PackDir. PackDir is created if it does not exist.global(+Boolean)
If true, find a writeable global directory based on the
file search path common_app_data. If false, find a
user-specific writeable directory based on user_app_datapack.If no writeable directory is found, generate possible location where this directory can be created and ask the user to create one of them.
820pack_install_dir(PackDir, Options) :- 821 option(pack_directory(PackDir), Options), 822 ensure_directory(PackDir), 823 !. 824pack_install_dir(PackDir, Options) :- 825 base_alias(Alias, Options), 826 absolute_file_name(Alias, PackDir, 827 [ file_type(directory), 828 access(write), 829 file_errors(fail) 830 ]), 831 !. 832pack_install_dir(PackDir, Options) :- 833 pack_create_install_dir(PackDir, Options). 834 835base_alias(Alias, Options) :- 836 option(global(true), Options), 837 !, 838 Alias = common_app_data(pack). 839base_alias(Alias, Options) :- 840 option(global(false), Options), 841 !, 842 Alias = user_app_data(pack). 843base_alias(Alias, _Options) :- 844 Alias = pack('.'). 845 846pack_create_install_dir(PackDir, Options) :- 847 base_alias(Alias, Options), 848 findall(Candidate = create_dir(Candidate), 849 ( absolute_file_name(Alias, Candidate, [solutions(all)]), 850 \+ exists_file(Candidate), 851 \+ exists_directory(Candidate), 852 file_directory_name(Candidate, Super), 853 ( exists_directory(Super) 854 -> access_file(Super, write) 855 ; true 856 ) 857 ), 858 Candidates0), 859 list_to_set(Candidates0, Candidates), % keep order 860 pack_create_install_dir(Candidates, PackDir, Options). 861 862pack_create_install_dir(Candidates, PackDir, Options) :- 863 Candidates = [Default=_|_], 864 !, 865 append(Candidates, [cancel=cancel], Menu), 866 menu(pack(create_pack_dir), Menu, Default, Selected, Options), 867 Selected \== cancel, 868 ( catch(make_directory_path(Selected), E, 869 (print_message(warning, E), fail)) 870 -> PackDir = Selected 871 ; delete(Candidates, PackDir=create_dir(PackDir), Remaining), 872 pack_create_install_dir(Remaining, PackDir, Options) 873 ). 874pack_create_install_dir(_, _, _) :- 875 print_message(error, pack(cannot_create_dir(pack(.)))), 876 fail.
890pack_unpack_from_local(Source0, PackTopDir, Name, PackDir, Options) :- 891 exists_directory(Source0), 892 remove_slash(Source0, Source), 893 !, 894 directory_file_path(PackTopDir, Name, PackDir), 895 ( option(link(true), Options) 896 -> ( same_file(Source, PackDir) 897 -> true 898 ; remove_existing_pack(PackDir, Options), 899 atom_concat(PackTopDir, '/', PackTopDirS), 900 relative_file_name(Source, PackTopDirS, RelPath), 901 link_file(RelPath, PackDir, symbolic), 902 assertion(same_file(Source, PackDir)) 903 ) 904 ; \+ option(git(false), Options), 905 is_git_directory(Source) 906 -> remove_existing_pack(PackDir, Options), 907 run_process(path(git), [clone, Source, PackDir], []) 908 ; prepare_pack_dir(PackDir, Options), 909 copy_directory(Source, PackDir) 910 ). 911pack_unpack_from_local(Source, PackTopDir, Name, PackDir, Options) :- 912 exists_file(Source), 913 directory_file_path(PackTopDir, Name, PackDir), 914 prepare_pack_dir(PackDir, Options), 915 pack_unpack(Source, PackDir, Name, Options).
924:- if(exists_source(library(archive))). 925pack_unpack(Source, PackDir, Pack, Options) :- 926 ensure_loaded_archive, 927 pack_archive_info(Source, Pack, _Info, StripOptions), 928 prepare_pack_dir(PackDir, Options), 929 archive_extract(Source, PackDir, 930 [ exclude(['._*']) % MacOS resource forks 931 | StripOptions 932 ]). 933:- else. 934pack_unpack(_,_,_,_) :- 935 existence_error(library, archive). 936:- endif.
944pack_install_local(M:Gen, Dir, Options) :- 945 findall(Pack-PackOptions, call(M:Gen, Pack, PackOptions), Pairs), 946 pack_install_set(Pairs, Dir, Options). 947 948pack_install_set(Pairs, Dir, Options) :- 949 must_be(list(pair), Pairs), 950 ensure_directory(Dir), 951 partition(known_media, Pairs, Local, Remote), 952 maplist(pack_options_to_versions, Local, LocalVersions), 953 ( Remote == [] 954 -> AllVersions = LocalVersions 955 ; pairs_keys(Remote, Packs), 956 prolog_description(Properties), 957 query_pack_server(versions(Packs, Properties), Result, Options), 958 ( Result = true(RemoteVersions) 959 -> append(LocalVersions, RemoteVersions, AllVersions) 960 ; print_message(error, pack(query_failed(Result))), 961 fail 962 ) 963 ), 964 local_packs(Dir, Existing), 965 pack_resolve(Pairs, Existing, AllVersions, Plan0, Options), 966 !, % for now, only first plan 967 maplist(hsts_info(Options), Plan0, Plan), 968 Options1 = [pack_directory(Dir), installed_packs(Existing)|Options], 969 download_plan(Pairs, Plan, PlanB, Options1), 970 register_downloads(PlanB, Options), 971 maplist(update_automatic, PlanB), 972 build_plan(PlanB, Built, Options1), 973 publish_download(PlanB, Options), 974 work_done(Pairs, Plan, PlanB, Built, Options). 975 976hsts_info(Options, Info0, Info) :- 977 hsts(Info0.get(url), URL, Options), 978 !, 979 Info = Info0.put(url, URL). 980hsts_info(_Options, Info, Info).
989known_media(_-Options) :-
990 option(url(_), Options).pack(Pack, i, Title, Version, URL) terms that represents the already
installed packages. Versions is obtained from the server. See
pack.pl from the web server for details. On success, this results
in a Plan to satisfies the requirements. The plan is a list of
packages to install with their location. The steps satisfy the
partial ordering of dependencies, such that dependencies are
installed before the dependents. Options:
1008pack_resolve(Pairs, Existing, Versions, Plan, Options) :-
1009 insert_existing(Existing, Versions, AllVersions, Options),
1010 phrase(select_version(Pairs, AllVersions,
1011 [ plan(PlanA), % access to plan
1012 dependency_for([]) % dependencies
1013 | Options
1014 ]),
1015 PlanA),
1016 mark_installed(PlanA, Existing, Plan).upgrade(true) is specified, the existing is merged into the set of
Available versions. Otherwise Existing is prepended to Available, so
it is selected as first.1027:- det(insert_existing/4). 1028insert_existing(Existing, [], Versions, _Options) => 1029 maplist(existing_to_versions, Existing, Versions). 1030insert_existing(Existing, [Pack-Versions|T0], AllPackVersions, Options), 1031 select(Installed, Existing, Existing2), 1032 Installed.pack == Pack => 1033 can_upgrade(Installed, Versions, Installed2), 1034 insert_existing_(Installed2, Versions, AllVersions, Options), 1035 AllPackVersions = [Pack-AllVersions|T], 1036 insert_existing(Existing2, T0, T, Options). 1037insert_existing(Existing, [H|T0], AllVersions, Options) => 1038 AllVersions = [H|T], 1039 insert_existing(Existing, T0, T, Options). 1040 1041existing_to_versions(Installed, Pack-[Version-[Installed]]) :- 1042 Pack = Installed.pack, 1043 Version = Installed.version. 1044 1045insert_existing_(Installed, Versions, AllVersions, Options) :- 1046 option(upgrade(true), Options), 1047 !, 1048 insert_existing_(Installed, Versions, AllVersions). 1049insert_existing_(Installed, Versions, AllVersions, _) :- 1050 AllVersions = [Installed.version-[Installed]|Versions]. 1051 1052insert_existing_(Installed, [H|T0], [H|T]) :- 1053 H = V0-_Infos, 1054 cmp_versions(>, V0, Installed.version), 1055 !, 1056 insert_existing_(Installed, T0, T). 1057insert_existing_(Installed, [H0|T], [H|T]) :- 1058 H0 = V0-Infos, 1059 V0 == Installed.version, 1060 !, 1061 H = V0-[Installed|Infos]. 1062insert_existing_(Installed, Versions, All) :- 1063 All = [Installed.version-[Installed]|Versions].
latest_version key to Installed if its version is older than
the latest available version.1070can_upgrade(Info, [Version-_|_], Info2) :- 1071 cmp_versions(>, Version, Info.version), 1072 !, 1073 Info2 = Info.put(latest_version, Version). 1074can_upgrade(Info, _, Info).
upgrade:true to elements of PlanA in Existing that are not the
same.1082mark_installed([], _, []). 1083mark_installed([Info|T], Existing, Plan) :- 1084 ( member(Installed, Existing), 1085 Installed.pack == Info.pack 1086 -> ( ( Installed.git == true 1087 -> Info.git == true, 1088 Installed.hash == Info.hash 1089 ; Version = Info.get(version) 1090 -> Installed.version == Version 1091 ) 1092 -> Plan = [Info.put(keep, true)|PlanT] % up-to-date 1093 ; Plan = [Info.put(upgrade, Installed)|PlanT] % needs upgrade 1094 ) 1095 ; Plan = [Info|PlanT] % new install 1096 ), 1097 mark_installed(T, Existing, PlanT).
1105select_version([], _, _) --> 1106 []. 1107select_version([Pack-PackOptions|More], Versions, Options) --> 1108 { memberchk(Pack-PackVersions, Versions), 1109 member(Version-Infos, PackVersions), 1110 compatible_version(Pack, Version, PackOptions), 1111 member(Info, Infos), 1112 pack_options_compatible_with_info(Info, PackOptions), 1113 pack_satisfies(Pack, Version, Info, Info2, PackOptions), 1114 all_downloads(PackVersions, Downloads) 1115 }, 1116 add_to_plan(Info2.put(_{version: Version, all_downloads:Downloads}), 1117 Versions, Options), 1118 select_version(More, Versions, Options). 1119select_version([Pack-_PackOptions|_More], _Versions, _Options) --> 1120 { existence_error(pack, Pack) }. % or warn and continue? 1121 1122all_downloads(PackVersions, AllDownloads) :- 1123 aggregate_all(sum(Downloads), 1124 ( member(_Version-Infos, PackVersions), 1125 member(Info, Infos), 1126 get_dict(downloads, Info, Downloads) 1127 ), 1128 AllDownloads). 1129 1130add_requirements([], _, _) --> 1131 []. 1132add_requirements([H|T], Versions, Options) --> 1133 { is_prolog_token(H), 1134 !, 1135 prolog_satisfies(H) 1136 }, 1137 add_requirements(T, Versions, Options). 1138add_requirements([H|T], Versions, Options) --> 1139 { member(Pack-PackVersions, Versions), 1140 member(Version-Infos, PackVersions), 1141 member(Info, Infos), 1142 ( Provides = @(Pack,Version) 1143 ; member(Provides, Info.get(provides)) 1144 ), 1145 satisfies_req(Provides, H), 1146 all_downloads(PackVersions, Downloads) 1147 }, 1148 add_to_plan(Info.put(_{version: Version, all_downloads:Downloads}), 1149 Versions, Options), 1150 add_requirements(T, Versions, Options).
1158add_to_plan(Info, _Versions, Options) --> 1159 { option(plan(Plan), Options), 1160 member_nonvar(Planned, Plan), 1161 Planned.pack == Info.pack, 1162 !, 1163 same_version(Planned, Info) % same pack, different version 1164 }. 1165add_to_plan(Info, _Versions, _Options) --> 1166 { member(Conflict, Info.get(conflicts)), 1167 is_prolog_token(Conflict), 1168 prolog_satisfies(Conflict), 1169 !, 1170 fail % incompatible with this Prolog 1171 }. 1172add_to_plan(Info, _Versions, Options) --> 1173 { option(plan(Plan), Options), 1174 member_nonvar(Planned, Plan), 1175 info_conflicts(Info, Planned), % Conflicts with a planned pack 1176 !, 1177 fail 1178 }. 1179add_to_plan(Info, Versions, Options) --> 1180 { select_option(dependency_for(Dep0), Options, Options1), 1181 Options2 = [dependency_for([Info.pack|Dep0])|Options1], 1182 ( Dep0 = [DepFor|_] 1183 -> add_dependency_for(DepFor, Info, Info1) 1184 ; Info1 = Info 1185 ) 1186 }, 1187 [Info1], 1188 add_requirements(Info.get(requires,[]), Versions, Options2). 1189 1190add_dependency_for(Pack, Info, Info) :- 1191 Old = Info.get(dependency_for), 1192 !, 1193 b_set_dict(dependency_for, Info, [Pack|Old]). 1194add_dependency_for(Pack, Info0, Info) :- 1195 Info = Info0.put(dependency_for, [Pack]). 1196 1197same_version(Info, Info) :- 1198 !. 1199same_version(Planned, Info) :- 1200 Hash = Planned.get(hash), 1201 Hash \== (-), 1202 !, 1203 Hash == Info.get(hash). 1204same_version(Planned, Info) :- 1205 Planned.get(version) == Info.get(version).
1211info_conflicts(Info, Planned) :- 1212 info_conflicts_(Info, Planned), 1213 !. 1214info_conflicts(Info, Planned) :- 1215 info_conflicts_(Planned, Info), 1216 !. 1217 1218info_conflicts_(Info, Planned) :- 1219 member(Conflict, Info.get(conflicts)), 1220 \+ is_prolog_token(Conflict), 1221 info_provides(Planned, Provides), 1222 satisfies_req(Provides, Conflict), 1223 !. 1224 1225info_provides(Info, Provides) :- 1226 ( Provides = Info.pack@Info.version 1227 ; member(Provides, Info.get(provides)) 1228 ).
1235pack_satisfies(_Pack, _Version, Info0, Info, Options) :- 1236 option(commit('HEAD'), Options), 1237 !, 1238 Info0.get(git) == true, 1239 Info = Info0.put(commit, 'HEAD'). 1240pack_satisfies(_Pack, _Version, Info, Info, Options) :- 1241 option(commit(Commit), Options), 1242 !, 1243 Commit == Info.get(hash). 1244pack_satisfies(Pack, Version, Info, Info, Options) :- 1245 option(version(ReqVersion), Options), 1246 !, 1247 satisfies_version(Pack, Version, ReqVersion). 1248pack_satisfies(_Pack, _Version, Info, Info, _Options).
1252satisfies_version(Pack, Version, ReqVersion) :-
1253 catch(require_version(pack(Pack), Version, ReqVersion),
1254 error(version_error(pack(Pack), Version, ReqVersion),_),
1255 fail).1261satisfies_req(Token, Token) => true. 1262satisfies_req(@(Token,_), Token) => true. 1263satisfies_req(@(Token,PrvVersion), Req), cmp(Req, Token, Cmp, ReqVersion) => 1264 cmp_versions(Cmp, PrvVersion, ReqVersion). 1265satisfies_req(_,_) => fail. 1266 1267cmp(Token < Version, Token, <, Version). 1268cmp(Token =< Version, Token, =<, Version). 1269cmp(Token = Version, Token, =, Version). 1270cmp(Token == Version, Token, ==, Version). 1271cmp(Token >= Version, Token, >=, Version). 1272cmp(Token > Version, Token, >, Version).
url(URL) option. This allows installing packages that are
not known to the server. In most cases, the URL will be a git URL or
the URL to download an archive. It can also be a file:// url to
install from a local archive.
The first clause deals with a wildcard URL. See pack_default_options/4, case (7).
1285:- det(pack_options_to_versions/2). 1286pack_options_to_versions(Pack-PackOptions, Pack-Versions) :- 1287 option(versions(Available), PackOptions), !, 1288 maplist(version_url_info(Pack, PackOptions), Available, Versions). 1289pack_options_to_versions(Pack-PackOptions, Pack-[Version-[Info]]) :- 1290 option(url(URL), PackOptions), 1291 findall(Prop, option_info_prop(PackOptions, Prop), Pairs), 1292 dict_create(Info, #, 1293 [ pack-Pack, 1294 url-URL 1295 | Pairs 1296 ]), 1297 Version = Info.get(version, '0.0.0'). 1298 1299version_url_info(Pack, PackOptions, Version-URL, Version-[Info]) :- 1300 findall(Prop, 1301 ( option_info_prop(PackOptions, Prop), 1302 Prop \= version-_ 1303 ), 1304 Pairs), 1305 dict_create(Info, #, 1306 [ pack-Pack, 1307 url-URL, 1308 version-Version 1309 | Pairs 1310 ]). 1311 1312option_info_prop(PackOptions, Prop-Value) :- 1313 option_info(Prop), 1314 Opt =.. [Prop,Value], 1315 option(Opt, PackOptions). 1316 1317option_info(git). 1318option_info(hash). 1319option_info(version). 1320option_info(branch). 1321option_info(link).
1328compatible_version(Pack, Version, PackOptions) :- 1329 option(version(ReqVersion), PackOptions), 1330 !, 1331 satisfies_version(Pack, Version, ReqVersion). 1332compatible_version(_, _, _).
1339pack_options_compatible_with_info(Info, PackOptions) :-
1340 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1341 dict_create(Dict, _, Pairs),
1342 Dict >:< Info.1352download_plan(_Targets, Plan, Plan, _Options) :- 1353 exclude(installed, Plan, []), 1354 !. 1355download_plan(Targets, Plan0, Plan, Options) :- 1356 confirm(download_plan(Plan0), yes, Options), 1357 maplist(download_from_info(Options), Plan0, Plan1), 1358 plan_unsatisfied_dependencies(Plan1, Deps), 1359 ( Deps == [] 1360 -> Plan = Plan1 1361 ; print_message(informational, pack(new_dependencies(Deps))), 1362 prolog_description(Properties), 1363 query_pack_server(versions(Deps, Properties), Result, []), 1364 ( Result = true(Versions) 1365 -> merge_installed(Plan1, Options, Existing), 1366 pack_resolve(Targets, Existing, Versions, Plan2, Options), 1367 !, 1368 download_plan(Targets, Plan2, Plan, Options) 1369 ; print_message(error, pack(query_failed(Result))), 1370 fail 1371 ) 1372 ).
installed_packs(Installed), so that the
recursive dependency resolution considers already installed packs
able to satisfy requirements. Packs in Plan shadow same
pack in Installed.1382merge_installed(Plan, Options, Existing) :- 1383 option(installed_packs(Installed), Options, []), 1384 exclude(in_plan(Plan), Installed, InstalledRest), 1385 append(Plan, InstalledRest, Existing). 1386 1387in_plan(Plan, Info) :- 1388 member(P, Plan), 1389 P.pack == Info.pack, 1390 !.
1397plan_unsatisfied_dependencies(Plan, Deps) :- 1398 phrase(plan_unsatisfied_dependencies(Plan, Plan), Deps). 1399 1400plan_unsatisfied_dependencies([], _) --> 1401 []. 1402plan_unsatisfied_dependencies([Info|Infos], Plan) --> 1403 { Deps = Info.get(requires) }, 1404 plan_unsatisfied_requirements(Deps, Plan), 1405 plan_unsatisfied_dependencies(Infos, Plan). 1406 1407plan_unsatisfied_requirements([], _) --> 1408 []. 1409plan_unsatisfied_requirements([H|T], Plan) --> 1410 { is_prolog_token(H), % Can this fail? 1411 prolog_satisfies(H) 1412 }, 1413 !, 1414 plan_unsatisfied_requirements(T, Plan). 1415plan_unsatisfied_requirements([H|T], Plan) --> 1416 { member(Info, Plan), 1417 ( ( Version = Info.get(version) 1418 -> Provides = @(Info.get(pack), Version) 1419 ; Provides = Info.get(pack) 1420 ) 1421 ; member(Provides, Info.get(provides)) 1422 ), 1423 satisfies_req(Provides, H) 1424 }, !, 1425 plan_unsatisfied_requirements(T, Plan). 1426plan_unsatisfied_requirements([H|T], Plan) --> 1427 [H], 1428 plan_unsatisfied_requirements(T, Plan).
1437build_plan(Plan, Ordered, Options) :-
1438 maplist(decide_autoload_pack(Options), Plan, Plan1),
1439 partition(needs_rebuild_from_info(Options), Plan1, ToBuild, NoBuild),
1440 maplist(attach_from_info(Options), NoBuild),
1441 ( ToBuild == []
1442 -> post_install_autoload(NoBuild),
1443 Ordered = []
1444 ; order_builds(ToBuild, Ordered),
1445 confirm(build_plan(Ordered), yes, Options),
1446 maplist(exec_plan_rebuild_step(Options), Ordered)
1447 ).
1453needs_rebuild_from_info(Options, Info) :-
1454 PackDir = Info.installed,
1455 is_foreign_pack(PackDir, _),
1456 \+ is_built(PackDir, Options).
1465is_built(PackDir, _Options) :-
1466 current_prolog_flag(arch, Arch),
1467 prolog_version_dotted(Version), % Major.Minor.Patch
1468 pack_status_dir(PackDir, built(Arch, Version, _)).
1475order_builds(ToBuild, Ordered) :-
1476 findall(Pack-Dependent, dep_edge(ToBuild, Pack, Dependent), Edges),
1477 maplist(get_dict(pack), ToBuild, Packs),
1478 vertices_edges_to_ugraph(Packs, Edges, Graph),
1479 ugraph_layers(Graph, Layers),
1480 append(Layers, PackNames),
1481 maplist(pack_info_from_name(ToBuild), PackNames, Ordered).1489dep_edge(Infos, Pack, Dependent) :- 1490 member(Info, Infos), 1491 Pack = Info.pack, 1492 member(Dependent, Info.get(dependency_for)), 1493 ( member(DepInfo, Infos), 1494 DepInfo.pack == Dependent 1495 -> true 1496 ). 1497 1498:- det(pack_info_from_name/3). 1499pack_info_from_name(Infos, Pack, Info) :- 1500 member(Info, Infos), 1501 Info.pack == Pack, 1502 !.
1508exec_plan_rebuild_step(Options, Info) :-
1509 print_message(informational, pack(build(Info.pack, Info.installed))),
1510 pack_post_install(Info, Options),
1511 attach_from_info(Options, Info).1517attach_from_info(_Options, Info) :- 1518 Info.get(keep) == true, 1519 !. 1520attach_from_info(Options, Info) :- 1521 ( option(pack_directory(_Parent), Options) 1522 -> pack_attach(Info.installed, [duplicate(replace)]) 1523 ; pack_attach(Info.installed, []) 1524 ).
1534download_from_info(Options, Info0, Info), option(dryrun(true), Options) => 1535 print_term(Info0, [nl(true)]), 1536 Info = Info0. 1537download_from_info(_Options, Info0, Info), installed(Info0) => 1538 Info = Info0. 1539download_from_info(_Options, Info0, Info), 1540 _{upgrade:OldInfo, git:true} :< Info0, 1541 is_git_directory(OldInfo.installed) => 1542 PackDir = OldInfo.installed, 1543 git_checkout_version(PackDir, [commit(Info0.hash)]), 1544 reload_info(PackDir, Info0, Info). 1545download_from_info(Options, Info0, Info), 1546 _{upgrade:OldInfo} :< Info0 => 1547 PackDir = OldInfo.installed, 1548 detach_pack(OldInfo.pack, PackDir), 1549 delete_directory_and_contents(PackDir), 1550 del_dict(upgrade, Info0, _, Info1), 1551 download_from_info(Options, Info1, Info). 1552download_from_info(Options, Info0, Info), 1553 _{url:URL, git:true} :< Info0, \+ have_git => 1554 git_archive_url(URL, Archive, Options), 1555 download_from_info([git_url(URL)|Options], 1556 Info0.put(_{ url:Archive, 1557 git:false, 1558 git_url:URL 1559 }), 1560 Info1), 1561 % restore the hash to register the download. 1562 ( Info1.get(version) == Info0.get(version), 1563 Hash = Info0.get(hash) 1564 -> Info = Info1.put(hash, Hash) 1565 ; Info = Info1 1566 ). 1567download_from_info(Options, Info0, Info), 1568 _{url:URL} :< Info0 => 1569 select_option(pack_directory(Dir), Options, Options1), 1570 select_option(version(_), Options1, Options2, _), 1571 download_info_extra(Info0, InstallOptions, Options2), 1572 pack_download_from_url(URL, Dir, Info0.pack, 1573 [ interactive(false), 1574 pack_dir(PackDir) 1575 | InstallOptions 1576 ]), 1577 reload_info(PackDir, Info0, Info). 1578 1579download_info_extra(Info, [git(true),commit(Hash)|Options], Options) :- 1580 Info.get(git) == true, 1581 !, 1582 Hash = Info.get(commit, 'HEAD'). 1583download_info_extra(Info, [link(true)|Options], Options) :- 1584 Info.get(link) == true, 1585 !. 1586download_info_extra(_, Options, Options). 1587 1588installed(Info) :- 1589 _ = Info.get(installed). 1590 1591detach_pack(Pack, PackDir) :- 1592 ( current_pack(Pack, PackDir) 1593 -> '$pack_detach'(Pack, PackDir) 1594 ; true 1595 ).
1604reload_info(_PackDir, Info, Info) :- 1605 _ = Info.get(installed), % we read it from the package 1606 !. 1607reload_info(PackDir, Info0, Info) :- 1608 local_pack_info(PackDir, Info1), 1609 Info = Info0.put(installed, PackDir) 1610 .put(downloaded, Info0.url) 1611 .put(Info1).
1618work_done(_, _, _, _, Options), 1619 option(silent(true), Options) => 1620 true. 1621work_done(Targets, Plan, Plan, [], _Options) => 1622 convlist(can_upgrade_target(Plan), Targets, CanUpgrade), 1623 ( CanUpgrade == [] 1624 -> pairs_keys(Targets, Packs), 1625 print_message(informational, pack(up_to_date(Packs))) 1626 ; print_message(informational, pack(installed_can_upgrade(CanUpgrade))) 1627 ). 1628work_done(_, _, _, _, _) => 1629 true. 1630 1631can_upgrade_target(Plan, Pack-_, Info) => 1632 member(Info, Plan), 1633 Info.pack == Pack, 1634 !, 1635 _ = Info.get(latest_version).
1642local_packs(Dir, Packs) :- 1643 findall(Pack, pack_in_subdir(Dir, Pack), Packs). 1644 1645pack_in_subdir(Dir, Info) :- 1646 directory_member(Dir, PackDir, 1647 [ file_type(directory), 1648 hidden(false) 1649 ]), 1650 local_pack_info(PackDir, Info). 1651 1652local_pack_info(PackDir, 1653 #{ pack: Pack, 1654 version: Version, 1655 title: Title, 1656 hash: Hash, 1657 url: URL, 1658 git: IsGit, 1659 requires: Requires, 1660 provides: Provides, 1661 conflicts: Conflicts, 1662 installed: PackDir 1663 }) :- 1664 directory_file_path(PackDir, 'pack.pl', MetaFile), 1665 exists_file(MetaFile), 1666 file_base_name(PackDir, DirName), 1667 findall(Term, pack_dir_info(PackDir, _, Term), Info), 1668 option(pack(Pack), Info, DirName), 1669 option(title(Title), Info, '<no title>'), 1670 option(version(Version), Info, '<no version>'), 1671 option(download(URL), Info, '<no download url>'), 1672 findall(Req, member(requires(Req), Info), Requires), 1673 findall(Prv, member(provides(Prv), Info), Provides), 1674 findall(Cfl, member(conflicts(Cfl), Info), Conflicts), 1675 ( have_git, 1676 is_git_directory(PackDir) 1677 -> git_hash(Hash, [directory(PackDir)]), 1678 IsGit = true 1679 ; Hash = '-', 1680 IsGit = false 1681 ). 1682 1683 1684 /******************************* 1685 * PROLOG VERSIONS * 1686 *******************************/
prolog(Dialect, Version)1697prolog_description([prolog(swi(Version))]) :- 1698 prolog_version(Version). 1699 1700prolog_version(Version) :- 1701 current_prolog_flag(version_git, Version), 1702 !. 1703prolog_version(Version) :- 1704 prolog_version_dotted(Version). 1705 1706prolog_version_dotted(Version) :- 1707 current_prolog_flag(version_data, swi(Major, Minor, Patch, _)), 1708 VNumbers = [Major, Minor, Patch], 1709 atomic_list_concat(VNumbers, '.', Version).
1716is_prolog_token(Token), cmp(Token, prolog, _Cmp, _Version) => true. 1717is_prolog_token(prolog:Feature), atom(Feature) => true. 1718is_prolog_token(prolog:Feature), flag_value_feature(Feature, _Flag, _Value) => 1719 true. 1720is_prolog_token(_) => fail.
requires(Token) terms for
library(Lib)1735prolog_satisfies(Token), cmp(Token, prolog, Cmp, ReqVersion) => 1736 prolog_version(CurrentVersion), 1737 cmp_versions(Cmp, CurrentVersion, ReqVersion). 1738prolog_satisfies(prolog:library(Lib)), atom(Lib) => 1739 exists_source(library(Lib)). 1740prolog_satisfies(prolog:Feature), atom(Feature) => 1741 current_prolog_flag(Feature, true). 1742prolog_satisfies(prolog:Feature), flag_value_feature(Feature, Flag, Value) => 1743 current_prolog_flag(Flag, Value). 1744 1745flag_value_feature(Feature, Flag, Value) :- 1746 compound(Feature), 1747 compound_name_arguments(Feature, Flag, [Value]), 1748 atom(Flag). 1749 1750 1751 /******************************* 1752 * INFO * 1753 *******************************/
Requires library(archive), which is lazily loaded when needed.
1767:- if(exists_source(library(archive))). 1768ensure_loaded_archive :- 1769 current_predicate(archive_open/3), 1770 !. 1771ensure_loaded_archive :- 1772 use_module(library(archive)). 1773 1774pack_archive_info(Archive, Pack, [archive_size(Bytes)|Info], Strip) :- 1775 ensure_loaded_archive, 1776 size_file(Archive, Bytes), 1777 setup_call_cleanup( 1778 archive_open(Archive, Handle, []), 1779 ( repeat, 1780 ( archive_next_header(Handle, InfoFile) 1781 -> true 1782 ; !, fail 1783 ) 1784 ), 1785 archive_close(Handle)), 1786 file_base_name(InfoFile, 'pack.pl'), 1787 atom_concat(Prefix, 'pack.pl', InfoFile), 1788 strip_option(Prefix, Pack, Strip), 1789 setup_call_cleanup( 1790 archive_open_entry(Handle, Stream), 1791 read_stream_to_terms(Stream, Info), 1792 close(Stream)), 1793 !, 1794 must_be(ground, Info), 1795 maplist(valid_term(pack_info_term), Info). 1796:- else. 1797pack_archive_info(_, _, _, _) :- 1798 existence_error(library, archive). 1799:- endif. 1800pack_archive_info(_, _, _, _) :- 1801 existence_error(pack_file, 'pack.pl'). 1802 1803strip_option('', _, []) :- !. 1804strip_option('./', _, []) :- !. 1805strip_option(Prefix, Pack, [remove_prefix(Prefix)]) :- 1806 atom_concat(PrefixDir, /, Prefix), 1807 file_base_name(PrefixDir, Base), 1808 ( Base == Pack 1809 -> true 1810 ; pack_version_file(Pack, _, Base) 1811 -> true 1812 ; \+ sub_atom(PrefixDir, _, _, _, /) 1813 ). 1814 1815read_stream_to_terms(Stream, Terms) :- 1816 read(Stream, Term0), 1817 read_stream_to_terms(Term0, Stream, Terms). 1818 1819read_stream_to_terms(end_of_file, _, []) :- !. 1820read_stream_to_terms(Term0, Stream, [Term0|Terms]) :- 1821 read(Stream, Term1), 1822 read_stream_to_terms(Term1, Stream, Terms).
1830pack_git_info(GitDir, Hash, [git(true), installed_size(Bytes)|Info]) :- 1831 exists_directory(GitDir), 1832 !, 1833 git_ls_tree(Entries, [directory(GitDir)]), 1834 git_hash(Hash, [directory(GitDir)]), 1835 maplist(arg(4), Entries, Sizes), 1836 sum_list(Sizes, Bytes), 1837 dir_metadata(GitDir, Info). 1838 1839dir_metadata(GitDir, Info) :- 1840 directory_file_path(GitDir, 'pack.pl', InfoFile), 1841 read_file_to_terms(InfoFile, Info, [encoding(utf8)]), 1842 maplist(valid_term(pack_info_term), Info).
1848download_file_sanity_check(Archive, Pack, Info) :- 1849 info_field(name(PackName), Info), 1850 info_field(version(PackVersion), Info), 1851 pack_version_file(PackFile, FileVersion, Archive), 1852 must_match([Pack, PackName, PackFile], name), 1853 must_match([PackVersion, FileVersion], version). 1854 1855info_field(Field, Info) :- 1856 memberchk(Field, Info), 1857 ground(Field), 1858 !. 1859info_field(Field, _Info) :- 1860 functor(Field, FieldName, _), 1861 print_message(error, pack(missing(FieldName))), 1862 fail. 1863 1864must_match(Values, _Field) :- 1865 sort(Values, [_]), 1866 !. 1867must_match(Values, Field) :- 1868 print_message(error, pack(conflict(Field, Values))), 1869 fail. 1870 1871 1872 /******************************* 1873 * INSTALLATION * 1874 *******************************/
1888prepare_pack_dir(Dir, Options) :- 1889 exists_directory(Dir), 1890 !, 1891 ( empty_directory(Dir) 1892 -> true 1893 ; remove_existing_pack(Dir, Options) 1894 -> make_directory(Dir) 1895 ). 1896prepare_pack_dir(Dir, _) :- 1897 ( read_link(Dir, _, _) 1898 ; access_file(Dir, exist) 1899 ), 1900 !, 1901 delete_file(Dir), 1902 make_directory(Dir). 1903prepare_pack_dir(Dir, _) :- 1904 make_directory(Dir).
1910empty_directory(Dir) :- 1911 \+ ( directory_files(Dir, Entries), 1912 member(Entry, Entries), 1913 \+ special(Entry) 1914 ). 1915 1916special(.). 1917special(..).
upgrade(true) is present. This is used to remove an old installation
before unpacking a new archive, copy or link a directory with the
new contents.1926remove_existing_pack(PackDir, Options) :- 1927 exists_directory(PackDir), 1928 !, 1929 ( ( option(upgrade(true), Options) 1930 ; confirm(remove_existing_pack(PackDir), yes, Options) 1931 ) 1932 -> delete_directory_and_contents(PackDir) 1933 ; print_message(error, pack(directory_exists(PackDir))), 1934 fail 1935 ). 1936remove_existing_pack(_, _).
1952pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1953 option(git(true), Options), 1954 !, 1955 directory_file_path(PackTopDir, Pack, PackDir), 1956 prepare_pack_dir(PackDir, Options), 1957 ( option(branch(Branch), Options) 1958 -> Extra = ['--branch', Branch] 1959 ; Extra = [] 1960 ), 1961 run_process(path(git), [clone, URL, PackDir|Extra], []), 1962 git_checkout_version(PackDir, [update(false)|Options]), 1963 option(pack_dir(PackDir), Options, _). 1964pack_download_from_url(URL0, PackTopDir, Pack, Options) :- 1965 download_url(URL0), 1966 !, 1967 hsts(URL0, URL, Options), 1968 directory_file_path(PackTopDir, Pack, PackDir), 1969 prepare_pack_dir(PackDir, Options), 1970 pack_download_dir(PackTopDir, DownLoadDir), 1971 download_file(URL, Pack, DownloadBase, Options), 1972 directory_file_path(DownLoadDir, DownloadBase, DownloadFile), 1973 ( option(insecure(true), Options, false) 1974 -> TLSOptions = [cert_verify_hook(ssl_verify)] 1975 ; TLSOptions = [] 1976 ), 1977 print_message(informational, pack(download(begin, Pack, URL, DownloadFile))), 1978 setup_call_cleanup( 1979 http_open(URL, In, TLSOptions), 1980 setup_call_cleanup( 1981 open(DownloadFile, write, Out, [type(binary)]), 1982 copy_stream_data(In, Out), 1983 close(Out)), 1984 close(In)), 1985 print_message(informational, pack(download(end, Pack, URL, DownloadFile))), 1986 pack_archive_info(DownloadFile, Pack, Info, _), 1987 ( option(git_url(GitURL), Options) 1988 -> Origin = GitURL % implicit download from git. 1989 ; download_file_sanity_check(DownloadFile, Pack, Info), 1990 Origin = URL 1991 ), 1992 pack_unpack_from_local(DownloadFile, PackTopDir, Pack, PackDir, Options), 1993 pack_assert(PackDir, archive(DownloadFile, Origin)), 1994 option(pack_dir(PackDir), Options, _). 1995pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1996 local_uri_file_name(URL, File), 1997 !, 1998 pack_unpack_from_local(File, PackTopDir, Pack, PackDir, Options), 1999 pack_assert(PackDir, archive(File, URL)), 2000 option(pack_dir(PackDir), Options, _). 2001pack_download_from_url(URL, _PackTopDir, _Pack, _Options) :- 2002 domain_error(url, URL).
'HEAD'. If 'HEAD', get the HEAD of the
explicit (option branch(Branch)), current or default branch. If
the commit is a hash and it is the tip of a branch, checkout
this branch. Else simply checkout the hash.commit('HEAD').2026git_checkout_version(PackDir, Options) :- 2027 option(commit('HEAD'), Options), 2028 option(branch(Branch), Options), 2029 !, 2030 git_ensure_on_branch(PackDir, Branch), 2031 run_process(path(git), ['-C', PackDir, pull], []). 2032git_checkout_version(PackDir, Options) :- 2033 option(commit('HEAD'), Options), 2034 git_current_branch(_, [directory(PackDir)]), 2035 !, 2036 run_process(path(git), ['-C', PackDir, pull], []). 2037git_checkout_version(PackDir, Options) :- 2038 option(commit('HEAD'), Options), 2039 !, 2040 git_default_branch(Branch, [directory(PackDir)]), 2041 git_ensure_on_branch(PackDir, Branch), 2042 run_process(path(git), ['-C', PackDir, pull], []). 2043git_checkout_version(PackDir, Options) :- 2044 option(commit(Hash), Options), 2045 run_process(path(git), ['-C', PackDir, fetch], []), 2046 git_branches(Branches, [contains(Hash), directory(PackDir)]), 2047 git_process_output(['-C', PackDir, 'rev-parse' | Branches], 2048 read_lines_to_atoms(Commits), 2049 []), 2050 nth1(I, Commits, Hash), 2051 nth1(I, Branches, Branch), 2052 !, 2053 git_ensure_on_branch(PackDir, Branch). 2054git_checkout_version(PackDir, Options) :- 2055 option(commit(Hash), Options), 2056 !, 2057 run_process(path(git), ['-C', PackDir, checkout, '--quiet', Hash], []). 2058git_checkout_version(PackDir, Options) :- 2059 option(version(Version), Options), 2060 !, 2061 git_tags(Tags, [directory(PackDir)]), 2062 ( memberchk(Version, Tags) 2063 -> Tag = Version 2064 ; member(Tag, Tags), 2065 sub_atom(Tag, B, _, 0, Version), 2066 sub_atom(Tag, 0, B, _, Prefix), 2067 version_prefix(Prefix) 2068 -> true 2069 ; existence_error(version_tag, Version) 2070 ), 2071 run_process(path(git), ['-C', PackDir, checkout, Tag], []). 2072git_checkout_version(_PackDir, Options) :- 2073 option(fresh(true), Options), 2074 !. 2075git_checkout_version(PackDir, _Options) :- 2076 git_current_branch(_, [directory(PackDir)]), 2077 !, 2078 run_process(path(git), ['-C', PackDir, pull], []). 2079git_checkout_version(PackDir, _Options) :- 2080 git_default_branch(Branch, [directory(PackDir)]), 2081 git_ensure_on_branch(PackDir, Branch), 2082 run_process(path(git), ['-C', PackDir, pull], []).
2088git_ensure_on_branch(PackDir, Branch) :- 2089 git_current_branch(Branch, [directory(PackDir)]), 2090 !. 2091git_ensure_on_branch(PackDir, Branch) :- 2092 run_process(path(git), ['-C', PackDir, checkout, Branch], []). 2093 2094read_lines_to_atoms(Atoms, In) :- 2095 read_line_to_string(In, Line), 2096 ( Line == end_of_file 2097 -> Atoms = [] 2098 ; atom_string(Atom, Line), 2099 Atoms = [Atom|T], 2100 read_lines_to_atoms(T, In) 2101 ). 2102 2103version_prefix(Prefix) :- 2104 atom_codes(Prefix, Codes), 2105 phrase(version_prefix, Codes). 2106 2107version_prefix --> 2108 [C], 2109 { code_type(C, alpha) }, 2110 !, 2111 version_prefix. 2112version_prefix --> 2113 "-". 2114version_prefix --> 2115 "_". 2116version_prefix --> 2117 "".
2124download_file(URL, Pack, File, Options) :- 2125 option(version(Version), Options), 2126 !, 2127 file_name_extension(_, Ext, URL), 2128 format(atom(File), '~w-~w.~w', [Pack, Version, Ext]). 2129download_file(URL, Pack, File, _) :- 2130 file_base_name(URL,Basename), 2131 no_int_file_name_extension(Tag,Ext,Basename), 2132 tag_version(Tag,Version), 2133 !, 2134 format(atom(File0), '~w-~w', [Pack, Version]), 2135 file_name_extension(File0, Ext, File). 2136download_file(URL, _, File, _) :- 2137 file_base_name(URL, File).
2145:- public pack_url_file/2. 2146pack_url_file(URL, FileID) :- 2147 github_release_url(URL, Pack, Version), 2148 !, 2149 download_file(URL, Pack, FileID, [version(Version)]). 2150pack_url_file(URL, FileID) :- 2151 file_base_name(URL, FileID). 2152 2153% ssl_verify(+SSL, +ProblemCert, +AllCerts, +FirstCert, +Error) 2154% 2155% Used if insecure(true) is given to pack_install/2. Accepts any 2156% certificate. 2157 2158:- public ssl_verify/5. 2159ssl_verify(_SSL, 2160 _ProblemCertificate, _AllCertificates, _FirstCertificate, 2161 _Error). 2162 2163pack_download_dir(PackTopDir, DownLoadDir) :- 2164 directory_file_path(PackTopDir, 'Downloads', DownLoadDir), 2165 ( exists_directory(DownLoadDir) 2166 -> true 2167 ; make_directory(DownLoadDir) 2168 ), 2169 ( access_file(DownLoadDir, write) 2170 -> true 2171 ; permission_error(write, directory, DownLoadDir) 2172 ).
ftp:// are also download URLs, but we cannot download
from them.2180download_url(URL) :- 2181 url_scheme(URL, Scheme), 2182 download_scheme(Scheme). 2183 2184url_scheme(URL, Scheme) :- 2185 atom(URL), 2186 uri_components(URL, Components), 2187 uri_data(scheme, Components, Scheme0), 2188 atom(Scheme0), 2189 Scheme = Scheme0. 2190 2191download_scheme(http). 2192download_scheme(https).
insecure(true), which may also be used to disable TLS
certificate checking. Note that the pack integrity is still
protected by its SHA1 hash.2203hsts(URL0, URL, Options) :- 2204 option(insecure(true), Options, false), 2205 !, 2206 URL = URL0. 2207hsts(URL0, URL, _Options) :- 2208 url_scheme(URL0, http), 2209 !, 2210 uri_edit(scheme(https), URL0, URL). 2211hsts(URL, URL, _Options).
2222pack_post_install(Info, Options) :-
2223 Pack = Info.pack,
2224 PackDir = Info.installed,
2225 post_install_foreign(Pack, PackDir, Options),
2226 post_install_autoload(Info),
2227 pack_attach(PackDir, [duplicate(warning)]).2235pack_rebuild :- 2236 forall(current_pack(Pack), 2237 ( print_message(informational, pack(rebuild(Pack))), 2238 pack_rebuild(Pack) 2239 )). 2240 2241pack_rebuild(Pack) :- 2242 current_pack(Pack, PackDir), 2243 !, 2244 post_install_foreign(Pack, PackDir, [rebuild(true)]), 2245 pack_attach(PackDir, [duplicate(replace)]). 2246pack_rebuild(Pack) :- 2247 unattached_pack(Pack, PackDir), 2248 !, 2249 post_install_foreign(Pack, PackDir, [rebuild(true)]), 2250 pack_attach(PackDir, [duplicate(replace)]). 2251pack_rebuild(Pack) :- 2252 existence_error(pack, Pack). 2253 2254unattached_pack(Pack, BaseDir) :- 2255 directory_file_path(Pack, 'pack.pl', PackFile), 2256 absolute_file_name(pack(PackFile), PackPath, 2257 [ access(read), 2258 file_errors(fail) 2259 ]), 2260 file_directory_name(PackPath, BaseDir).
2276post_install_foreign(Pack, PackDir, Options) :- 2277 is_foreign_pack(PackDir, _), 2278 !, 2279 ( pack_info_term(PackDir, pack_version(Version)) 2280 -> true 2281 ; Version = 1 2282 ), 2283 option(rebuild(Rebuild), Options, if_absent), 2284 current_prolog_flag(arch, Arch), 2285 prolog_version_dotted(PrologVersion), 2286 ( Rebuild == if_absent, 2287 foreign_present(PackDir, Arch) 2288 -> print_message(informational, pack(kept_foreign(Pack, Arch))), 2289 ( pack_status_dir(PackDir, built(Arch, _, _)) 2290 -> true 2291 ; pack_assert(PackDir, built(Arch, PrologVersion, downloaded)) 2292 ) 2293 ; BuildSteps0 = [[dependencies], [configure], build, install, [test]], 2294 ( Rebuild == true 2295 -> BuildSteps1 = [distclean|BuildSteps0] 2296 ; BuildSteps1 = BuildSteps0 2297 ), 2298 ( option(test(false), Options) 2299 -> delete(BuildSteps1, [test], BuildSteps2) 2300 ; BuildSteps2 = BuildSteps1 2301 ), 2302 ( option(clean(true), Options) 2303 -> append(BuildSteps2, [[clean]], BuildSteps) 2304 ; BuildSteps = BuildSteps2 2305 ), 2306 build_steps(BuildSteps, PackDir, [pack_version(Version)|Options]), 2307 pack_assert(PackDir, built(Arch, PrologVersion, built)) 2308 ). 2309post_install_foreign(_, _, _).
lib directory for
the current architecture.
2320foreign_present(PackDir, Arch) :-
2321 atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
2322 exists_directory(ForeignBaseDir),
2323 !,
2324 atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
2325 exists_directory(ForeignDir),
2326 current_prolog_flag(shared_object_extension, Ext),
2327 atomic_list_concat([ForeignDir, '/*.', Ext], Pattern),
2328 expand_file_name(Pattern, Files),
2329 Files \== [].2336is_foreign_pack(PackDir, Type) :- 2337 foreign_file(File, Type), 2338 directory_file_path(PackDir, File, Path), 2339 exists_file(Path). 2340 2341foreign_file('CMakeLists.txt', cmake). 2342foreign_file('configure', configure). 2343foreign_file('configure.in', autoconf). 2344foreign_file('configure.ac', autoconf). 2345foreign_file('Makefile.am', automake). 2346foreign_file('Makefile', make). 2347foreign_file('makefile', make). 2348foreign_file('conanfile.txt', conan). 2349foreign_file('conanfile.py', conan). 2350 2351 2352 /******************************* 2353 * AUTOLOAD * 2354 *******************************/
2360post_install_autoload(List), is_list(List) => 2361 maplist(post_install_autoload, List). 2362post_install_autoload(Info), 2363 _{installed:PackDir, autoload:true} :< Info => 2364 directory_file_path(PackDir, prolog, PrologLibDir), 2365 make_library_index(PrologLibDir). 2366post_install_autoload(Info) => 2367 directory_file_path(Info.installed, 'prolog/INDEX.pl', IndexFile), 2368 ( exists_file(IndexFile) 2369 -> E = error(_,_), 2370 print_message(warning, pack(delete_autoload_index(Info.pack, IndexFile))), 2371 catch(delete_file(IndexFile), E, 2372 print_message(warning, E)) 2373 ; true 2374 ).
2381decide_autoload_pack(Options, Info0, Info) :- 2382 is_autoload_pack(Info0.pack, Info0.installed, Options), 2383 !, 2384 Info = Info0.put(autoload, true). 2385decide_autoload_pack(_, Info, Info). 2386 2387is_autoload_pack(_Pack, _PackDir, Options) :- 2388 option(autoload(true), Options), 2389 !. 2390is_autoload_pack(Pack, PackDir, Options) :- 2391 pack_info_term(PackDir, autoload(true)), 2392 confirm(autoload(Pack), no, Options). 2393 2394 2395 /******************************* 2396 * UPGRADE * 2397 *******************************/
pack_install(Pack, [upgrade(true)]).2403pack_upgrade(Pack) :- 2404 pack_install(Pack, [upgrade(true)]). 2405 2406 2407 /******************************* 2408 * REMOVE * 2409 *******************************/
true delete dependencies without asking.2422pack_remove(Pack) :- 2423 pack_remove(Pack, []). 2424 2425pack_remove(Pack, Options) :- 2426 option(dependencies(false), Options), 2427 !, 2428 pack_remove_forced(Pack). 2429pack_remove(Pack, Options) :- 2430 ( dependents(Pack, Deps) 2431 -> ( option(dependencies(true), Options) 2432 -> true 2433 ; confirm_remove(Pack, Deps, Delete, Options) 2434 ), 2435 forall(member(P, Delete), pack_remove_forced(P)) 2436 ; pack_remove_forced(Pack) 2437 ). 2438 2439pack_remove_forced(Pack) :- 2440 catch('$pack_detach'(Pack, BaseDir), 2441 error(existence_error(pack, Pack), _), 2442 fail), 2443 !, 2444 ( read_link(BaseDir, _, Target) 2445 -> What = link(Target) 2446 ; What = directory 2447 ), 2448 print_message(informational, pack(remove(What, BaseDir))), 2449 delete_directory_and_contents(BaseDir). 2450pack_remove_forced(Pack) :- 2451 unattached_pack(Pack, BaseDir), 2452 !, 2453 delete_directory_and_contents(BaseDir). 2454pack_remove_forced(Pack) :- 2455 print_message(informational, error(existence_error(pack, Pack),_)). 2456 2457confirm_remove(Pack, Deps, Delete, Options) :- 2458 print_message(warning, pack(depends(Pack, Deps))), 2459 menu(pack(resolve_remove), 2460 [ [Pack] = remove_only(Pack), 2461 [Pack|Deps] = remove_deps(Pack, Deps), 2462 [] = cancel 2463 ], [], Delete, Options), 2464 Delete \== []. 2465 2466 2467 /******************************* 2468 * PUBLISH * 2469 *******************************/
?- pack_publish('.', []).
Alternatively, an archive file has been uploaded to a public location. In this scenario we can publish the pack using
?- pack_publish(URL, [])
In both scenarios, pack_publish/2 by default creates an isolated environment and installs the package in this directory from the public URL. On success it triggers the pack server to register the URL as a new pack or a new release of a pack.
Packs may also be published using the app pack, e.g.
swipl pack publish .
Options:
true, and Spec is a git managed directory, install using
the remote repo.git tag -s <tag>.git tag -f <tag>.false (default true), perform the installation, but do
not upload to the server. This can be used for testing.true (default), install and build all packages in an
isolated package directory. If false, use other packages
installed for the environment. The latter may be used to
speedup debugging.true (default), clean the destination directory first2522pack_publish(Dir, Options) :- 2523 \+ download_url(Dir), 2524 is_git_directory(Dir), !, 2525 pack_git_info(Dir, _Hash, Metadata), 2526 prepare_repository(Dir, Metadata, Options), 2527 ( memberchk(download(URL), Metadata), 2528 git_url(URL, _) 2529 -> true 2530 ; option(remote(Remote), Options, origin), 2531 git_remote_url(Remote, RemoteURL, [directory(Dir)]), 2532 git_to_https_url(RemoteURL, URL) 2533 ), 2534 memberchk(version(Version), Metadata), 2535 pack_publish_(URL, 2536 [ version(Version) 2537 | Options 2538 ]). 2539pack_publish(Spec, Options) :- 2540 pack_publish_(Spec, Options). 2541 2542pack_publish_(Spec, Options) :- 2543 pack_default_options(Spec, Pack, Options, DefOptions), 2544 option(url(URL), DefOptions), 2545 valid_publish_url(URL, Options), 2546 prepare_build_location(Pack, Dir, Clean, Options), 2547 ( option(register(false), Options) 2548 -> InstallOptions = DefOptions 2549 ; InstallOptions = [publish(Pack)|DefOptions] 2550 ), 2551 call_cleanup(pack_install(Pack, 2552 [ pack(Pack) 2553 | InstallOptions 2554 ]), 2555 cleanup_publish(Clean, Dir)). 2556 2557cleanup_publish(true, Dir) :- 2558 !, 2559 delete_directory_and_contents(Dir). 2560cleanup_publish(_, _). 2561 2562valid_publish_url(URL, Options) :- 2563 option(register(Register), Options, true), 2564 ( Register == false 2565 -> true 2566 ; download_url(URL) 2567 -> true 2568 ; permission_error(publish, pack, URL) 2569 ). 2570 2571prepare_build_location(Pack, Dir, Clean, Options) :- 2572 ( option(pack_directory(Dir), Options) 2573 -> ensure_directory(Dir), 2574 ( option(clean(true), Options, true) 2575 -> delete_directory_contents(Dir) 2576 ; true 2577 ) 2578 ; tmp_file(pack, Dir), 2579 make_directory(Dir), 2580 Clean = true 2581 ), 2582 ( option(isolated(false), Options) 2583 -> detach_pack(Pack, _), 2584 attach_packs(Dir, [search(first)]) 2585 ; attach_packs(Dir, [replace(true)]) 2586 ).
register(false) is provided, this is
a test run and therefore we do not need this. Otherwise we demand
the working directory to be clean, we tag the current commit and
push the current branch.2597prepare_repository(_Dir, _Metadata, Options) :- 2598 option(register(false), Options), 2599 !. 2600prepare_repository(Dir, Metadata, Options) :- 2601 git_dir_must_be_clean(Dir), 2602 git_must_be_on_default_branch(Dir, Options), 2603 tag_git_dir(Dir, Metadata, Action, Options), 2604 confirm(git_push, yes, Options), 2605 run_process(path(git), ['-C', file(Dir), push ], []), 2606 ( Action = push_tag(Tag) 2607 -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], []) 2608 ; true 2609 ). 2610 2611git_dir_must_be_clean(Dir) :- 2612 git_describe(Description, [directory(Dir)]), 2613 ( sub_atom(Description, _, _, 0, '-DIRTY') 2614 -> print_message(error, pack(git_not_clean(Dir))), 2615 fail 2616 ; true 2617 ). 2618 2619git_must_be_on_default_branch(Dir, Options) :- 2620 ( option(branch(Default), Options) 2621 -> true 2622 ; git_default_branch(Default, [directory(Dir)]) 2623 ), 2624 git_current_branch(Current, [directory(Dir)]), 2625 ( Default == Current 2626 -> true 2627 ; print_message(error, 2628 pack(git_branch_not_default(Dir, Default, Current))), 2629 fail 2630 ).
2639tag_git_dir(Dir, Metadata, Action, Options) :- 2640 memberchk(version(Version), Metadata), 2641 atom_concat('V', Version, Tag), 2642 git_tags(Tags, [directory(Dir)]), 2643 ( memberchk(Tag, Tags) 2644 -> git_tag_is_consistent(Dir, Tag, Action, Options) 2645 ; format(string(Message), 'Release ~w', [Version]), 2646 findall(Opt, git_tag_option(Opt, Options), Argv, 2647 [ '-m', Message, Tag ]), 2648 confirm(git_tag(Tag), yes, Options), 2649 run_process(path(git), ['-C', file(Dir), tag | Argv ], []), 2650 Action = push_tag(Tag) 2651 ). 2652 2653git_tag_option('-s', Options) :- option(sign(true), Options, true). 2654git_tag_option('-f', Options) :- option(force(true), Options, true). 2655 2656git_tag_is_consistent(Dir, Tag, Action, Options) :- 2657 format(atom(TagRef), 'refs/tags/~w', [Tag]), 2658 format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]), 2659 option(remote(Remote), Options, origin), 2660 git_ls_remote(Dir, LocalTags, [tags(true)]), 2661 memberchk(CommitHash-CommitRef, LocalTags), 2662 ( git_hash(CommitHash, [directory(Dir)]) 2663 -> true 2664 ; print_message(error, pack(git_release_tag_not_at_head(Tag))), 2665 fail 2666 ), 2667 memberchk(TagHash-TagRef, LocalTags), 2668 git_ls_remote(Remote, RemoteTags, [tags(true)]), 2669 ( memberchk(RemoteCommitHash-CommitRef, RemoteTags), 2670 memberchk(RemoteTagHash-TagRef, RemoteTags) 2671 -> ( RemoteCommitHash == CommitHash, 2672 RemoteTagHash == TagHash 2673 -> Action = none 2674 ; print_message(error, pack(git_tag_out_of_sync(Tag))), 2675 fail 2676 ) 2677 ; Action = push_tag(Tag) 2678 ).
2686git_to_https_url(URL, URL) :- 2687 download_url(URL), 2688 !. 2689git_to_https_url(GitURL, URL) :- 2690 atom_concat('git@github.com:', Repo, GitURL), 2691 !, 2692 atom_concat('https://github.com/', Repo, URL). 2693git_to_https_url(GitURL, _) :- 2694 print_message(error, pack(git_no_https(GitURL))), 2695 fail. 2696 2697 2698 /******************************* 2699 * PROPERTIES * 2700 *******************************/
README file (if present)TODO file (if present)2723pack_property(Pack, Property) :- 2724 findall(Pack-Property, pack_property_(Pack, Property), List), 2725 member(Pack-Property, List). % make det if applicable 2726 2727pack_property_(Pack, Property) :- 2728 pack_info(Pack, _, Property). 2729pack_property_(Pack, Property) :- 2730 \+ \+ info_file(Property, _), 2731 '$pack':pack(Pack, BaseDir), 2732 access_file(BaseDir, read), 2733 directory_files(BaseDir, Files), 2734 member(File, Files), 2735 info_file(Property, Pattern), 2736 downcase_atom(File, Pattern), 2737 directory_file_path(BaseDir, File, InfoFile), 2738 arg(1, Property, InfoFile). 2739 2740info_file(readme(_), 'readme.txt'). 2741info_file(readme(_), 'readme'). 2742info_file(todo(_), 'todo.txt'). 2743info_file(todo(_), 'todo'). 2744 2745 2746 /******************************* 2747 * VERSION LOGIC * 2748 *******************************/
mypack-1.5.2757pack_version_file(Pack, Version, GitHubRelease) :- 2758 atomic(GitHubRelease), 2759 github_release_url(GitHubRelease, Pack, Version), 2760 !. 2761pack_version_file(Pack, Version, Path) :- 2762 atomic(Path), 2763 file_base_name(Path, File), 2764 no_int_file_name_extension(Base, _Ext, File), 2765 atom_codes(Base, Codes), 2766 ( phrase(pack_version(Pack, Version), Codes), 2767 safe_pack_name(Pack) 2768 -> true 2769 ). 2770 2771no_int_file_name_extension(Base, Ext, File) :- 2772 file_name_extension(Base0, Ext0, File), 2773 \+ atom_number(Ext0, _), 2774 !, 2775 Base = Base0, 2776 Ext = Ext0. 2777no_int_file_name_extension(File, '', File).
2784safe_pack_name(Name) :- 2785 atom_length(Name, Len), 2786 Len >= 3, % demand at least three length 2787 atom_codes(Name, Codes), 2788 maplist(safe_pack_char, Codes), 2789 !. 2790 2791safe_pack_char(C) :- between(0'a, 0'z, C), !. 2792safe_pack_char(C) :- between(0'A, 0'Z, C), !. 2793safe_pack_char(C) :- between(0'0, 0'9, C), !. 2794safe_pack_char(0'_).
2800pack_version(Pack, Version) --> 2801 string(Codes), "-", 2802 version(Parts), 2803 !, 2804 { atom_codes(Pack, Codes), 2805 atomic_list_concat(Parts, '.', Version) 2806 }. 2807 2808version([H|T]) --> 2809 version_part(H), 2810 ( "." 2811 -> version(T) 2812 ; {T=[]} 2813 ). 2814 2815version_part(*) --> "*", !. 2816version_part(Int) --> integer(Int). 2817 2818 2819 /******************************* 2820 * GIT LOGIC * 2821 *******************************/
git program. This could be simple, but Apple
decided to include a fake `/usr/bin/git` that triggers the Xcode
installation. So, if we find git at `/usr/bin/git` we should check
that Xcode is properly enabled. This is the case if xcode-select
-p points at an Xcode installation. Note that if we find git at
some other location, we assume it is installed by the user,
Macports, Homebrew or something else.2833have_git :- 2834 process_which(path(git), GIT), 2835 is_sane_git(GIT). 2836 2837:- if(current_prolog_flag(apple, true)). 2838sane_xcode_path --> 2839 "Xcode.app/Contents". 2840sane_xcode_path --> 2841 "CommandLineTools". 2842 2843is_sane_git('/usr/bin/git') :- 2844 !, 2845 process_which(path('xcode-select'), XSpath), 2846 catch(run_process(XSpath,['-p'],[output(Output),error(_)]), error(_,_), fail), 2847 once(phrase((string(_), sane_xcode_path), Output, _)). 2848:- endif. 2849is_sane_git(_).
2855git_url(URL, Pack) :- 2856 uri_components(URL, Components), 2857 uri_data(scheme, Components, Scheme), 2858 nonvar(Scheme), % must be full URL 2859 uri_data(path, Components, Path), 2860 ( Scheme == git 2861 -> true 2862 ; git_download_scheme(Scheme), 2863 file_name_extension(_, git, Path) 2864 ; git_download_scheme(Scheme), 2865 catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail) 2866 -> true 2867 ), 2868 file_base_name(Path, PackExt), 2869 ( file_name_extension(Pack, git, PackExt) 2870 -> true 2871 ; Pack = PackExt 2872 ), 2873 ( safe_pack_name(Pack) 2874 -> true 2875 ; domain_error(pack_name, Pack) 2876 ). 2877 2878git_download_scheme(http). 2879git_download_scheme(https).
https:/github.com/<owner>/<pack>/archive/[vV]?<version>.zip'
2888github_release_url(URL, Pack, Version) :- 2889 uri_components(URL, Components), 2890 uri_data(authority, Components, 'github.com'), 2891 uri_data(scheme, Components, Scheme), 2892 download_scheme(Scheme), 2893 uri_data(path, Components, Path), 2894 github_archive_path(Archive,Pack,File), 2895 atomic_list_concat(Archive, /, Path), 2896 file_name_extension(Tag, Ext, File), 2897 github_archive_extension(Ext), 2898 tag_version(Tag, Version), 2899 !. 2900 2901github_archive_path(['',_User,Pack,archive,File],Pack,File). 2902github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File). 2903 2904github_archive_extension(tgz). 2905github_archive_extension(zip).
[vV]?int(\.int)*.2912tag_version(Tag, Version) :- 2913 version_tag_prefix(Prefix), 2914 atom_concat(Prefix, Version, Tag), 2915 is_version(Version). 2916 2917version_tag_prefix(v). 2918version_tag_prefix('V'). 2919version_tag_prefix('').
2928git_archive_url(URL, Archive, Options) :- 2929 uri_components(URL, Components), 2930 uri_data(authority, Components, 'github.com'), 2931 uri_data(path, Components, Path), 2932 atomic_list_concat(['', User, RepoGit], /, Path), 2933 $, 2934 remove_git_ext(RepoGit, Repo), 2935 git_archive_version(Version, Options), 2936 atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath), 2937 uri_edit([ path(ArchivePath), 2938 host('codeload.github.com') 2939 ], 2940 URL, Archive). 2941git_archive_url(URL, _, _) :- 2942 print_message(error, pack(no_git(URL))), 2943 fail. 2944 2945remove_git_ext(RepoGit, Repo) :- 2946 file_name_extension(Repo, git, RepoGit), 2947 !. 2948remove_git_ext(Repo, Repo). 2949 2950git_archive_version(Version, Options) :- 2951 option(commit(Version), Options), 2952 !. 2953git_archive_version(Version, Options) :- 2954 option(branch(Version), Options), 2955 !. 2956git_archive_version(Version, Options) :- 2957 option(version(Version), Options), 2958 !. 2959git_archive_version('HEAD', _). 2960 2961 /******************************* 2962 * QUERY CENTRAL DB * 2963 *******************************/
publish(Pack) that must be
a no-op.publish(Pack) by do_publish(Pack).2978register_downloads(_, Options) :- 2979 option(register(false), Options), 2980 !. 2981register_downloads(_, Options) :- 2982 option(publish(_), Options), 2983 !. 2984register_downloads(Infos, Options) :- 2985 convlist(download_data, Infos, Data), 2986 ( Data == [] 2987 -> true 2988 ; query_pack_server(downloaded(Data), Reply, Options), 2989 ( option(do_publish(Pack), Options) 2990 -> ( member(Info, Infos), 2991 Info.pack == Pack 2992 -> true 2993 ), 2994 ( Reply = true(Actions), 2995 memberchk(Pack-Result, Actions) 2996 -> ( registered(Result) 2997 -> print_message(informational, pack(published(Info, Result))) 2998 ; print_message(error, pack(publish_failed(Info, Result))), 2999 fail 3000 ) 3001 ; print_message(error, pack(publish_failed(Info, false))) 3002 ) 3003 ; true 3004 ) 3005 ). 3006 3007registered(git(_URL)). 3008registered(file(_URL)). 3009 3010publish_download(Infos, Options) :- 3011 select_option(publish(Pack), Options, Options1), 3012 !, 3013 register_downloads(Infos, [do_publish(Pack)|Options1]). 3014publish_download(_Infos, _Options).
download(URL, Hash, Metadata).
Where URL is location of the GIT repository or URL of the download archive. Hash is either the GIT commit hash or the SHA1 of the archive file.
3027download_data(Info, Data), 3028 Info.get(git) == true => % Git clone 3029 Data = download(URL, Hash, Metadata), 3030 URL = Info.get(downloaded), 3031 pack_git_info(Info.installed, Hash, Metadata). 3032download_data(Info, Data), 3033 _{git_url:URL,hash:Hash} :< Info, Hash \== (-) => 3034 Data = download(URL, Hash, Metadata), % Git downloaded as zip 3035 dir_metadata(Info.installed, Metadata). 3036download_data(Info, Data) => % Archive download. 3037 Data = download(URL, Hash, Metadata), 3038 URL = Info.get(downloaded), 3039 download_url(URL), 3040 pack_status_dir(Info.installed, archive(Archive, URL)), 3041 file_sha1(Archive, Hash), 3042 pack_archive_info(Archive, _Pack, Metadata, _).
3049query_pack_server(Query, Result, Options) :- 3050 ( option(server(ServerOpt), Options) 3051 -> server_url(ServerOpt, ServerBase) 3052 ; setting(server, ServerBase), 3053 ServerBase \== '' 3054 ), 3055 atom_concat(ServerBase, query, Server), 3056 format(codes(Data), '~q.~n', Query), 3057 info_level(Informational, Options), 3058 print_message(Informational, pack(contacting_server(Server))), 3059 setup_call_cleanup( 3060 http_open(Server, In, 3061 [ post(codes(application/'x-prolog', Data)), 3062 header(content_type, ContentType) 3063 ]), 3064 read_reply(ContentType, In, Result), 3065 close(In)), 3066 message_severity(Result, Level, Informational), 3067 print_message(Level, pack(server_reply(Result))). 3068 3069server_url(URL0, URL) :- 3070 uri_components(URL0, Components), 3071 uri_data(scheme, Components, Scheme), 3072 var(Scheme), 3073 !, 3074 atom_concat('https://', URL0, URL1), 3075 server_url(URL1, URL). 3076server_url(URL0, URL) :- 3077 uri_components(URL0, Components), 3078 uri_data(path, Components, ''), 3079 !, 3080 uri_edit([path('/pack/')], URL0, URL). 3081server_url(URL, URL). 3082 3083read_reply(ContentType, In, Result) :- 3084 sub_atom(ContentType, 0, _, _, 'application/x-prolog'), 3085 !, 3086 set_stream(In, encoding(utf8)), 3087 read(In, Result). 3088read_reply(ContentType, In, _Result) :- 3089 read_string(In, 500, String), 3090 print_message(error, pack(no_prolog_response(ContentType, String))), 3091 fail. 3092 3093info_level(Level, Options) :- 3094 option(silent(true), Options), 3095 !, 3096 Level = silent. 3097info_level(informational, _). 3098 3099message_severity(true(_), Informational, Informational). 3100message_severity(false, warning, _). 3101message_severity(exception(_), error, _). 3102 3103 3104 /******************************* 3105 * WILDCARD URIs * 3106 *******************************/
3115available_download_versions(URL, Versions, _Options) :- 3116 wildcard_pattern(URL), 3117 github_url(URL, User, Repo), % demands https 3118 !, 3119 findall(Version-VersionURL, 3120 github_version(User, Repo, Version, VersionURL), 3121 Versions). 3122available_download_versions(URL0, Versions, Options) :- 3123 wildcard_pattern(URL0), 3124 !, 3125 hsts(URL0, URL, Options), 3126 file_directory_name(URL, DirURL0), 3127 ensure_slash(DirURL0, DirURL), 3128 print_message(informational, pack(query_versions(DirURL))), 3129 setup_call_cleanup( 3130 http_open(DirURL, In, []), 3131 load_html(stream(In), DOM, 3132 [ syntax_errors(quiet) 3133 ]), 3134 close(In)), 3135 findall(MatchingURL, 3136 absolute_matching_href(DOM, URL, MatchingURL), 3137 MatchingURLs), 3138 ( MatchingURLs == [] 3139 -> print_message(warning, pack(no_matching_urls(URL))) 3140 ; true 3141 ), 3142 versioned_urls(MatchingURLs, VersionedURLs), 3143 sort_version_pairs(VersionedURLs, Versions), 3144 print_message(informational, pack(found_versions(Versions))). 3145available_download_versions(URL, [Version-URL], _Options) :- 3146 ( pack_version_file(_Pack, Version0, URL) 3147 -> Version = Version0 3148 ; Version = '0.0.0' 3149 ).
3155sort_version_pairs(Pairs, Sorted) :- 3156 map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed), 3157 sort(1, @>=, Keyed, SortedKeyed), 3158 pairs_values(SortedKeyed, Sorted). 3159 3160version_pair_sort_key_(Version-_Data, Key) :- 3161 version_sort_key(Version, Key). 3162 3163version_sort_key(Version, Key) :- 3164 split_string(Version, ".", "", Parts), 3165 maplist(number_string, Key, Parts), 3166 !. 3167version_sort_key(Version, _) :- 3168 domain_error(version, Version).
3174github_url(URL, User, Repo) :-
3175 uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3176 atomic_list_concat(['',User,Repo|_], /, Path).3184github_version(User, Repo, Version, VersionURI) :- 3185 atomic_list_concat(['',repos,User,Repo,tags], /, Path1), 3186 uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)), 3187 setup_call_cleanup( 3188 http_open(ApiUri, In, 3189 [ request_header('Accept'='application/vnd.github.v3+json') 3190 ]), 3191 json_read_dict(In, Dicts), 3192 close(In)), 3193 member(Dict, Dicts), 3194 atom_string(Tag, Dict.name), 3195 tag_version(Tag, Version), 3196 atom_string(VersionURI, Dict.zipball_url). 3197 3198wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *). 3199wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?). 3200 3201ensure_slash(Dir, DirS) :- 3202 ( sub_atom(Dir, _, _, 0, /) 3203 -> DirS = Dir 3204 ; atom_concat(Dir, /, DirS) 3205 ). 3206 3207remove_slash(Dir0, Dir) :- 3208 Dir0 \== '/', 3209 atom_concat(Dir1, /, Dir0), 3210 !, 3211 remove_slash(Dir1, Dir). 3212remove_slash(Dir, Dir). 3213 3214absolute_matching_href(DOM, Pattern, Match) :- 3215 xpath(DOM, //a(@href), HREF), 3216 uri_normalized(HREF, Pattern, Match), 3217 wildcard_match(Pattern, Match). 3218 3219versioned_urls([], []). 3220versioned_urls([H|T0], List) :- 3221 file_base_name(H, File), 3222 ( pack_version_file(_Pack, Version, File) 3223 -> List = [Version-H|T] 3224 ; List = T 3225 ), 3226 versioned_urls(T0, T). 3227 3228 3229 /******************************* 3230 * DEPENDENCIES * 3231 *******************************/
3239pack_provides(Pack, Pack@Version) :- 3240 current_pack(Pack), 3241 once(pack_info(Pack, version, version(Version))). 3242pack_provides(Pack, Provides) :- 3243 findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList), 3244 member(Provides, PrvList). 3245 3246pack_requires(Pack, Requires) :- 3247 current_pack(Pack), 3248 findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList), 3249 member(Requires, ReqList). 3250 3251pack_conflicts(Pack, Conflicts) :- 3252 current_pack(Pack), 3253 findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList), 3254 member(Conflicts, CflList).
3261pack_depends_on(Pack, Dependency) :- 3262 ground(Pack), 3263 !, 3264 pack_requires(Pack, Requires), 3265 \+ is_prolog_token(Requires), 3266 pack_provides(Dependency, Provides), 3267 satisfies_req(Provides, Requires). 3268pack_depends_on(Pack, Dependency) :- 3269 ground(Dependency), 3270 !, 3271 pack_provides(Dependency, Provides), 3272 pack_requires(Pack, Requires), 3273 satisfies_req(Provides, Requires). 3274pack_depends_on(Pack, Dependency) :- 3275 current_pack(Pack), 3276 pack_depends_on(Pack, Dependency).
3283dependents(Pack, Deps) :- 3284 setof(Dep, dependent(Pack, Dep, []), Deps). 3285 3286dependent(Pack, Dep, Seen) :- 3287 pack_depends_on(Dep0, Pack), 3288 \+ memberchk(Dep0, Seen), 3289 ( Dep = Dep0 3290 ; dependent(Dep0, Dep, [Dep0|Seen]) 3291 ).
3297validate_dependencies :- 3298 setof(Issue, pack_dependency_issue(_, Issue), Issues), 3299 !, 3300 print_message(warning, pack(dependency_issues(Issues))). 3301validate_dependencies.
3313pack_dependency_issue(Pack, Issue) :- 3314 current_pack(Pack), 3315 pack_dependency_issue_(Pack, Issue). 3316 3317pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :- 3318 pack_requires(Pack, Requires), 3319 ( is_prolog_token(Requires) 3320 -> \+ prolog_satisfies(Requires) 3321 ; \+ ( pack_provides(_, Provides), 3322 satisfies_req(Provides, Requires) ) 3323 ). 3324pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :- 3325 pack_conflicts(Pack, Conflicts), 3326 ( is_prolog_token(Conflicts) 3327 -> prolog_satisfies(Conflicts) 3328 ; pack_provides(_, Provides), 3329 satisfies_req(Provides, Conflicts) 3330 ). 3331 3332 3333 /******************************* 3334 * RECORD PACK FACTS * 3335 *******************************/
built if we built it or downloaded if it was downloaded.true, pack was installed as dependency.3351pack_assert(PackDir, Fact) :- 3352 must_be(ground, Fact), 3353 findall(Term, pack_status_dir(PackDir, Term), Facts0), 3354 update_facts(Facts0, Fact, Facts), 3355 OpenOptions = [encoding(utf8), lock(exclusive)], 3356 status_file(PackDir, StatusFile), 3357 ( Facts == Facts0 3358 -> true 3359 ; Facts0 \== [], 3360 append(Facts0, New, Facts) 3361 -> setup_call_cleanup( 3362 open(StatusFile, append, Out, OpenOptions), 3363 maplist(write_fact(Out), New), 3364 close(Out)) 3365 ; setup_call_cleanup( 3366 open(StatusFile, write, Out, OpenOptions), 3367 ( write_facts_header(Out), 3368 maplist(write_fact(Out), Facts) 3369 ), 3370 close(Out)) 3371 ). 3372 3373update_facts([], Fact, [Fact]) :- 3374 !. 3375update_facts([H|T], Fact, [Fact|T]) :- 3376 general_pack_fact(Fact, GenFact), 3377 general_pack_fact(H, GenTerm), 3378 GenFact =@= GenTerm, 3379 !. 3380update_facts([H|T0], Fact, [H|T]) :- 3381 update_facts(T0, Fact, T). 3382 3383general_pack_fact(built(Arch, _Version, _How), General) => 3384 General = built(Arch, _, _). 3385general_pack_fact(Term, General), compound(Term) => 3386 compound_name_arity(Term, Name, Arity), 3387 compound_name_arity(General, Name, Arity). 3388general_pack_fact(Term, General) => 3389 General = Term. 3390 3391write_facts_header(Out) :- 3392 format(Out, '% Fact status file. Managed by package manager.~n', []). 3393 3394write_fact(Out, Term) :- 3395 format(Out, '~q.~n', [Term]).
status.db.3403pack_status(Pack, Fact) :- 3404 current_pack(Pack, PackDir), 3405 pack_status_dir(PackDir, Fact). 3406 3407pack_status_dir(PackDir, Fact) :- 3408 det_if(ground(Fact), pack_status_(PackDir, Fact)). 3409 3410pack_status_(PackDir, Fact) :- 3411 status_file(PackDir, StatusFile), 3412 catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact), 3413 error(existence_error(source_sink, StatusFile), _), 3414 fail). 3415 3416pack_status_term(built(atom, version, oneof([built,downloaded]))). 3417pack_status_term(automatic(boolean)). 3418pack_status_term(archive(atom, atom)).
3428update_automatic(Info) :- 3429 _ = Info.get(dependency_for), 3430 \+ pack_status(Info.installed, automatic(_)), 3431 !, 3432 pack_assert(Info.installed, automatic(true)). 3433update_automatic(Info) :- 3434 pack_assert(Info.installed, automatic(false)). 3435 3436status_file(PackDir, StatusFile) :- 3437 directory_file_path(PackDir, 'status.db', StatusFile). 3438 3439 /******************************* 3440 * USER INTERACTION * 3441 *******************************/ 3442 3443:- multifile prolog:message//1.
3447menu(_Question, _Alternatives, Default, Selection, Options) :- 3448 option(interactive(false), Options), 3449 !, 3450 Selection = Default. 3451menu(Question, Alternatives, Default, Selection, _) :- 3452 length(Alternatives, N), 3453 between(1, 5, _), 3454 print_message(query, Question), 3455 print_menu(Alternatives, Default, 1), 3456 print_message(query, pack(menu(select))), 3457 read_selection(N, Choice), 3458 !, 3459 ( Choice == default 3460 -> Selection = Default 3461 ; nth1(Choice, Alternatives, Selection=_) 3462 -> true 3463 ). 3464 ([], _, _). 3466print_menu([Value=Label|T], Default, I) :- 3467 ( Value == Default 3468 -> print_message(query, pack(menu(default_item(I, Label)))) 3469 ; print_message(query, pack(menu(item(I, Label)))) 3470 ), 3471 I2 is I + 1, 3472 print_menu(T, Default, I2). 3473 3474read_selection(Max, Choice) :- 3475 get_single_char(Code), 3476 ( answered_default(Code) 3477 -> Choice = default 3478 ; code_type(Code, digit(Choice)), 3479 between(1, Max, Choice) 3480 -> true 3481 ; print_message(warning, pack(menu(reply(1,Max)))), 3482 fail 3483 ).
3491confirm(_Question, Default, Options) :- 3492 Default \== none, 3493 option(interactive(false), Options, true), 3494 !, 3495 Default == yes. 3496confirm(Question, Default, _) :- 3497 between(1, 5, _), 3498 print_message(query, pack(confirm(Question, Default))), 3499 read_yes_no(YesNo, Default), 3500 !, 3501 format(user_error, '~N', []), 3502 YesNo == yes. 3503 3504read_yes_no(YesNo, Default) :- 3505 get_single_char(Code), 3506 code_yes_no(Code, Default, YesNo), 3507 !. 3508 3509code_yes_no(0'y, _, yes). 3510code_yes_no(0'Y, _, yes). 3511code_yes_no(0'n, _, no). 3512code_yes_no(0'N, _, no). 3513code_yes_no(_, none, _) :- !, fail. 3514code_yes_no(C, Default, Default) :- 3515 answered_default(C). 3516 3517answered_default(0'\r). 3518answered_default(0'\n). 3519answered_default(0'\s). 3520 3521 3522 /******************************* 3523 * MESSAGES * 3524 *******************************/ 3525 3526:- multifile prolog:message//1. 3527 3528prologmessage(pack(Message)) --> 3529 message(Message). 3530 3531:- discontiguous 3532 message//1, 3533 label//1. 3534 3535message(invalid_term(pack_info_term, Term)) --> 3536 [ 'Invalid package meta data: ~q'-[Term] ]. 3537message(invalid_term(pack_status_term, Term)) --> 3538 [ 'Invalid package status data: ~q'-[Term] ]. 3539message(directory_exists(Dir)) --> 3540 [ 'Package target directory exists and is not empty:', nl, 3541 '\t~q'-[Dir] 3542 ]. 3543message(already_installed(pack(Pack, Version))) --> 3544 [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ]. 3545message(already_installed(Pack)) --> 3546 [ 'Pack `~w'' is already installed. Package info:'-[Pack] ]. 3547message(kept_foreign(Pack, Arch)) --> 3548 [ 'Found foreign libraries for architecture '-[], 3549 ansi(code, '~q', [Arch]), nl, 3550 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]), 3551 ' to rebuild from sources'-[] 3552 ]. 3553message(no_pack_installed(Pack)) --> 3554 [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ]. 3555message(dependency_issues(Issues)) --> 3556 [ 'The current set of packs has dependency issues:', nl ], 3557 dep_issues(Issues). 3558message(depends(Pack, Deps)) --> 3559 [ 'The following packs depend on `~w\':'-[Pack], nl ], 3560 pack_list(Deps). 3561message(remove(link(To), PackDir)) --> 3562 [ 'Removing ', url(PackDir), nl, ' as link to ', url(To) ]. 3563message(remove(directory, PackDir)) --> 3564 [ 'Removing ~q and contents'-[PackDir] ]. 3565message(remove_existing_pack(PackDir)) --> 3566 [ 'Remove old installation in ~q'-[PackDir] ]. 3567message(delete_autoload_index(Pack, Index)) --> 3568 [ 'Pack ' ], msg_pack(Pack), [ ': deleting autoload index ', url(Index) ]. 3569message(download_plan(Plan)) --> 3570 [ ansi(bold, 'Installation plan:', []), nl ], 3571 install_plan(Plan, Actions), 3572 install_label(Actions). 3573message(build_plan(Plan)) --> 3574 [ ansi(bold, 'The following packs have post install scripts:', []), nl ], 3575 msg_build_plan(Plan), 3576 [ nl, ansi(bold, 'Run scripts?', []) ]. 3577message(autoload(Pack)) --> 3578 [ 'Pack ' ], msg_pack(Pack), 3579 [ ' prefers to be added as autoload library', 3580 nl, ansi(bold, 'Allow?', []) 3581 ]. 3582message(no_meta_data(BaseDir)) --> 3583 [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ]. 3584message(search_no_matches(Name)) --> 3585 [ 'Search for "~w", returned no matching packages'-[Name] ]. 3586message(rebuild(Pack)) --> 3587 [ 'Checking pack "~w" for rebuild ...'-[Pack] ]. 3588message(up_to_date([Pack])) --> 3589 !, 3590 [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ]. 3591message(up_to_date(Packs)) --> 3592 [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ]. 3593message(installed_can_upgrade(List)) --> 3594 sequence(msg_can_upgrade_target, [nl], List). 3595message(new_dependencies(Deps)) --> 3596 [ 'Found new dependencies after downloading (~p).'-[Deps], nl ]. 3597message(query_versions(URL)) --> 3598 [ 'Querying "~w" to find new versions ...'-[URL] ]. 3599message(no_matching_urls(URL)) --> 3600 [ 'Could not find any matching URL: ~q'-[URL] ]. 3601message(found_versions([Latest-_URL|More])) --> 3602 { length(More, Len) }, 3603 [ ' Latest version: ~w (~D older)'-[Latest, Len] ]. 3604message(build(Pack, PackDir)) --> 3605 [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ]. 3606message(contacting_server(Server)) --> 3607 [ 'Contacting server at ~w ...'-[Server], flush ]. 3608message(server_reply(true(_))) --> 3609 [ at_same_line, ' ok'-[] ]. 3610message(server_reply(false)) --> 3611 [ at_same_line, ' done'-[] ]. 3612message(server_reply(exception(E))) --> 3613 [ 'Server reported the following error:'-[], nl ], 3614 '$messages':translate_message(E). 3615message(cannot_create_dir(Alias)) --> 3616 { findall(PackDir, 3617 absolute_file_name(Alias, PackDir, [solutions(all)]), 3618 PackDirs0), 3619 sort(PackDirs0, PackDirs) 3620 }, 3621 [ 'Cannot find a place to create a package directory.'-[], 3622 'Considered:'-[] 3623 ], 3624 candidate_dirs(PackDirs). 3625message(conflict(version, [PackV, FileV])) --> 3626 ['Version mismatch: pack.pl: '-[]], msg_version(PackV), 3627 [', file claims version '-[]], msg_version(FileV). 3628message(conflict(name, [PackInfo, FileInfo])) --> 3629 ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]], 3630 [', file claims ~w: ~p'-[FileInfo]]. 3631message(no_prolog_response(ContentType, String)) --> 3632 [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl, 3633 '~s'-[String] 3634 ]. 3635message(download(begin, Pack, _URL, _DownloadFile)) --> 3636 [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ]. 3637message(download(end, _, _, File)) --> 3638 { size_file(File, Bytes) }, 3639 [ at_same_line, '~D bytes'-[Bytes] ]. 3640message(no_git(URL)) --> 3641 [ 'Cannot install from git repository ', url(URL), '.', nl, 3642 'Cannot find git program and do not know how to download the code', nl, 3643 'from this git service. Please install git and retry.' 3644 ]. 3645message(git_no_https(GitURL)) --> 3646 [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ]. 3647message(git_branch_not_default(Dir, Default, Current)) --> 3648 [ 'GIT current branch on ', url(Dir), ' is not default.', nl, 3649 ' Current branch: ', ansi(code, '~w', [Current]), 3650 ' default: ', ansi(code, '~w', [Default]) 3651 ]. 3652message(git_not_clean(Dir)) --> 3653 [ 'GIT working directory is dirty: ', url(Dir), nl, 3654 'Your repository must be clean before publishing.' 3655 ]. 3656message(git_push) --> 3657 [ 'Push release to GIT origin?' ]. 3658message(git_tag(Tag)) --> 3659 [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ]. 3660message(git_release_tag_not_at_head(Tag)) --> 3661 [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl, 3662 'If you want to update the tag, please run ', 3663 ansi(code, 'git tag -d ~w', [Tag]) 3664 ]. 3665message(git_tag_out_of_sync(Tag)) --> 3666 [ 'Release tag ', ansi(code, '~w', [Tag]), 3667 ' differs from this tag at the origin' 3668 ]. 3669 3670message(published(Info, At)) --> 3671 [ 'Published pack ' ], msg_pack(Info), msg_info_version(Info), 3672 [' to be installed from '], 3673 msg_published_address(At). 3674message(publish_failed(Info, Reason)) --> 3675 [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ], 3676 msg_publish_failed(Reason). 3677 3678msg_publish_failed(throw(error(permission_error(register, 3679 pack(_),_URL),_))) --> 3680 [ ' is already registered with a different URL']. 3681msg_publish_failed(download) --> 3682 [' was already published?']. 3683msg_publish_failed(Status) --> 3684 [ ' failed for unknown reason (~p)'-[Status] ]. 3685 3686msg_published_address(git(URL)) --> 3687 msg_url(URL, _). 3688msg_published_address(file(URL)) --> 3689 msg_url(URL, _). 3690 3691candidate_dirs([]) --> []. 3692candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T). 3693 % Questions 3694message(resolve_remove) --> 3695 [ nl, 'Please select an action:', nl, nl ]. 3696message(create_pack_dir) --> 3697 [ nl, 'Create directory for packages', nl ]. 3698message(menu(item(I, Label))) --> 3699 [ '~t(~d)~6| '-[I] ], 3700 label(Label). 3701message(menu(default_item(I, Label))) --> 3702 [ '~t(~d)~6| * '-[I] ], 3703 label(Label). 3704message(menu(select)) --> 3705 [ nl, 'Your choice? ', flush ]. 3706message(confirm(Question, Default)) --> 3707 message(Question), 3708 confirm_default(Default), 3709 [ flush ]. 3710message(menu(reply(Min,Max))) --> 3711 ( { Max =:= Min+1 } 3712 -> [ 'Please enter ~w or ~w'-[Min,Max] ] 3713 ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ] 3714 ). 3715 3716 % support predicates 3717dep_issues(Issues) --> 3718 sequence(dep_issue, [nl], Issues). 3719 3720dep_issue(unsatisfied(Pack, Requires)) --> 3721 [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]]. 3722dep_issue(conflicts(Pack, Conflict)) --> 3723 [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3730install_label([link]) --> 3731 !, 3732 [ ansi(bold, 'Activate pack?', []) ]. 3733install_label([unpack]) --> 3734 !, 3735 [ ansi(bold, 'Unpack archive?', []) ]. 3736install_label(_) --> 3737 [ ansi(bold, 'Download packs?', []) ]. 3738 3739 3740install_plan(Plan, Actions) --> 3741 install_plan(Plan, Actions, Sec), 3742 sec_warning(Sec). 3743 3744install_plan([], [], _) --> 3745 []. 3746install_plan([H|T], [AH|AT], Sec) --> 3747 install_step(H, AH, Sec), [nl], 3748 install_plan(T, AT, Sec). 3749 3750install_step(Info, keep, _Sec) --> 3751 { Info.get(keep) == true }, 3752 !, 3753 [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ], 3754 msg_can_upgrade(Info). 3755install_step(Info, Action, Sec) --> 3756 { From = Info.get(upgrade), 3757 VFrom = From.version, 3758 VTo = Info.get(version), 3759 ( cmp_versions(>=, VTo, VFrom) 3760 -> Label = ansi(bold, ' Upgrade ', []) 3761 ; Label = ansi(warning, ' Downgrade ', []) 3762 ) 3763 }, 3764 [ Label ], msg_pack(Info), 3765 [ ' from version ~w to ~w'- [From.version, Info.get(version)] ], 3766 install_from(Info, Action, Sec). 3767install_step(Info, Action, Sec) --> 3768 { _From = Info.get(upgrade) }, 3769 [ ' Upgrade ' ], msg_pack(Info), 3770 install_from(Info, Action, Sec). 3771install_step(Info, Action, Sec) --> 3772 { Dep = Info.get(dependency_for) }, 3773 [ ' Install ' ], msg_pack(Info), 3774 [ ' at version ~w as dependency for '-[Info.version], 3775 ansi(code, '~w', [Dep]) 3776 ], 3777 install_from(Info, Action, Sec), 3778 msg_downloads(Info). 3779install_step(Info, Action, Sec) --> 3780 { Info.get(commit) == 'HEAD' }, 3781 !, 3782 [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ], 3783 install_from(Info, Action, Sec), 3784 msg_downloads(Info). 3785install_step(Info, link, _Sec) --> 3786 { Info.get(link) == true, 3787 uri_file_name(Info.get(url), Dir) 3788 }, 3789 !, 3790 [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ]. 3791install_step(Info, Action, Sec) --> 3792 [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ], 3793 install_from(Info, Action, Sec), 3794 msg_downloads(Info). 3795install_step(Info, Action, Sec) --> 3796 [ ' Install ' ], msg_pack(Info), 3797 install_from(Info, Action, Sec), 3798 msg_downloads(Info). 3799 3800install_from(Info, download, Sec) --> 3801 { download_url(Info.url) }, 3802 !, 3803 [ ' from ' ], msg_url(Info.url, Sec). 3804install_from(Info, unpack, Sec) --> 3805 [ ' from ' ], msg_url(Info.url, Sec). 3806 3807msg_url(URL, unsafe) --> 3808 { atomic(URL), 3809 atom_concat('http://', Rest, URL) 3810 }, 3811 [ ansi(error, '~w', ['http://']), '~w'-[Rest] ]. 3812msg_url(URL, _) --> 3813 [ url(URL) ]. 3814 3815sec_warning(Sec) --> 3816 { var(Sec) }, 3817 !. 3818sec_warning(unsafe) --> 3819 [ ansi(warning, ' WARNING: The installation plan includes downloads \c 3820 from insecure HTTP servers.', []), nl 3821 ]. 3822 3823msg_downloads(Info) --> 3824 { Downloads = Info.get(all_downloads), 3825 Downloads > 0 3826 }, 3827 [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ], 3828 !. 3829msg_downloads(_) --> 3830 []. 3831 3832msg_pack(Pack) --> 3833 { atom(Pack) }, 3834 !, 3835 [ ansi(code, '~w', [Pack]) ]. 3836msg_pack(Info) --> 3837 msg_pack(Info.pack). 3838 3839msg_info_version(Info) --> 3840 [ ansi(code, '@~w', [Info.get(version)]) ], 3841 !. 3842msg_info_version(_Info) --> 3843 [].
3849msg_build_plan(Plan) --> 3850 sequence(build_step, [nl], Plan). 3851 3852build_step(Info) --> 3853 [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ]. 3854 3855msg_can_upgrade_target(Info) --> 3856 [ ' Pack ' ], msg_pack(Info), 3857 [ ' is installed at version ~w'-[Info.version] ], 3858 msg_can_upgrade(Info). 3859 3860pack_list([]) --> []. 3861pack_list([H|T]) --> 3862 [ ' - Pack ' ], msg_pack(H), [nl], 3863 pack_list(T). 3864 3865label(remove_only(Pack)) --> 3866 [ 'Only remove package ~w (break dependencies)'-[Pack] ]. 3867label(remove_deps(Pack, Deps)) --> 3868 { length(Deps, Count) }, 3869 [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ]. 3870label(create_dir(Dir)) --> 3871 [ '~w'-[Dir] ]. 3872label(install_from(git(URL))) --> 3873 !, 3874 [ 'GIT repository at ~w'-[URL] ]. 3875label(install_from(URL)) --> 3876 [ '~w'-[URL] ]. 3877label(cancel) --> 3878 [ 'Cancel' ]. 3879 3880confirm_default(yes) --> 3881 [ ' Y/n? ' ]. 3882confirm_default(no) --> 3883 [ ' y/N? ' ]. 3884confirm_default(none) --> 3885 [ ' y/n? ' ]. 3886 3887msg_version(Version) --> 3888 [ '~w'-[Version] ]. 3889 3890msg_can_upgrade(Info) --> 3891 { Latest = Info.get(latest_version) }, 3892 [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ]. 3893msg_can_upgrade(_) --> 3894 []. 3895 3896 3897 /******************************* 3898 * MISC * 3899 *******************************/ 3900 3901local_uri_file_name(URL, FileName) :- 3902 uri_file_name(URL, FileName), 3903 !. 3904local_uri_file_name(URL, FileName) :- 3905 uri_components(URL, Components), 3906 uri_data(scheme, Components, File), File == file, 3907 uri_data(authority, Components, FileNameEnc), 3908 uri_data(path, Components, ''), 3909 uri_encoded(path, FileName, FileNameEnc). 3910 3911det_if(Cond, Goal) :- 3912 ( 3913 -> , 3914 ! 3915 ; 3916 ). 3917 3918member_nonvar(_, Var) :- 3919 var(Var), 3920 !, 3921 fail. 3922member_nonvar(E, [E|_]). 3923member_nonvar(E, [_|T]) :- 3924 member_nonvar(E, T)
A package manager for Prolog
The library(prolog_pack) provides the SWI-Prolog package manager. This library lets you inspect installed packages, install packages, remove packages, etc. This library complemented by the built-in predicates such as attach_packs/2 that makes installed packages available as libraries.
The important functionality of this library is encapsulated in the app
pack. For help, run