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(http/json)). 67:- use_module(library(http/http_client), []). 68:- use_module(library(debug), [assertion/1]). 69:- use_module(library(pairs), [pairs_keys/2]). 70:- autoload(library(git)). 71:- autoload(library(sgml)). 72:- autoload(library(sha)). 73:- autoload(library(build/tools)). 74:- autoload(library(ansi_term), [ansi_format/3]). 75:- autoload(library(pprint), [print_term/2]). 76:- autoload(library(prolog_versions), [require_version/3, cmp_versions/3]). 77:- autoload(library(ugraphs), [vertices_edges_to_ugraph/3, ugraph_layers/2]). 78:- autoload(library(process), [process_which/2]). 79 80:- meta_predicate 81 pack_install_local( , , ).
96 /******************************* 97 * CONSTANTS * 98 *******************************/ 99 100:- setting(server, atom, 'https://www.swi-prolog.org/pack/', 101 'Server to exchange pack information'). 102 103 104 /******************************* 105 * LOCAL DECLARATIONS * 106 *******************************/ 107 108:- op(900, xfx, @). % Token@Version 109 110:- meta_predicate det_if( , ). 111 112 /******************************* 113 * PACKAGE INFO * 114 *******************************/
121current_pack(Pack) :- 122 current_pack(Pack, _). 123 124current_pack(Pack, Dir) :- 125 '$pack':pack(Pack, Dir).
132pack_list_installed :-
133 pack_list('', [installed(true)]),
134 validate_dependencies.
140pack_info(Name) :- 141 pack_info(info, Name). 142 143pack_info(Level, Name) :- 144 must_be(atom, Name), 145 findall(Info, pack_info(Name, Level, Info), Infos0), 146 ( Infos0 == [] 147 -> print_message(warning, pack(no_pack_installed(Name))), 148 fail 149 ; true 150 ), 151 findall(Def, pack_default(Level, Infos, Def), Defs), 152 append(Infos0, Defs, Infos1), 153 sort(Infos1, Infos), 154 show_info(Name, Infos, [info(Level)]). 155 156 157show_info(_Name, _Properties, Options) :- 158 option(silent(true), Options), 159 !. 160show_info(_Name, _Properties, Options) :- 161 option(show_info(false), Options), 162 !. 163show_info(Name, Properties, Options) :- 164 option(info(list), Options), 165 !, 166 memberchk(title(Title), Properties), 167 memberchk(version(Version), Properties), 168 format('i ~w@~w ~28|- ~w~n', [Name, Version, Title]). 169show_info(Name, Properties, _) :- 170 !, 171 print_property_value('Package'-'~w', [Name]), 172 findall(Term, pack_level_info(info, Term, _, _), Terms), 173 maplist(print_property(Properties), Terms). 174 175print_property(_, nl) :- 176 !, 177 format('~n'). 178print_property(Properties, Term) :- 179 findall(Term, member(Term, Properties), Terms), 180 Terms \== [], 181 !, 182 pack_level_info(_, Term, LabelFmt, _Def), 183 ( LabelFmt = Label-FmtElem 184 -> true 185 ; Label = LabelFmt, 186 FmtElem = '~w' 187 ), 188 multi_valued(Terms, FmtElem, FmtList, Values), 189 atomic_list_concat(FmtList, ', ', Fmt), 190 print_property_value(Label-Fmt, Values). 191print_property(_, _). 192 193multi_valued([H], LabelFmt, [LabelFmt], Values) :- 194 !, 195 H =.. [_|Values]. 196multi_valued([H|T], LabelFmt, [LabelFmt|LT], Values) :- 197 H =.. [_|VH], 198 append(VH, MoreValues, Values), 199 multi_valued(T, LabelFmt, LT, MoreValues). 200 201 202pvalue_column(29). 203print_property_value(Prop-Fmt, Values) :- 204 !, 205 pvalue_column(C), 206 atomic_list_concat(['~w:~t~*|', Fmt, '~n'], Format), 207 format(Format, [Prop,C|Values]). 208 209pack_info(Name, Level, Info) :- 210 '$pack':pack(Name, BaseDir), 211 pack_dir_info(BaseDir, Level, Info). 212 213pack_dir_info(BaseDir, Level, Info) :- 214 ( Info = directory(BaseDir) 215 ; pack_info_term(BaseDir, Info) 216 ), 217 pack_level_info(Level, Info, _Format, _Default). 218 219:- public pack_level_info/4. % used by web-server 220 221pack_level_info(_, title(_), 'Title', '<no title>'). 222pack_level_info(_, version(_), 'Installed version', '<unknown>'). 223pack_level_info(info, automatic(_), 'Automatic (dependency only)', -). 224pack_level_info(info, directory(_), 'Installed in directory', -). 225pack_level_info(info, link(_), 'Installed as link to'-'~w', -). 226pack_level_info(info, built(_,_), 'Built on'-'~w for SWI-Prolog ~w', -). 227pack_level_info(info, author(_, _), 'Author'-'~w <~w>', -). 228pack_level_info(info, maintainer(_, _), 'Maintainer'-'~w <~w>', -). 229pack_level_info(info, packager(_, _), 'Packager'-'~w <~w>', -). 230pack_level_info(info, home(_), 'Home page', -). 231pack_level_info(info, download(_), 'Download URL', -). 232pack_level_info(_, provides(_), 'Provides', -). 233pack_level_info(_, requires(_), 'Requires', -). 234pack_level_info(_, conflicts(_), 'Conflicts with', -). 235pack_level_info(_, replaces(_), 'Replaces packages', -). 236pack_level_info(info, library(_), 'Provided libraries', -). 237 238pack_default(Level, Infos, Def) :- 239 pack_level_info(Level, ITerm, _Format, Def), 240 Def \== (-), 241 \+ memberchk(ITerm, Infos).
247pack_info_term(BaseDir, Info) :- 248 directory_file_path(BaseDir, 'pack.pl', InfoFile), 249 catch( 250 term_in_file(valid_term(pack_info_term), InfoFile, Info), 251 error(existence_error(source_sink, InfoFile), _), 252 ( print_message(error, pack(no_meta_data(BaseDir))), 253 fail 254 )). 255pack_info_term(BaseDir, library(Lib)) :- 256 atom_concat(BaseDir, '/prolog/', LibDir), 257 atom_concat(LibDir, '*.pl', Pattern), 258 expand_file_name(Pattern, Files), 259 maplist(atom_concat(LibDir), Plain, Files), 260 convlist(base_name, Plain, Libs), 261 member(Lib, Libs). 262pack_info_term(BaseDir, automatic(Boolean)) :- 263 once(pack_status_dir(BaseDir, automatic(Boolean))). 264pack_info_term(BaseDir, built(Arch, Prolog)) :- 265 pack_status_dir(BaseDir, built(Arch, Prolog, _How)). 266pack_info_term(BaseDir, link(Dest)) :- 267 read_link(BaseDir, _, Dest). 268 269base_name(File, Base) :- 270 file_name_extension(Base, pl, File).
call(Valid, Term)
is true.276:- meta_predicate 277 term_in_file( , , ). 278 279term_in_file(Valid, File, Term) :- 280 exists_file(File), 281 setup_call_cleanup( 282 open(File, read, In, [encoding(utf8)]), 283 term_in_stream(Valid, In, Term), 284 close(In)). 285 286term_in_stream(Valid, In, Term) :- 287 repeat, 288 read_term(In, Term0, []), 289 ( Term0 == end_of_file 290 -> !, fail 291 ; Term = Term0, 292 call(Valid, Term0) 293 ). 294 295:- meta_predicate 296 valid_term( , ). 297 298valid_term(Type, Term) :- 299 Term =.. [Name|Args], 300 same_length(Args, Types), 301 Decl =.. [Name|Types], 302 ( call(Type, Decl) 303 -> maplist(valid_info_arg, Types, Args) 304 ; print_message(warning, pack(invalid_term(Type, Term))), 305 fail 306 ). 307 308valid_info_arg(Type, Arg) :- 309 must_be(Type, Arg).
316pack_info_term(name(atom)). % Synopsis 317pack_info_term(title(atom)). 318pack_info_term(keywords(list(atom))). 319pack_info_term(description(list(atom))). 320pack_info_term(version(version)). 321pack_info_term(author(atom, email_or_url_or_empty)). % Persons 322pack_info_term(maintainer(atom, email_or_url)). 323pack_info_term(packager(atom, email_or_url)). 324pack_info_term(pack_version(nonneg)). % Package convention version 325pack_info_term(home(atom)). % Home page 326pack_info_term(download(atom)). % Source 327pack_info_term(provides(atom)). % Dependencies 328pack_info_term(requires(dependency)). 329pack_info_term(conflicts(dependency)). % Conflicts with package 330pack_info_term(replaces(atom)). % Replaces another package 331pack_info_term(autoload(boolean)). % Default installation options 332 333:- multifile 334 error:has_type/2. 335 336errorhas_type(version, Version) :- 337 atom(Version), 338 is_version(Version). 339errorhas_type(email_or_url, Address) :- 340 atom(Address), 341 ( sub_atom(Address, _, _, _, @) 342 -> true 343 ; uri_is_global(Address) 344 ). 345errorhas_type(email_or_url_or_empty, Address) :- 346 ( Address == '' 347 -> true 348 ; error:has_type(email_or_url, Address) 349 ). 350errorhas_type(dependency, Value) :- 351 is_dependency(Value). 352 353is_version(Version) :- 354 split_string(Version, ".", "", Parts), 355 maplist(number_string, _, Parts). 356 357is_dependency(Var) :- 358 var(Var), 359 !, 360 fail. 361is_dependency(Token) :- 362 atom(Token), 363 !. 364is_dependency(Term) :- 365 compound(Term), 366 compound_name_arguments(Term, Op, [Token,Version]), 367 atom(Token), 368 cmp(Op, _), 369 is_version(Version), 370 !. 371is_dependency(PrologToken) :- 372 is_prolog_token(PrologToken). 373 374cmp(<, @<). 375cmp(=<, @=<). 376cmp(==, ==). 377cmp(>=, @>=). 378cmp(>, @>). 379 380 381 /******************************* 382 * SEARCH * 383 *******************************/
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.
425pack_list(Query) :- 426 pack_list(Query, []). 427 428pack_search(Query) :- 429 pack_list(Query, []). 430 431pack_list(Query, Options) :- 432 ( option(installed(true), Options) 433 ; option(outdated(true), Options) 434 ; option(server(false), Options) 435 ), 436 !, 437 local_search(Query, Local), 438 maplist(arg(1), Local, Packs), 439 ( option(server(false), Options) 440 -> Hits = [] 441 ; query_pack_server(info(Packs), true(Hits), Options) 442 ), 443 list_hits(Hits, Local, Options). 444pack_list(Query, Options) :- 445 query_pack_server(search(Query), Result, Options), 446 ( Result == false 447 -> ( local_search(Query, Packs), 448 Packs \== [] 449 -> forall(member(pack(Pack, Stat, Title, Version, _), Packs), 450 format('~w ~w@~w ~28|- ~w~n', 451 [Stat, Pack, Version, Title])) 452 ; print_message(warning, pack(search_no_matches(Query))) 453 ) 454 ; Result = true(Hits), % Hits = list(pack(Name, p, Title, Version, URL)) 455 local_search(Query, Local), 456 list_hits(Hits, Local, []) 457 ). 458 459list_hits(Hits, Local, Options) :- 460 append(Hits, Local, All), 461 sort(All, Sorted), 462 join_status(Sorted, Packs0), 463 include(filtered(Options), Packs0, Packs), 464 maplist(list_hit(Options), Packs). 465 466filtered(Options, pack(_,Tag,_,_,_)) :- 467 option(outdated(true), Options), 468 !, 469 Tag == 'U'. 470filtered(_, _). 471 472list_hit(_Options, pack(Pack, Tag, Title, Version, _URL)) => 473 list_tag(Tag), 474 ansi_format(code, '~w', [Pack]), 475 format('@'), 476 list_version(Tag, Version), 477 format('~35|- ', []), 478 ansi_format(comment, '~w~n', [Title]). 479 480list_tag(Tag) :- 481 tag_color(Tag, Color), 482 ansi_format(Color, '~w ', [Tag]). 483 484list_version(Tag, VersionI-VersionS) => 485 tag_color(Tag, Color), 486 ansi_format(Color, '~w', [VersionI]), 487 ansi_format(bold, '(~w)', [VersionS]). 488list_version(_Tag, Version) => 489 ansi_format([], '~w', [Version]). 490 491tag_color('U', warning) :- !. 492tag_color('A', comment) :- !. 493tag_color(_, []).
pack(Name, Status, Version, URL)
. If
the versions do not match, Version is
VersionInstalled-VersionRemote
and similar for thee URL.502join_status([], []). 503join_status([ pack(Pack, i, Title, Version, URL), 504 pack(Pack, p, Title, Version, _) 505 | T0 506 ], 507 [ pack(Pack, Tag, Title, Version, URL) 508 | T 509 ]) :- 510 !, 511 ( pack_status(Pack, automatic(true)) 512 -> Tag = a 513 ; Tag = i 514 ), 515 join_status(T0, T). 516join_status([ pack(Pack, i, Title, VersionI, URLI), 517 pack(Pack, p, _, VersionS, URLS) 518 | T0 519 ], 520 [ pack(Pack, Tag, Title, VersionI-VersionS, URLI-URLS) 521 | T 522 ]) :- 523 !, 524 version_sort_key(VersionI, VDI), 525 version_sort_key(VersionS, VDS), 526 ( VDI @< VDS 527 -> Tag = 'U' 528 ; Tag = 'A' 529 ), 530 join_status(T0, T). 531join_status([ pack(Pack, i, Title, VersionI, URL) 532 | T0 533 ], 534 [ pack(Pack, l, Title, VersionI, URL) 535 | T 536 ]) :- 537 !, 538 join_status(T0, T). 539join_status([H|T0], [H|T]) :- 540 join_status(T0, T).
546local_search(Query, Packs) :- 547 findall(Pack, matching_installed_pack(Query, Pack), Packs). 548 549matching_installed_pack(Query, pack(Pack, i, Title, Version, URL)) :- 550 current_pack(Pack), 551 findall(Term, 552 ( pack_info(Pack, _, Term), 553 search_info(Term) 554 ), Info), 555 ( sub_atom_icasechk(Pack, _, Query) 556 -> true 557 ; memberchk(title(Title), Info), 558 sub_atom_icasechk(Title, _, Query) 559 ), 560 option(title(Title), Info, '<no title>'), 561 option(version(Version), Info, '<no version>'), 562 option(download(URL), Info, '<no download url>'). 563 564search_info(title(_)). 565search_info(version(_)). 566search_info(download(_)). 567 568 569 /******************************* 570 * INSTALL * 571 *******************************/
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.
671pack_install(Spec) :- 672 pack_default_options(Spec, Pack, [], Options), 673 pack_install(Pack, [pack(Pack)|Options]). 674 675pack_install(Specs, Options) :- 676 is_list(Specs), 677 !, 678 maplist(pack_options(Options), Specs, Pairs), 679 pack_install_dir(PackTopDir, Options), 680 pack_install_set(Pairs, PackTopDir, Options). 681pack_install(Spec, Options) :- 682 pack_default_options(Spec, Pack, Options, DefOptions), 683 ( option(already_installed(Installed), DefOptions) 684 -> print_message(informational, pack(already_installed(Installed))) 685 ; merge_options(Options, DefOptions, PackOptions), 686 pack_install_dir(PackTopDir, PackOptions), 687 pack_install_set([Pack-PackOptions], PackTopDir, Options) 688 ). 689 690pack_options(Options, Spec, Pack-PackOptions) :- 691 pack_default_options(Spec, Pack, Options, DefOptions), 692 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.718pack_default_options(_Spec, Pack, OptsIn, Options) :- % (1) 719 option(already_installed(pack(Pack,_Version)), OptsIn), 720 !, 721 Options = OptsIn. 722pack_default_options(_Spec, Pack, OptsIn, Options) :- % (2) 723 option(url(URL), OptsIn), 724 !, 725 ( option(git(_), OptsIn) 726 -> Options = OptsIn 727 ; git_url(URL, Pack) 728 -> Options = [git(true)|OptsIn] 729 ; Options = OptsIn 730 ), 731 ( nonvar(Pack) 732 -> true 733 ; option(pack(Pack), Options) 734 -> true 735 ; pack_version_file(Pack, _Version, URL) 736 ). 737pack_default_options(Archive, Pack, OptsIn, Options) :- % (3) 738 must_be(atom, Archive), 739 \+ uri_is_global(Archive), 740 expand_file_name(Archive, [File]), 741 exists_file(File), 742 !, 743 ( pack_version_file(Pack, Version, File) 744 -> uri_file_name(FileURL, File), 745 merge_options([url(FileURL), version(Version)], OptsIn, Options) 746 ; domain_error(pack_file_name, Archive) 747 ). 748pack_default_options(URL, Pack, OptsIn, Options) :- % (4) 749 git_url(URL, Pack), 750 !, 751 merge_options([git(true), url(URL)], OptsIn, Options). 752pack_default_options(FileURL, Pack, _, Options) :- % (5) 753 uri_file_name(FileURL, Dir), 754 exists_directory(Dir), 755 pack_info_term(Dir, name(Pack)), 756 !, 757 ( pack_info_term(Dir, version(Version)) 758 -> uri_file_name(DirURL, Dir), 759 Options = [url(DirURL), version(Version)] 760 ; throw(error(existence_error(key, version, Dir),_)) 761 ). 762pack_default_options('.', Pack, OptsIn, Options) :- % (6) 763 pack_info_term('.', name(Pack)), 764 !, 765 working_directory(Dir, Dir), 766 ( pack_info_term(Dir, version(Version)) 767 -> uri_file_name(DirURL, Dir), 768 NewOptions = [url(DirURL), version(Version) | Options1], 769 ( current_prolog_flag(windows, true) 770 -> Options1 = [] 771 ; Options1 = [link(true), rebuild(make)] 772 ), 773 merge_options(NewOptions, OptsIn, Options) 774 ; throw(error(existence_error(key, version, Dir),_)) 775 ). 776pack_default_options(URL, Pack, OptsIn, Options) :- % (7) 777 pack_version_file(Pack, Version, URL), 778 download_url(URL), 779 !, 780 available_download_versions(URL, Available), 781 Available = [URLVersion-LatestURL|_], 782 NewOptions = [url(LatestURL)|VersionOptions], 783 version_options(Version, URLVersion, Available, VersionOptions), 784 merge_options(NewOptions, OptsIn, Options). 785pack_default_options(Pack, Pack, Options, Options) :- % (8) 786 \+ uri_is_global(Pack). 787 788version_options(Version, Version, _, [version(Version)]) :- !. 789version_options(Version, _, Available, [versions(Available)]) :- 790 sub_atom(Version, _, _, _, *), 791 !. 792version_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_data
pack
.If no writeable directory is found, generate possible location where this directory can be created and ask the user to create one of them.
812pack_install_dir(PackDir, Options) :- 813 option(pack_directory(PackDir), Options), 814 ensure_directory(PackDir), 815 !. 816pack_install_dir(PackDir, Options) :- 817 base_alias(Alias, Options), 818 absolute_file_name(Alias, PackDir, 819 [ file_type(directory), 820 access(write), 821 file_errors(fail) 822 ]), 823 !. 824pack_install_dir(PackDir, Options) :- 825 pack_create_install_dir(PackDir, Options). 826 827base_alias(Alias, Options) :- 828 option(global(true), Options), 829 !, 830 Alias = common_app_data(pack). 831base_alias(Alias, Options) :- 832 option(global(false), Options), 833 !, 834 Alias = user_app_data(pack). 835base_alias(Alias, _Options) :- 836 Alias = pack('.'). 837 838pack_create_install_dir(PackDir, Options) :- 839 base_alias(Alias, Options), 840 findall(Candidate = create_dir(Candidate), 841 ( absolute_file_name(Alias, Candidate, [solutions(all)]), 842 \+ exists_file(Candidate), 843 \+ exists_directory(Candidate), 844 file_directory_name(Candidate, Super), 845 ( exists_directory(Super) 846 -> access_file(Super, write) 847 ; true 848 ) 849 ), 850 Candidates0), 851 list_to_set(Candidates0, Candidates), % keep order 852 pack_create_install_dir(Candidates, PackDir, Options). 853 854pack_create_install_dir(Candidates, PackDir, Options) :- 855 Candidates = [Default=_|_], 856 !, 857 append(Candidates, [cancel=cancel], Menu), 858 menu(pack(create_pack_dir), Menu, Default, Selected, Options), 859 Selected \== cancel, 860 ( catch(make_directory_path(Selected), E, 861 (print_message(warning, E), fail)) 862 -> PackDir = Selected 863 ; delete(Candidates, PackDir=create_dir(PackDir), Remaining), 864 pack_create_install_dir(Remaining, PackDir, Options) 865 ). 866pack_create_install_dir(_, _, _) :- 867 print_message(error, pack(cannot_create_dir(pack(.)))), 868 fail.
882pack_unpack_from_local(Source0, PackTopDir, Name, PackDir, Options) :- 883 exists_directory(Source0), 884 remove_slash(Source0, Source), 885 !, 886 directory_file_path(PackTopDir, Name, PackDir), 887 ( option(link(true), Options) 888 -> ( same_file(Source, PackDir) 889 -> true 890 ; remove_existing_pack(PackDir, Options), 891 atom_concat(PackTopDir, '/', PackTopDirS), 892 relative_file_name(Source, PackTopDirS, RelPath), 893 link_file(RelPath, PackDir, symbolic), 894 assertion(same_file(Source, PackDir)) 895 ) 896 ; is_git_directory(Source) 897 -> remove_existing_pack(PackDir, Options), 898 run_process(path(git), [clone, Source, PackDir], []) 899 ; prepare_pack_dir(PackDir, Options), 900 copy_directory(Source, PackDir) 901 ). 902pack_unpack_from_local(Source, PackTopDir, Name, PackDir, Options) :- 903 exists_file(Source), 904 directory_file_path(PackTopDir, Name, PackDir), 905 prepare_pack_dir(PackDir, Options), 906 pack_unpack(Source, PackDir, Name, Options).
915:- if(exists_source(library(archive))). 916pack_unpack(Source, PackDir, Pack, Options) :- 917 ensure_loaded_archive, 918 pack_archive_info(Source, Pack, _Info, StripOptions), 919 prepare_pack_dir(PackDir, Options), 920 archive_extract(Source, PackDir, 921 [ exclude(['._*']) % MacOS resource forks 922 | StripOptions 923 ]). 924:- else. 925pack_unpack(_,_,_,_) :- 926 existence_error(library, archive). 927:- endif.
935pack_install_local(M:Gen, Dir, Options) :- 936 findall(Pack-PackOptions, call(M:Gen, Pack, PackOptions), Pairs), 937 pack_install_set(Pairs, Dir, Options). 938 939pack_install_set(Pairs, Dir, Options) :- 940 must_be(list(pair), Pairs), 941 ensure_directory(Dir), 942 partition(known_media, Pairs, Local, Remote), 943 maplist(pack_options_to_versions, Local, LocalVersions), 944 ( Remote == [] 945 -> AllVersions = LocalVersions 946 ; pairs_keys(Remote, Packs), 947 prolog_description(Properties), 948 query_pack_server(versions(Packs, Properties), Result, Options), 949 ( Result = true(RemoteVersions) 950 -> append(LocalVersions, RemoteVersions, AllVersions) 951 ; print_message(error, pack(query_failed(Result))), 952 fail 953 ) 954 ), 955 local_packs(Dir, Existing), 956 pack_resolve(Pairs, Existing, AllVersions, Plan, Options), 957 !, % for now, only first plan 958 Options1 = [pack_directory(Dir)|Options], 959 download_plan(Pairs, Plan, PlanB, Options1), 960 register_downloads(PlanB, Options), 961 maplist(update_automatic, PlanB), 962 build_plan(PlanB, Built, Options1), 963 publish_download(PlanB, Options), 964 work_done(Pairs, Plan, PlanB, Built, Options).
973known_media(_-Options) :-
974 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:
992pack_resolve(Pairs, Existing, Versions, Plan, Options) :-
993 insert_existing(Existing, Versions, AllVersions, Options),
994 phrase(select_version(Pairs, AllVersions,
995 [ plan(PlanA), % access to plan
996 dependency_for([]) % dependencies
997 | Options
998 ]),
999 PlanA),
1000 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.1011:- det(insert_existing/4). 1012insert_existing(Existing, [], Versions, _Options) => 1013 maplist(existing_to_versions, Existing, Versions). 1014insert_existing(Existing, [Pack-Versions|T0], AllPackVersions, Options), 1015 select(Installed, Existing, Existing2), 1016 Installed.pack == Pack => 1017 can_upgrade(Installed, Versions, Installed2), 1018 insert_existing_(Installed2, Versions, AllVersions, Options), 1019 AllPackVersions = [Pack-AllVersions|T], 1020 insert_existing(Existing2, T0, T, Options). 1021insert_existing(Existing, [H|T0], AllVersions, Options) => 1022 AllVersions = [H|T], 1023 insert_existing(Existing, T0, T, Options). 1024 1025existing_to_versions(Installed, Pack-[Version-[Installed]]) :- 1026 Pack = Installed.pack, 1027 Version = Installed.version. 1028 1029insert_existing_(Installed, Versions, AllVersions, Options) :- 1030 option(upgrade(true), Options), 1031 !, 1032 insert_existing_(Installed, Versions, AllVersions). 1033insert_existing_(Installed, Versions, AllVersions, _) :- 1034 AllVersions = [Installed.version-[Installed]|Versions]. 1035 1036insert_existing_(Installed, [H|T0], [H|T]) :- 1037 H = V0-_Infos, 1038 cmp_versions(>, V0, Installed.version), 1039 !, 1040 insert_existing_(Installed, T0, T). 1041insert_existing_(Installed, [H0|T], [H|T]) :- 1042 H0 = V0-Infos, 1043 V0 == Installed.version, 1044 !, 1045 H = V0-[Installed|Infos]. 1046insert_existing_(Installed, Versions, All) :- 1047 All = [Installed.version-[Installed]|Versions].
latest_version
key to Installed if its version is older than
the latest available version.1054can_upgrade(Info, [Version-_|_], Info2) :- 1055 cmp_versions(>, Version, Info.version), 1056 !, 1057 Info2 = Info.put(latest_version, Version). 1058can_upgrade(Info, _, Info).
upgrade:true
to elements of PlanA in Existing that are not the
same.1066mark_installed([], _, []). 1067mark_installed([Info|T], Existing, Plan) :- 1068 ( member(Installed, Existing), 1069 Installed.pack == Info.pack 1070 -> ( ( Installed.git == true 1071 -> Info.git == true, 1072 Installed.hash == Info.hash 1073 ; Version = Info.get(version) 1074 -> Installed.version == Version 1075 ) 1076 -> Plan = [Info.put(keep, true)|PlanT] % up-to-date 1077 ; Plan = [Info.put(upgrade, Installed)|PlanT] % needs upgrade 1078 ) 1079 ; Plan = [Info|PlanT] % new install 1080 ), 1081 mark_installed(T, Existing, PlanT).
1089select_version([], _, _) --> 1090 []. 1091select_version([Pack-PackOptions|More], Versions, Options) --> 1092 { memberchk(Pack-PackVersions, Versions), 1093 member(Version-Infos, PackVersions), 1094 compatible_version(Pack, Version, PackOptions), 1095 member(Info, Infos), 1096 pack_options_compatible_with_info(Info, PackOptions), 1097 pack_satisfies(Pack, Version, Info, Info2, PackOptions), 1098 all_downloads(PackVersions, Downloads) 1099 }, 1100 add_to_plan(Info2.put(_{version: Version, all_downloads:Downloads}), 1101 Versions, Options), 1102 select_version(More, Versions, Options). 1103select_version([Pack-_PackOptions|_More], _Versions, _Options) --> 1104 { existence_error(pack, Pack) }. % or warn and continue? 1105 1106all_downloads(PackVersions, AllDownloads) :- 1107 aggregate_all(sum(Downloads), 1108 ( member(_Version-Infos, PackVersions), 1109 member(Info, Infos), 1110 get_dict(downloads, Info, Downloads) 1111 ), 1112 AllDownloads). 1113 1114add_requirements([], _, _) --> 1115 []. 1116add_requirements([H|T], Versions, Options) --> 1117 { is_prolog_token(H), 1118 !, 1119 prolog_satisfies(H) 1120 }, 1121 add_requirements(T, Versions, Options). 1122add_requirements([H|T], Versions, Options) --> 1123 { member(Pack-PackVersions, Versions), 1124 member(Version-Infos, PackVersions), 1125 member(Info, Infos), 1126 ( Provides = @(Pack,Version) 1127 ; member(Provides, Info.get(provides)) 1128 ), 1129 satisfies_req(Provides, H), 1130 all_downloads(PackVersions, Downloads) 1131 }, 1132 add_to_plan(Info.put(_{version: Version, all_downloads:Downloads}), 1133 Versions, Options), 1134 add_requirements(T, Versions, Options).
1142add_to_plan(Info, _Versions, Options) --> 1143 { option(plan(Plan), Options), 1144 member_nonvar(Planned, Plan), 1145 Planned.pack == Info.pack, 1146 !, 1147 same_version(Planned, Info) % same pack, different version 1148 }. 1149add_to_plan(Info, _Versions, _Options) --> 1150 { member(Conflict, Info.get(conflicts)), 1151 is_prolog_token(Conflict), 1152 prolog_satisfies(Conflict), 1153 !, 1154 fail % incompatible with this Prolog 1155 }. 1156add_to_plan(Info, _Versions, Options) --> 1157 { option(plan(Plan), Options), 1158 member_nonvar(Planned, Plan), 1159 info_conflicts(Info, Planned), % Conflicts with a planned pack 1160 !, 1161 fail 1162 }. 1163add_to_plan(Info, Versions, Options) --> 1164 { select_option(dependency_for(Dep0), Options, Options1), 1165 Options2 = [dependency_for([Info.pack|Dep0])|Options1], 1166 ( Dep0 = [DepFor|_] 1167 -> add_dependency_for(DepFor, Info, Info1) 1168 ; Info1 = Info 1169 ) 1170 }, 1171 [Info1], 1172 add_requirements(Info.get(requires,[]), Versions, Options2). 1173 1174add_dependency_for(Pack, Info, Info) :- 1175 Old = Info.get(dependency_for), 1176 !, 1177 b_set_dict(dependency_for, Info, [Pack|Old]). 1178add_dependency_for(Pack, Info0, Info) :- 1179 Info = Info0.put(dependency_for, [Pack]). 1180 1181same_version(Info, Info) :- 1182 !. 1183same_version(Planned, Info) :- 1184 Hash = Planned.get(hash), 1185 Hash \== (-), 1186 !, 1187 Hash == Info.get(hash). 1188same_version(Planned, Info) :- 1189 Planned.get(version) == Info.get(version).
1195info_conflicts(Info, Planned) :- 1196 info_conflicts_(Info, Planned), 1197 !. 1198info_conflicts(Info, Planned) :- 1199 info_conflicts_(Planned, Info), 1200 !. 1201 1202info_conflicts_(Info, Planned) :- 1203 member(Conflict, Info.get(conflicts)), 1204 \+ is_prolog_token(Conflict), 1205 info_provides(Planned, Provides), 1206 satisfies_req(Provides, Conflict), 1207 !. 1208 1209info_provides(Info, Provides) :- 1210 ( Provides = Info.pack@Info.version 1211 ; member(Provides, Info.get(provides)) 1212 ).
1219pack_satisfies(_Pack, _Version, Info0, Info, Options) :- 1220 option(commit('HEAD'), Options), 1221 !, 1222 Info0.get(git) == true, 1223 Info = Info0.put(commit, 'HEAD'). 1224pack_satisfies(_Pack, _Version, Info, Info, Options) :- 1225 option(commit(Commit), Options), 1226 !, 1227 Commit == Info.get(hash). 1228pack_satisfies(Pack, Version, Info, Info, Options) :- 1229 option(version(ReqVersion), Options), 1230 !, 1231 satisfies_version(Pack, Version, ReqVersion). 1232pack_satisfies(_Pack, _Version, Info, Info, _Options).
1236satisfies_version(Pack, Version, ReqVersion) :-
1237 catch(require_version(pack(Pack), Version, ReqVersion),
1238 error(version_error(pack(Pack), Version, ReqVersion),_),
1239 fail).
1245satisfies_req(Token, Token) => true. 1246satisfies_req(@(Token,_), Token) => true. 1247satisfies_req(@(Token,PrvVersion), Req), cmp(Req, Token, Cmp, ReqVersion) => 1248 cmp_versions(Cmp, PrvVersion, ReqVersion). 1249satisfies_req(_,_) => fail. 1250 1251cmp(Token < Version, Token, <, Version). 1252cmp(Token =< Version, Token, =<, Version). 1253cmp(Token = Version, Token, =, Version). 1254cmp(Token == Version, Token, ==, Version). 1255cmp(Token >= Version, Token, >=, Version). 1256cmp(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).
1269:- det(pack_options_to_versions/2). 1270pack_options_to_versions(Pack-PackOptions, Pack-Versions) :- 1271 option(versions(Available), PackOptions), !, 1272 maplist(version_url_info(Pack, PackOptions), Available, Versions). 1273pack_options_to_versions(Pack-PackOptions, Pack-[Version-[Info]]) :- 1274 option(url(URL), PackOptions), 1275 findall(Prop, option_info_prop(PackOptions, Prop), Pairs), 1276 dict_create(Info, #, 1277 [ pack-Pack, 1278 url-URL 1279 | Pairs 1280 ]), 1281 Version = Info.get(version, '0.0.0'). 1282 1283version_url_info(Pack, PackOptions, Version-URL, Version-[Info]) :- 1284 findall(Prop, 1285 ( option_info_prop(PackOptions, Prop), 1286 Prop \= version-_ 1287 ), 1288 Pairs), 1289 dict_create(Info, #, 1290 [ pack-Pack, 1291 url-URL, 1292 version-Version 1293 | Pairs 1294 ]). 1295 1296option_info_prop(PackOptions, Prop-Value) :- 1297 option_info(Prop), 1298 Opt =.. [Prop,Value], 1299 option(Opt, PackOptions). 1300 1301option_info(git). 1302option_info(hash). 1303option_info(version). 1304option_info(branch). 1305option_info(link).
1312compatible_version(Pack, Version, PackOptions) :- 1313 option(version(ReqVersion), PackOptions), 1314 !, 1315 satisfies_version(Pack, Version, ReqVersion). 1316compatible_version(_, _, _).
1323pack_options_compatible_with_info(Info, PackOptions) :-
1324 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1325 dict_create(Dict, _, Pairs),
1326 Dict >:< Info.
1336download_plan(_Targets, Plan, Plan, _Options) :- 1337 exclude(installed, Plan, []), 1338 !. 1339download_plan(Targets, Plan0, Plan, Options) :- 1340 confirm(download_plan(Plan0), yes, Options), 1341 maplist(download_from_info(Options), Plan0, Plan1), 1342 plan_unsatisfied_dependencies(Plan1, Deps), 1343 ( Deps == [] 1344 -> Plan = Plan1 1345 ; print_message(informational, pack(new_dependencies(Deps))), 1346 prolog_description(Properties), 1347 query_pack_server(versions(Deps, Properties), Result, []), 1348 ( Result = true(Versions) 1349 -> pack_resolve(Targets, Plan1, Versions, Plan2, Options), 1350 !, 1351 download_plan(Targets, Plan2, Plan, Options) 1352 ; print_message(error, pack(query_failed(Result))), 1353 fail 1354 ) 1355 ).
1362plan_unsatisfied_dependencies(Plan, Deps) :- 1363 phrase(plan_unsatisfied_dependencies(Plan, Plan), Deps). 1364 1365plan_unsatisfied_dependencies([], _) --> 1366 []. 1367plan_unsatisfied_dependencies([Info|Infos], Plan) --> 1368 { Deps = Info.get(requires) }, 1369 plan_unsatisfied_requirements(Deps, Plan), 1370 plan_unsatisfied_dependencies(Infos, Plan). 1371 1372plan_unsatisfied_requirements([], _) --> 1373 []. 1374plan_unsatisfied_requirements([H|T], Plan) --> 1375 { is_prolog_token(H), % Can this fail? 1376 prolog_satisfies(H) 1377 }, 1378 !, 1379 plan_unsatisfied_requirements(T, Plan). 1380plan_unsatisfied_requirements([H|T], Plan) --> 1381 { member(Info, Plan), 1382 ( ( Version = Info.get(version) 1383 -> Provides = @(Info.get(pack), Version) 1384 ; Provides = Info.get(pack) 1385 ) 1386 ; member(Provides, Info.get(provides)) 1387 ), 1388 satisfies_req(Provides, H) 1389 }, !, 1390 plan_unsatisfied_requirements(T, Plan). 1391plan_unsatisfied_requirements([H|T], Plan) --> 1392 [H], 1393 plan_unsatisfied_requirements(T, Plan).
1402build_plan(Plan, Ordered, Options) :- 1403 partition(needs_rebuild_from_info(Options), Plan, ToBuild, NoBuild), 1404 maplist(attach_from_info(Options), NoBuild), 1405 ( ToBuild == [] 1406 -> Ordered = [] 1407 ; order_builds(ToBuild, Ordered), 1408 confirm(build_plan(Ordered), yes, Options), 1409 maplist(exec_plan_rebuild_step(Options), Ordered) 1410 ). 1411 1412needs_rebuild_from_info(Options, Info) :- 1413 needs_rebuild(Info.installed, Options).
1419needs_rebuild(PackDir, Options) :-
1420 ( is_foreign_pack(PackDir, _),
1421 \+ is_built(PackDir, Options)
1422 -> true
1423 ; is_autoload_pack(PackDir, Options),
1424 post_install_autoload(PackDir, Options),
1425 fail
1426 ).
1435is_built(PackDir, _Options) :-
1436 current_prolog_flag(arch, Arch),
1437 prolog_version_dotted(Version), % Major.Minor.Patch
1438 pack_status_dir(PackDir, built(Arch, Version, _)).
1445order_builds(ToBuild, Ordered) :- 1446 findall(Pack-Dep, dep_edge(ToBuild, Pack, Dep), Edges), 1447 maplist(get_dict(pack), ToBuild, Packs), 1448 vertices_edges_to_ugraph(Packs, Edges, Graph), 1449 ugraph_layers(Graph, Layers), 1450 append(Layers, PackNames), 1451 maplist(pack_info_from_name(ToBuild), PackNames, Ordered). 1452 1453dep_edge(Infos, Pack, Dep) :- 1454 member(Info, Infos), 1455 Pack = Info.pack, 1456 member(Dep, Info.get(dependency_for)), 1457 ( member(DepInfo, Infos), 1458 DepInfo.pack == Dep 1459 -> true 1460 ). 1461 1462:- det(pack_info_from_name/3). 1463pack_info_from_name(Infos, Pack, Info) :- 1464 member(Info, Infos), 1465 Info.pack == Pack, 1466 !.
1472exec_plan_rebuild_step(Options, Info) :-
1473 print_message(informational, pack(build(Info.pack, Info.installed))),
1474 pack_post_install(Info.pack, Info.installed, Options),
1475 attach_from_info(Options, Info).
1481attach_from_info(_Options, Info) :- 1482 Info.get(keep) == true, 1483 !. 1484attach_from_info(Options, Info) :- 1485 ( option(pack_directory(_Parent), Options) 1486 -> pack_attach(Info.installed, [duplicate(replace)]) 1487 ; pack_attach(Info.installed, []) 1488 ).
1498download_from_info(Options, Info0, Info), option(dryrun(true), Options) => 1499 print_term(Info0, [nl(true)]), 1500 Info = Info0. 1501download_from_info(_Options, Info0, Info), installed(Info0) => 1502 Info = Info0. 1503download_from_info(_Options, Info0, Info), 1504 _{upgrade:OldInfo, git:true} :< Info0, 1505 is_git_directory(OldInfo.installed) => 1506 PackDir = OldInfo.installed, 1507 git_checkout_version(PackDir, [commit(Info0.hash)]), 1508 reload_info(PackDir, Info0, Info). 1509download_from_info(Options, Info0, Info), 1510 _{upgrade:OldInfo} :< Info0 => 1511 PackDir = OldInfo.installed, 1512 detach_pack(OldInfo.pack, PackDir), 1513 delete_directory_and_contents(PackDir), 1514 del_dict(upgrade, Info0, _, Info1), 1515 download_from_info(Options, Info1, Info). 1516download_from_info(Options, Info0, Info), 1517 _{url:URL, git:true} :< Info0, \+ have_git => 1518 git_archive_url(URL, Archive, Options), 1519 download_from_info([git_url(URL)|Options], 1520 Info0.put(_{ url:Archive, 1521 git:false, 1522 git_url:URL 1523 }), 1524 Info1), 1525 % restore the hash to register the download. 1526 ( Info1.get(version) == Info0.get(version), 1527 Hash = Info0.get(hash) 1528 -> Info = Info1.put(hash, Hash) 1529 ; Info = Info1 1530 ). 1531download_from_info(Options, Info0, Info), 1532 _{url:URL} :< Info0 => 1533 select_option(pack_directory(Dir), Options, Options1), 1534 select_option(version(_), Options1, Options2, _), 1535 download_info_extra(Info0, InstallOptions, Options2), 1536 pack_download_from_url(URL, Dir, Info0.pack, 1537 [ interactive(false), 1538 pack_dir(PackDir) 1539 | InstallOptions 1540 ]), 1541 reload_info(PackDir, Info0, Info). 1542 1543download_info_extra(Info, [git(true),commit(Hash)|Options], Options) :- 1544 Info.get(git) == true, 1545 !, 1546 Hash = Info.get(commit, 'HEAD'). 1547download_info_extra(Info, [link(true)|Options], Options) :- 1548 Info.get(link) == true, 1549 !. 1550download_info_extra(_, Options, Options). 1551 1552installed(Info) :- 1553 _ = Info.get(installed). 1554 1555detach_pack(Pack, PackDir) :- 1556 ( current_pack(Pack, PackDir) 1557 -> '$pack_detach'(Pack, PackDir) 1558 ; true 1559 ).
1568reload_info(_PackDir, Info, Info) :- 1569 _ = Info.get(installed), % we read it from the package 1570 !. 1571reload_info(PackDir, Info0, Info) :- 1572 local_pack_info(PackDir, Info1), 1573 Info = Info0.put(installed, PackDir) 1574 .put(downloaded, Info0.url) 1575 .put(Info1).
1582work_done(_, _, _, _, Options), 1583 option(silent(true), Options) => 1584 true. 1585work_done(Targets, Plan, Plan, [], _Options) => 1586 convlist(can_upgrade_target(Plan), Targets, CanUpgrade), 1587 ( CanUpgrade == [] 1588 -> pairs_keys(Targets, Packs), 1589 print_message(informational, pack(up_to_date(Packs))) 1590 ; print_message(informational, pack(installed_can_upgrade(CanUpgrade))) 1591 ). 1592work_done(_, _, _, _, _) => 1593 true. 1594 1595can_upgrade_target(Plan, Pack-_, Info) => 1596 member(Info, Plan), 1597 Info.pack == Pack, 1598 !, 1599 _ = Info.get(latest_version).
1606local_packs(Dir, Packs) :- 1607 findall(Pack, pack_in_subdir(Dir, Pack), Packs). 1608 1609pack_in_subdir(Dir, Info) :- 1610 directory_member(Dir, PackDir, 1611 [ file_type(directory), 1612 hidden(false) 1613 ]), 1614 local_pack_info(PackDir, Info). 1615 1616local_pack_info(PackDir, 1617 #{ pack: Pack, 1618 version: Version, 1619 title: Title, 1620 hash: Hash, 1621 url: URL, 1622 git: IsGit, 1623 requires: Requires, 1624 provides: Provides, 1625 conflicts: Conflicts, 1626 installed: PackDir 1627 }) :- 1628 directory_file_path(PackDir, 'pack.pl', MetaFile), 1629 exists_file(MetaFile), 1630 file_base_name(PackDir, DirName), 1631 findall(Term, pack_dir_info(PackDir, _, Term), Info), 1632 option(pack(Pack), Info, DirName), 1633 option(title(Title), Info, '<no title>'), 1634 option(version(Version), Info, '<no version>'), 1635 option(download(URL), Info, '<no download url>'), 1636 findall(Req, member(requires(Req), Info), Requires), 1637 findall(Prv, member(provides(Prv), Info), Provides), 1638 findall(Cfl, member(conflicts(Cfl), Info), Conflicts), 1639 ( have_git, 1640 is_git_directory(PackDir) 1641 -> git_hash(Hash, [directory(PackDir)]), 1642 IsGit = true 1643 ; Hash = '-', 1644 IsGit = false 1645 ). 1646 1647 1648 /******************************* 1649 * PROLOG VERSIONS * 1650 *******************************/
prolog(Dialect, Version)
1661prolog_description([prolog(swi(Version))]) :- 1662 prolog_version(Version). 1663 1664prolog_version(Version) :- 1665 current_prolog_flag(version_git, Version), 1666 !. 1667prolog_version(Version) :- 1668 prolog_version_dotted(Version). 1669 1670prolog_version_dotted(Version) :- 1671 current_prolog_flag(version_data, swi(Major, Minor, Patch, _)), 1672 VNumbers = [Major, Minor, Patch], 1673 atomic_list_concat(VNumbers, '.', Version).
1680is_prolog_token(Token), cmp(Token, prolog, _Cmp, _Version) => true. 1681is_prolog_token(prolog:_Feature) => true. 1682is_prolog_token(_) => fail.
requires(Token)
terms for
library(Lib)
1697prolog_satisfies(Token), cmp(Token, prolog, Cmp, ReqVersion) => 1698 prolog_version(CurrentVersion), 1699 cmp_versions(Cmp, CurrentVersion, ReqVersion). 1700prolog_satisfies(prolog:library(Lib)), atom(Lib) => 1701 exists_source(library(Lib)). 1702prolog_satisfies(prolog:Feature), atom(Feature) => 1703 current_prolog_flag(Feature, true). 1704prolog_satisfies(prolog:Feature), flag_value_feature(Feature, Flag, Value) => 1705 current_prolog_flag(Flag, Value). 1706 1707flag_value_feature(Feature, Flag, Value) :- 1708 compound(Feature), 1709 compound_name_arguments(Feature, Flag, [Value]). 1710 1711 1712 /******************************* 1713 * INFO * 1714 *******************************/
Requires library(archive), which is lazily loaded when needed.
1728:- if(exists_source(library(archive))). 1729ensure_loaded_archive :- 1730 current_predicate(archive_open/3), 1731 !. 1732ensure_loaded_archive :- 1733 use_module(library(archive)). 1734 1735pack_archive_info(Archive, Pack, [archive_size(Bytes)|Info], Strip) :- 1736 ensure_loaded_archive, 1737 size_file(Archive, Bytes), 1738 setup_call_cleanup( 1739 archive_open(Archive, Handle, []), 1740 ( repeat, 1741 ( archive_next_header(Handle, InfoFile) 1742 -> true 1743 ; !, fail 1744 ) 1745 ), 1746 archive_close(Handle)), 1747 file_base_name(InfoFile, 'pack.pl'), 1748 atom_concat(Prefix, 'pack.pl', InfoFile), 1749 strip_option(Prefix, Pack, Strip), 1750 setup_call_cleanup( 1751 archive_open_entry(Handle, Stream), 1752 read_stream_to_terms(Stream, Info), 1753 close(Stream)), 1754 !, 1755 must_be(ground, Info), 1756 maplist(valid_term(pack_info_term), Info). 1757:- else. 1758pack_archive_info(_, _, _, _) :- 1759 existence_error(library, archive). 1760:- endif. 1761pack_archive_info(_, _, _, _) :- 1762 existence_error(pack_file, 'pack.pl'). 1763 1764strip_option('', _, []) :- !. 1765strip_option('./', _, []) :- !. 1766strip_option(Prefix, Pack, [remove_prefix(Prefix)]) :- 1767 atom_concat(PrefixDir, /, Prefix), 1768 file_base_name(PrefixDir, Base), 1769 ( Base == Pack 1770 -> true 1771 ; pack_version_file(Pack, _, Base) 1772 -> true 1773 ; \+ sub_atom(PrefixDir, _, _, _, /) 1774 ). 1775 1776read_stream_to_terms(Stream, Terms) :- 1777 read(Stream, Term0), 1778 read_stream_to_terms(Term0, Stream, Terms). 1779 1780read_stream_to_terms(end_of_file, _, []) :- !. 1781read_stream_to_terms(Term0, Stream, [Term0|Terms]) :- 1782 read(Stream, Term1), 1783 read_stream_to_terms(Term1, Stream, Terms).
1791pack_git_info(GitDir, Hash, [git(true), installed_size(Bytes)|Info]) :- 1792 exists_directory(GitDir), 1793 !, 1794 git_ls_tree(Entries, [directory(GitDir)]), 1795 git_hash(Hash, [directory(GitDir)]), 1796 maplist(arg(4), Entries, Sizes), 1797 sum_list(Sizes, Bytes), 1798 dir_metadata(GitDir, Info). 1799 1800dir_metadata(GitDir, Info) :- 1801 directory_file_path(GitDir, 'pack.pl', InfoFile), 1802 read_file_to_terms(InfoFile, Info, [encoding(utf8)]), 1803 must_be(ground, Info), 1804 maplist(valid_term(pack_info_term), Info).
1810download_file_sanity_check(Archive, Pack, Info) :- 1811 info_field(name(PackName), Info), 1812 info_field(version(PackVersion), Info), 1813 pack_version_file(PackFile, FileVersion, Archive), 1814 must_match([Pack, PackName, PackFile], name), 1815 must_match([PackVersion, FileVersion], version). 1816 1817info_field(Field, Info) :- 1818 memberchk(Field, Info), 1819 ground(Field), 1820 !. 1821info_field(Field, _Info) :- 1822 functor(Field, FieldName, _), 1823 print_message(error, pack(missing(FieldName))), 1824 fail. 1825 1826must_match(Values, _Field) :- 1827 sort(Values, [_]), 1828 !. 1829must_match(Values, Field) :- 1830 print_message(error, pack(conflict(Field, Values))), 1831 fail. 1832 1833 1834 /******************************* 1835 * INSTALLATION * 1836 *******************************/
1850prepare_pack_dir(Dir, Options) :- 1851 exists_directory(Dir), 1852 !, 1853 ( empty_directory(Dir) 1854 -> true 1855 ; remove_existing_pack(Dir, Options) 1856 -> make_directory(Dir) 1857 ). 1858prepare_pack_dir(Dir, _) :- 1859 ( read_link(Dir, _, _) 1860 ; access_file(Dir, exist) 1861 ), 1862 !, 1863 delete_file(Dir), 1864 make_directory(Dir). 1865prepare_pack_dir(Dir, _) :- 1866 make_directory(Dir).
1872empty_directory(Dir) :- 1873 \+ ( directory_files(Dir, Entries), 1874 member(Entry, Entries), 1875 \+ special(Entry) 1876 ). 1877 1878special(.). 1879special(..).
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.1888remove_existing_pack(PackDir, Options) :- 1889 exists_directory(PackDir), 1890 !, 1891 ( ( option(upgrade(true), Options) 1892 ; confirm(remove_existing_pack(PackDir), yes, Options) 1893 ) 1894 -> delete_directory_and_contents(PackDir) 1895 ; print_message(error, pack(directory_exists(PackDir))), 1896 fail 1897 ). 1898remove_existing_pack(_, _).
1914pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1915 option(git(true), Options), 1916 !, 1917 directory_file_path(PackTopDir, Pack, PackDir), 1918 prepare_pack_dir(PackDir, Options), 1919 ( option(branch(Branch), Options) 1920 -> Extra = ['--branch', Branch] 1921 ; Extra = [] 1922 ), 1923 run_process(path(git), [clone, URL, PackDir|Extra], []), 1924 git_checkout_version(PackDir, [update(false)|Options]), 1925 option(pack_dir(PackDir), Options, _). 1926pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1927 download_url(URL), 1928 !, 1929 directory_file_path(PackTopDir, Pack, PackDir), 1930 prepare_pack_dir(PackDir, Options), 1931 pack_download_dir(PackTopDir, DownLoadDir), 1932 download_file(URL, Pack, DownloadBase, Options), 1933 directory_file_path(DownLoadDir, DownloadBase, DownloadFile), 1934 ( option(insecure(true), Options, false) 1935 -> TLSOptions = [cert_verify_hook(ssl_verify)] 1936 ; TLSOptions = [] 1937 ), 1938 print_message(informational, pack(download(begin, Pack, URL, DownloadFile))), 1939 setup_call_cleanup( 1940 http_open(URL, In, TLSOptions), 1941 setup_call_cleanup( 1942 open(DownloadFile, write, Out, [type(binary)]), 1943 copy_stream_data(In, Out), 1944 close(Out)), 1945 close(In)), 1946 print_message(informational, pack(download(end, Pack, URL, DownloadFile))), 1947 pack_archive_info(DownloadFile, Pack, Info, _), 1948 ( option(git_url(GitURL), Options) 1949 -> Origin = GitURL % implicit download from git. 1950 ; download_file_sanity_check(DownloadFile, Pack, Info), 1951 Origin = URL 1952 ), 1953 pack_unpack_from_local(DownloadFile, PackTopDir, Pack, PackDir, Options), 1954 pack_assert(PackDir, archive(DownloadFile, Origin)), 1955 option(pack_dir(PackDir), Options, _). 1956pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1957 local_uri_file_name(URL, File), 1958 !, 1959 pack_unpack_from_local(File, PackTopDir, Pack, PackDir, Options), 1960 pack_assert(PackDir, archive(File, URL)), 1961 option(pack_dir(PackDir), Options, _). 1962pack_download_from_url(URL, _PackTopDir, _Pack, _Options) :- 1963 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')
.1987git_checkout_version(PackDir, Options) :- 1988 option(commit('HEAD'), Options), 1989 option(branch(Branch), Options), 1990 !, 1991 git_ensure_on_branch(PackDir, Branch), 1992 run_process(path(git), ['-C', PackDir, pull], []). 1993git_checkout_version(PackDir, Options) :- 1994 option(commit('HEAD'), Options), 1995 git_current_branch(_, [directory(PackDir)]), 1996 !, 1997 run_process(path(git), ['-C', PackDir, pull], []). 1998git_checkout_version(PackDir, Options) :- 1999 option(commit('HEAD'), Options), 2000 !, 2001 git_default_branch(Branch, [directory(PackDir)]), 2002 git_ensure_on_branch(PackDir, Branch), 2003 run_process(path(git), ['-C', PackDir, pull], []). 2004git_checkout_version(PackDir, Options) :- 2005 option(commit(Hash), Options), 2006 run_process(path(git), ['-C', PackDir, fetch], []), 2007 git_branches(Branches, [contains(Hash), directory(PackDir)]), 2008 git_process_output(['-C', PackDir, 'rev-parse' | Branches], 2009 read_lines_to_atoms(Commits), 2010 []), 2011 nth1(I, Commits, Hash), 2012 nth1(I, Branches, Branch), 2013 !, 2014 git_ensure_on_branch(PackDir, Branch). 2015git_checkout_version(PackDir, Options) :- 2016 option(commit(Hash), Options), 2017 !, 2018 run_process(path(git), ['-C', PackDir, checkout, '--quiet', Hash], []). 2019git_checkout_version(PackDir, Options) :- 2020 option(version(Version), Options), 2021 !, 2022 git_tags(Tags, [directory(PackDir)]), 2023 ( memberchk(Version, Tags) 2024 -> Tag = Version 2025 ; member(Tag, Tags), 2026 sub_atom(Tag, B, _, 0, Version), 2027 sub_atom(Tag, 0, B, _, Prefix), 2028 version_prefix(Prefix) 2029 -> true 2030 ; existence_error(version_tag, Version) 2031 ), 2032 run_process(path(git), ['-C', PackDir, checkout, Tag], []). 2033git_checkout_version(_PackDir, Options) :- 2034 option(fresh(true), Options), 2035 !. 2036git_checkout_version(PackDir, _Options) :- 2037 git_current_branch(_, [directory(PackDir)]), 2038 !, 2039 run_process(path(git), ['-C', PackDir, pull], []). 2040git_checkout_version(PackDir, _Options) :- 2041 git_default_branch(Branch, [directory(PackDir)]), 2042 git_ensure_on_branch(PackDir, Branch), 2043 run_process(path(git), ['-C', PackDir, pull], []).
2049git_ensure_on_branch(PackDir, Branch) :- 2050 git_current_branch(Branch, [directory(PackDir)]), 2051 !. 2052git_ensure_on_branch(PackDir, Branch) :- 2053 run_process(path(git), ['-C', PackDir, checkout, Branch], []). 2054 2055read_lines_to_atoms(Atoms, In) :- 2056 read_line_to_string(In, Line), 2057 ( Line == end_of_file 2058 -> Atoms = [] 2059 ; atom_string(Atom, Line), 2060 Atoms = [Atom|T], 2061 read_lines_to_atoms(T, In) 2062 ). 2063 2064version_prefix(Prefix) :- 2065 atom_codes(Prefix, Codes), 2066 phrase(version_prefix, Codes). 2067 2068version_prefix --> 2069 [C], 2070 { code_type(C, alpha) }, 2071 !, 2072 version_prefix. 2073version_prefix --> 2074 "-". 2075version_prefix --> 2076 "_". 2077version_prefix --> 2078 "".
2085download_file(URL, Pack, File, Options) :- 2086 option(version(Version), Options), 2087 !, 2088 file_name_extension(_, Ext, URL), 2089 format(atom(File), '~w-~w.~w', [Pack, Version, Ext]). 2090download_file(URL, Pack, File, _) :- 2091 file_base_name(URL,Basename), 2092 no_int_file_name_extension(Tag,Ext,Basename), 2093 tag_version(Tag,Version), 2094 !, 2095 format(atom(File0), '~w-~w', [Pack, Version]), 2096 file_name_extension(File0, Ext, File). 2097download_file(URL, _, File, _) :- 2098 file_base_name(URL, File).
2106:- public pack_url_file/2. 2107pack_url_file(URL, FileID) :- 2108 github_release_url(URL, Pack, Version), 2109 !, 2110 download_file(URL, Pack, FileID, [version(Version)]). 2111pack_url_file(URL, FileID) :- 2112 file_base_name(URL, FileID). 2113 2114% ssl_verify(+SSL, +ProblemCert, +AllCerts, +FirstCert, +Error) 2115% 2116% Used if insecure(true) is given to pack_install/2. Accepts any 2117% certificate. 2118 2119:- public ssl_verify/5. 2120ssl_verify(_SSL, 2121 _ProblemCertificate, _AllCertificates, _FirstCertificate, 2122 _Error). 2123 2124pack_download_dir(PackTopDir, DownLoadDir) :- 2125 directory_file_path(PackTopDir, 'Downloads', DownLoadDir), 2126 ( exists_directory(DownLoadDir) 2127 -> true 2128 ; make_directory(DownLoadDir) 2129 ), 2130 ( access_file(DownLoadDir, write) 2131 -> true 2132 ; permission_error(write, directory, DownLoadDir) 2133 ).
ftp://
are also download URLs, but we cannot download
from them.2141download_url(URL) :- 2142 atom(URL), 2143 uri_components(URL, Components), 2144 uri_data(scheme, Components, Scheme), 2145 download_scheme(Scheme). 2146 2147download_scheme(http). 2148download_scheme(https).
2158pack_post_install(Pack, PackDir, Options) :-
2159 post_install_foreign(Pack, PackDir, Options),
2160 post_install_autoload(PackDir, Options),
2161 attach_packs(PackDir, [duplicate(warning)]).
2169pack_rebuild :- 2170 forall(current_pack(Pack), 2171 ( print_message(informational, pack(rebuild(Pack))), 2172 pack_rebuild(Pack) 2173 )). 2174 2175pack_rebuild(Pack) :- 2176 current_pack(Pack, PackDir), 2177 !, 2178 post_install_foreign(Pack, PackDir, [rebuild(true)]). 2179pack_rebuild(Pack) :- 2180 unattached_pack(Pack, PackDir), 2181 !, 2182 post_install_foreign(Pack, PackDir, [rebuild(true)]). 2183pack_rebuild(Pack) :- 2184 existence_error(pack, Pack). 2185 2186unattached_pack(Pack, BaseDir) :- 2187 directory_file_path(Pack, 'pack.pl', PackFile), 2188 absolute_file_name(pack(PackFile), PackPath, 2189 [ access(read), 2190 file_errors(fail) 2191 ]), 2192 file_directory_name(PackPath, BaseDir).
2208post_install_foreign(Pack, PackDir, Options) :- 2209 is_foreign_pack(PackDir, _), 2210 !, 2211 ( pack_info_term(PackDir, pack_version(Version)) 2212 -> true 2213 ; Version = 1 2214 ), 2215 option(rebuild(Rebuild), Options, if_absent), 2216 current_prolog_flag(arch, Arch), 2217 prolog_version_dotted(PrologVersion), 2218 ( Rebuild == if_absent, 2219 foreign_present(PackDir, Arch) 2220 -> print_message(informational, pack(kept_foreign(Pack, Arch))), 2221 ( pack_status_dir(PackDir, built(Arch, _, _)) 2222 -> true 2223 ; pack_assert(PackDir, built(Arch, PrologVersion, downloaded)) 2224 ) 2225 ; BuildSteps0 = [[dependencies], [configure], build, install, [test]], 2226 ( Rebuild == true 2227 -> BuildSteps1 = [distclean|BuildSteps0] 2228 ; BuildSteps1 = BuildSteps0 2229 ), 2230 ( option(test(false), Options) 2231 -> delete(BuildSteps1, [test], BuildSteps2) 2232 ; BuildSteps2 = BuildSteps1 2233 ), 2234 ( option(clean(true), Options) 2235 -> append(BuildSteps2, [[clean]], BuildSteps) 2236 ; BuildSteps = BuildSteps2 2237 ), 2238 build_steps(BuildSteps, PackDir, [pack_version(Version)|Options]), 2239 pack_assert(PackDir, built(Arch, PrologVersion, built)) 2240 ). 2241post_install_foreign(_, _, _).
lib
directory for
the current architecture.
2252foreign_present(PackDir, Arch) :-
2253 atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
2254 exists_directory(ForeignBaseDir),
2255 !,
2256 atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
2257 exists_directory(ForeignDir),
2258 current_prolog_flag(shared_object_extension, Ext),
2259 atomic_list_concat([ForeignDir, '/*.', Ext], Pattern),
2260 expand_file_name(Pattern, Files),
2261 Files \== [].
2268is_foreign_pack(PackDir, Type) :- 2269 foreign_file(File, Type), 2270 directory_file_path(PackDir, File, Path), 2271 exists_file(Path). 2272 2273foreign_file('CMakeLists.txt', cmake). 2274foreign_file('configure', configure). 2275foreign_file('configure.in', autoconf). 2276foreign_file('configure.ac', autoconf). 2277foreign_file('Makefile.am', automake). 2278foreign_file('Makefile', make). 2279foreign_file('makefile', make). 2280foreign_file('conanfile.txt', conan). 2281foreign_file('conanfile.py', conan). 2282 2283 2284 /******************************* 2285 * AUTOLOAD * 2286 *******************************/
2292post_install_autoload(PackDir, Options) :- 2293 is_autoload_pack(PackDir, Options), 2294 !, 2295 directory_file_path(PackDir, prolog, PrologLibDir), 2296 make_library_index(PrologLibDir). 2297post_install_autoload(_, _). 2298 2299is_autoload_pack(PackDir, Options) :- 2300 option(autoload(true), Options, true), 2301 pack_info_term(PackDir, autoload(true)). 2302 2303 2304 /******************************* 2305 * UPGRADE * 2306 *******************************/
pack_install(Pack, [upgrade(true)])
.2312pack_upgrade(Pack) :- 2313 pack_install(Pack, [upgrade(true)]). 2314 2315 2316 /******************************* 2317 * REMOVE * 2318 *******************************/
true
delete dependencies without asking.2331pack_remove(Pack) :- 2332 pack_remove(Pack, []). 2333 2334pack_remove(Pack, Options) :- 2335 option(dependencies(false), Options), 2336 !, 2337 pack_remove_forced(Pack). 2338pack_remove(Pack, Options) :- 2339 ( dependents(Pack, Deps) 2340 -> ( option(dependencies(true), Options) 2341 -> true 2342 ; confirm_remove(Pack, Deps, Delete, Options) 2343 ), 2344 forall(member(P, Delete), pack_remove_forced(P)) 2345 ; pack_remove_forced(Pack) 2346 ). 2347 2348pack_remove_forced(Pack) :- 2349 catch('$pack_detach'(Pack, BaseDir), 2350 error(existence_error(pack, Pack), _), 2351 fail), 2352 !, 2353 print_message(informational, pack(remove(BaseDir))), 2354 delete_directory_and_contents(BaseDir). 2355pack_remove_forced(Pack) :- 2356 unattached_pack(Pack, BaseDir), 2357 !, 2358 delete_directory_and_contents(BaseDir). 2359pack_remove_forced(Pack) :- 2360 print_message(informational, error(existence_error(pack, Pack),_)). 2361 2362confirm_remove(Pack, Deps, Delete, Options) :- 2363 print_message(warning, pack(depends(Pack, Deps))), 2364 menu(pack(resolve_remove), 2365 [ [Pack] = remove_only(Pack), 2366 [Pack|Deps] = remove_deps(Pack, Deps), 2367 [] = cancel 2368 ], [], Delete, Options), 2369 Delete \== []. 2370 2371 2372 /******************************* 2373 * PUBLISH * 2374 *******************************/
?- 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 first2427pack_publish(Dir, Options) :- 2428 \+ download_url(Dir), 2429 is_git_directory(Dir), !, 2430 pack_git_info(Dir, _Hash, Metadata), 2431 prepare_repository(Dir, Metadata, Options), 2432 ( memberchk(download(URL), Metadata), 2433 git_url(URL, _) 2434 -> true 2435 ; option(remote(Remote), Options, origin), 2436 git_remote_url(Remote, RemoteURL, [directory(Dir)]), 2437 git_to_https_url(RemoteURL, URL) 2438 ), 2439 memberchk(version(Version), Metadata), 2440 pack_publish_(URL, 2441 [ version(Version) 2442 | Options 2443 ]). 2444pack_publish(Spec, Options) :- 2445 pack_publish_(Spec, Options). 2446 2447pack_publish_(Spec, Options) :- 2448 pack_default_options(Spec, Pack, Options, DefOptions), 2449 option(url(URL), DefOptions), 2450 valid_publish_url(URL, Options), 2451 prepare_build_location(Pack, Dir, Clean, Options), 2452 ( option(register(false), Options) 2453 -> InstallOptions = DefOptions 2454 ; InstallOptions = [publish(Pack)|DefOptions] 2455 ), 2456 call_cleanup(pack_install(Pack, 2457 [ pack(Pack) 2458 | InstallOptions 2459 ]), 2460 cleanup_publish(Clean, Dir)). 2461 2462cleanup_publish(true, Dir) :- 2463 !, 2464 delete_directory_and_contents(Dir). 2465cleanup_publish(_, _). 2466 2467valid_publish_url(URL, Options) :- 2468 option(register(Register), Options, true), 2469 ( Register == false 2470 -> true 2471 ; download_url(URL) 2472 -> true 2473 ; permission_error(publish, pack, URL) 2474 ). 2475 2476prepare_build_location(Pack, Dir, Clean, Options) :- 2477 ( option(pack_directory(Dir), Options) 2478 -> ensure_directory(Dir), 2479 ( option(clean(true), Options, true) 2480 -> delete_directory_contents(Dir) 2481 ; true 2482 ) 2483 ; tmp_file(pack, Dir), 2484 make_directory(Dir), 2485 Clean = true 2486 ), 2487 ( option(isolated(false), Options) 2488 -> detach_pack(Pack, _), 2489 attach_packs(Dir, [search(first)]) 2490 ; attach_packs(Dir, [replace(true)]) 2491 ).
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.2502prepare_repository(_Dir, _Metadata, Options) :- 2503 option(register(false), Options), 2504 !. 2505prepare_repository(Dir, Metadata, Options) :- 2506 git_dir_must_be_clean(Dir), 2507 git_must_be_on_default_branch(Dir, Options), 2508 tag_git_dir(Dir, Metadata, Action, Options), 2509 confirm(git_push, yes, Options), 2510 run_process(path(git), ['-C', file(Dir), push ], []), 2511 ( Action = push_tag(Tag) 2512 -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], []) 2513 ; true 2514 ). 2515 2516git_dir_must_be_clean(Dir) :- 2517 git_describe(Description, [directory(Dir)]), 2518 ( sub_atom(Description, _, _, 0, '-DIRTY') 2519 -> print_message(error, pack(git_not_clean(Dir))), 2520 fail 2521 ; true 2522 ). 2523 2524git_must_be_on_default_branch(Dir, Options) :- 2525 ( option(branch(Default), Options) 2526 -> true 2527 ; git_default_branch(Default, [directory(Dir)]) 2528 ), 2529 git_current_branch(Current, [directory(Dir)]), 2530 ( Default == Current 2531 -> true 2532 ; print_message(error, 2533 pack(git_branch_not_default(Dir, Default, Current))), 2534 fail 2535 ).
2544tag_git_dir(Dir, Metadata, Action, Options) :- 2545 memberchk(version(Version), Metadata), 2546 atom_concat('V', Version, Tag), 2547 git_tags(Tags, [directory(Dir)]), 2548 ( memberchk(Tag, Tags) 2549 -> git_tag_is_consistent(Dir, Tag, Action, Options) 2550 ; format(string(Message), 'Release ~w', [Version]), 2551 findall(Opt, git_tag_option(Opt, Options), Argv, 2552 [ '-m', Message, Tag ]), 2553 confirm(git_tag(Tag), yes, Options), 2554 run_process(path(git), ['-C', file(Dir), tag | Argv ], []), 2555 Action = push_tag(Tag) 2556 ). 2557 2558git_tag_option('-s', Options) :- option(sign(true), Options, true). 2559git_tag_option('-f', Options) :- option(force(true), Options, true). 2560 2561git_tag_is_consistent(Dir, Tag, Action, Options) :- 2562 format(atom(TagRef), 'refs/tags/~w', [Tag]), 2563 format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]), 2564 option(remote(Remote), Options, origin), 2565 git_ls_remote(Dir, LocalTags, [tags(true)]), 2566 memberchk(CommitHash-CommitRef, LocalTags), 2567 ( git_hash(CommitHash, [directory(Dir)]) 2568 -> true 2569 ; print_message(error, pack(git_release_tag_not_at_head(Tag))), 2570 fail 2571 ), 2572 memberchk(TagHash-TagRef, LocalTags), 2573 git_ls_remote(Remote, RemoteTags, [tags(true)]), 2574 ( memberchk(RemoteCommitHash-CommitRef, RemoteTags), 2575 memberchk(RemoteTagHash-TagRef, RemoteTags) 2576 -> ( RemoteCommitHash == CommitHash, 2577 RemoteTagHash == TagHash 2578 -> Action = none 2579 ; print_message(error, pack(git_tag_out_of_sync(Tag))), 2580 fail 2581 ) 2582 ; Action = push_tag(Tag) 2583 ).
2591git_to_https_url(URL, URL) :- 2592 download_url(URL), 2593 !. 2594git_to_https_url(GitURL, URL) :- 2595 atom_concat('git@github.com:', Repo, GitURL), 2596 !, 2597 atom_concat('https://github.com/', Repo, URL). 2598git_to_https_url(GitURL, _) :- 2599 print_message(error, pack(git_no_https(GitURL))), 2600 fail. 2601 2602 2603 /******************************* 2604 * PROPERTIES * 2605 *******************************/
README
file (if present)TODO
file (if present)2628pack_property(Pack, Property) :- 2629 findall(Pack-Property, pack_property_(Pack, Property), List), 2630 member(Pack-Property, List). % make det if applicable 2631 2632pack_property_(Pack, Property) :- 2633 pack_info(Pack, _, Property). 2634pack_property_(Pack, Property) :- 2635 \+ \+ info_file(Property, _), 2636 '$pack':pack(Pack, BaseDir), 2637 access_file(BaseDir, read), 2638 directory_files(BaseDir, Files), 2639 member(File, Files), 2640 info_file(Property, Pattern), 2641 downcase_atom(File, Pattern), 2642 directory_file_path(BaseDir, File, InfoFile), 2643 arg(1, Property, InfoFile). 2644 2645info_file(readme(_), 'readme.txt'). 2646info_file(readme(_), 'readme'). 2647info_file(todo(_), 'todo.txt'). 2648info_file(todo(_), 'todo'). 2649 2650 2651 /******************************* 2652 * VERSION LOGIC * 2653 *******************************/
mypack-1.5
.2662pack_version_file(Pack, Version, GitHubRelease) :- 2663 atomic(GitHubRelease), 2664 github_release_url(GitHubRelease, Pack, Version), 2665 !. 2666pack_version_file(Pack, Version, Path) :- 2667 atomic(Path), 2668 file_base_name(Path, File), 2669 no_int_file_name_extension(Base, _Ext, File), 2670 atom_codes(Base, Codes), 2671 ( phrase(pack_version(Pack, Version), Codes), 2672 safe_pack_name(Pack) 2673 -> true 2674 ). 2675 2676no_int_file_name_extension(Base, Ext, File) :- 2677 file_name_extension(Base0, Ext0, File), 2678 \+ atom_number(Ext0, _), 2679 !, 2680 Base = Base0, 2681 Ext = Ext0. 2682no_int_file_name_extension(File, '', File).
2689safe_pack_name(Name) :- 2690 atom_length(Name, Len), 2691 Len >= 3, % demand at least three length 2692 atom_codes(Name, Codes), 2693 maplist(safe_pack_char, Codes), 2694 !. 2695 2696safe_pack_char(C) :- between(0'a, 0'z, C), !. 2697safe_pack_char(C) :- between(0'A, 0'Z, C), !. 2698safe_pack_char(C) :- between(0'0, 0'9, C), !. 2699safe_pack_char(0'_).
2705pack_version(Pack, Version) --> 2706 string(Codes), "-", 2707 version(Parts), 2708 !, 2709 { atom_codes(Pack, Codes), 2710 atomic_list_concat(Parts, '.', Version) 2711 }. 2712 2713version([H|T]) --> 2714 version_part(H), 2715 ( "." 2716 -> version(T) 2717 ; {T=[]} 2718 ). 2719 2720version_part(*) --> "*", !. 2721version_part(Int) --> integer(Int). 2722 2723 2724 /******************************* 2725 * GIT LOGIC * 2726 *******************************/ 2727 2728have_git :- 2729 process_which(path(git), _).
2736git_url(URL, Pack) :- 2737 uri_components(URL, Components), 2738 uri_data(scheme, Components, Scheme), 2739 nonvar(Scheme), % must be full URL 2740 uri_data(path, Components, Path), 2741 ( Scheme == git 2742 -> true 2743 ; git_download_scheme(Scheme), 2744 file_name_extension(_, git, Path) 2745 ; git_download_scheme(Scheme), 2746 catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail) 2747 -> true 2748 ), 2749 file_base_name(Path, PackExt), 2750 ( file_name_extension(Pack, git, PackExt) 2751 -> true 2752 ; Pack = PackExt 2753 ), 2754 ( safe_pack_name(Pack) 2755 -> true 2756 ; domain_error(pack_name, Pack) 2757 ). 2758 2759git_download_scheme(http). 2760git_download_scheme(https).
https:/github.com/<owner>/<pack>/archive/[vV]?<version>.zip'
2769github_release_url(URL, Pack, Version) :- 2770 uri_components(URL, Components), 2771 uri_data(authority, Components, 'github.com'), 2772 uri_data(scheme, Components, Scheme), 2773 download_scheme(Scheme), 2774 uri_data(path, Components, Path), 2775 github_archive_path(Archive,Pack,File), 2776 atomic_list_concat(Archive, /, Path), 2777 file_name_extension(Tag, Ext, File), 2778 github_archive_extension(Ext), 2779 tag_version(Tag, Version), 2780 !. 2781 2782github_archive_path(['',_User,Pack,archive,File],Pack,File). 2783github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File). 2784 2785github_archive_extension(tgz). 2786github_archive_extension(zip).
[vV]?int(\.int)*
.2793tag_version(Tag, Version) :- 2794 version_tag_prefix(Prefix), 2795 atom_concat(Prefix, Version, Tag), 2796 is_version(Version). 2797 2798version_tag_prefix(v). 2799version_tag_prefix('V'). 2800version_tag_prefix('').
2809git_archive_url(URL, Archive, Options) :- 2810 uri_components(URL, Components), 2811 uri_data(authority, Components, 'github.com'), 2812 uri_data(path, Components, Path), 2813 atomic_list_concat(['', User, RepoGit], /, Path), 2814 $, 2815 remove_git_ext(RepoGit, Repo), 2816 git_archive_version(Version, Options), 2817 atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath), 2818 uri_edit([ path(ArchivePath), 2819 host('codeload.github.com') 2820 ], 2821 URL, Archive). 2822git_archive_url(URL, _, _) :- 2823 print_message(error, pack(no_git(URL))), 2824 fail. 2825 2826remove_git_ext(RepoGit, Repo) :- 2827 file_name_extension(Repo, git, RepoGit), 2828 !. 2829remove_git_ext(Repo, Repo). 2830 2831git_archive_version(Version, Options) :- 2832 option(commit(Version), Options), 2833 !. 2834git_archive_version(Version, Options) :- 2835 option(branch(Version), Options), 2836 !. 2837git_archive_version(Version, Options) :- 2838 option(version(Version), Options), 2839 !. 2840git_archive_version('HEAD', _). 2841 2842 /******************************* 2843 * QUERY CENTRAL DB * 2844 *******************************/
2851register_downloads(_, Options) :- 2852 option(register(false), Options), 2853 \+ option(do_publish(_), Options), 2854 !. 2855register_downloads(Infos, Options) :- 2856 convlist(download_data, Infos, Data), 2857 ( Data == [] 2858 -> true 2859 ; query_pack_server(downloaded(Data), Reply, Options), 2860 ( option(do_publish(Pack), Options) 2861 -> ( member(Info, Infos), 2862 Info.pack == Pack 2863 -> true 2864 ), 2865 ( Reply = true(Actions), 2866 memberchk(Pack-Result, Actions) 2867 -> ( registered(Result) 2868 -> true 2869 ; print_message(error, pack(publish_failed(Info, Result))), 2870 fail 2871 ) 2872 ; print_message(error, pack(publish_failed(Info, false))) 2873 ) 2874 ; true 2875 ) 2876 ). 2877 2878registered(git(_URL)). 2879registered(file(_URL)). 2880 2881publish_download(Infos, Options) :- 2882 select_option(publish(Pack), Options, Options1), 2883 !, 2884 register_downloads(Infos, [do_publish(Pack)|Options1]). 2885publish_download(_Infos, _Options). 2886 2887download_data(Info, Data), 2888 Info.get(git) == true => % Git clone 2889 Data = download(URL, Hash, Metadata), 2890 URL = Info.get(downloaded), 2891 pack_git_info(Info.installed, Hash, Metadata). 2892download_data(Info, Data), 2893 _{git_url:URL,hash:Hash} :< Info, Hash \== (-) => 2894 Data = download(URL, Hash, Metadata), % Git downloaded as zip 2895 dir_metadata(Info.installed, Metadata). 2896download_data(Info, Data) => % Archive download. 2897 Data = download(URL, Hash, Metadata), 2898 URL = Info.get(downloaded), 2899 download_url(URL), 2900 pack_status_dir(Info.installed, archive(Archive, URL)), 2901 file_sha1(Archive, Hash), 2902 pack_archive_info(Archive, _Pack, Metadata, _).
2909query_pack_server(Query, Result, Options) :- 2910 ( option(server(ServerOpt), Options) 2911 -> server_url(ServerOpt, ServerBase) 2912 ; setting(server, ServerBase), 2913 ServerBase \== '' 2914 ), 2915 atom_concat(ServerBase, query, Server), 2916 format(codes(Data), '~q.~n', Query), 2917 info_level(Informational, Options), 2918 print_message(Informational, pack(contacting_server(Server))), 2919 setup_call_cleanup( 2920 http_open(Server, In, 2921 [ post(codes(application/'x-prolog', Data)), 2922 header(content_type, ContentType) 2923 ]), 2924 read_reply(ContentType, In, Result), 2925 close(In)), 2926 message_severity(Result, Level, Informational), 2927 print_message(Level, pack(server_reply(Result))). 2928 2929server_url(URL0, URL) :- 2930 uri_components(URL0, Components), 2931 uri_data(scheme, Components, Scheme), 2932 var(Scheme), 2933 !, 2934 atom_concat('https://', URL0, URL1), 2935 server_url(URL1, URL). 2936server_url(URL0, URL) :- 2937 uri_components(URL0, Components), 2938 uri_data(path, Components, ''), 2939 !, 2940 uri_edit([path('/pack/')], URL0, URL). 2941server_url(URL, URL). 2942 2943read_reply(ContentType, In, Result) :- 2944 sub_atom(ContentType, 0, _, _, 'application/x-prolog'), 2945 !, 2946 set_stream(In, encoding(utf8)), 2947 read(In, Result). 2948read_reply(ContentType, In, _Result) :- 2949 read_string(In, 500, String), 2950 print_message(error, pack(no_prolog_response(ContentType, String))), 2951 fail. 2952 2953info_level(Level, Options) :- 2954 option(silent(true), Options), 2955 !, 2956 Level = silent. 2957info_level(informational, _). 2958 2959message_severity(true(_), Informational, Informational). 2960message_severity(false, warning, _). 2961message_severity(exception(_), error, _). 2962 2963 2964 /******************************* 2965 * WILDCARD URIs * 2966 *******************************/
2975available_download_versions(URL, Versions) :- 2976 wildcard_pattern(URL), 2977 github_url(URL, User, Repo), 2978 !, 2979 findall(Version-VersionURL, 2980 github_version(User, Repo, Version, VersionURL), 2981 Versions). 2982available_download_versions(URL, Versions) :- 2983 wildcard_pattern(URL), 2984 !, 2985 file_directory_name(URL, DirURL0), 2986 ensure_slash(DirURL0, DirURL), 2987 print_message(informational, pack(query_versions(DirURL))), 2988 setup_call_cleanup( 2989 http_open(DirURL, In, []), 2990 load_html(stream(In), DOM, 2991 [ syntax_errors(quiet) 2992 ]), 2993 close(In)), 2994 findall(MatchingURL, 2995 absolute_matching_href(DOM, URL, MatchingURL), 2996 MatchingURLs), 2997 ( MatchingURLs == [] 2998 -> print_message(warning, pack(no_matching_urls(URL))) 2999 ; true 3000 ), 3001 versioned_urls(MatchingURLs, VersionedURLs), 3002 sort_version_pairs(VersionedURLs, Versions), 3003 print_message(informational, pack(found_versions(Versions))). 3004available_download_versions(URL, [Version-URL]) :- 3005 ( pack_version_file(_Pack, Version0, URL) 3006 -> Version = Version0 3007 ; Version = '0.0.0' 3008 ).
3014sort_version_pairs(Pairs, Sorted) :- 3015 map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed), 3016 sort(1, @>=, Keyed, SortedKeyed), 3017 pairs_values(SortedKeyed, Sorted). 3018 3019version_pair_sort_key_(Version-_Data, Key) :- 3020 version_sort_key(Version, Key). 3021 3022version_sort_key(Version, Key) :- 3023 split_string(Version, ".", "", Parts), 3024 maplist(number_string, Key, Parts), 3025 !. 3026version_sort_key(Version, _) :- 3027 domain_error(version, Version).
3033github_url(URL, User, Repo) :-
3034 uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3035 atomic_list_concat(['',User,Repo|_], /, Path).
3043github_version(User, Repo, Version, VersionURI) :- 3044 atomic_list_concat(['',repos,User,Repo,tags], /, Path1), 3045 uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)), 3046 setup_call_cleanup( 3047 http_open(ApiUri, In, 3048 [ request_header('Accept'='application/vnd.github.v3+json') 3049 ]), 3050 json_read_dict(In, Dicts), 3051 close(In)), 3052 member(Dict, Dicts), 3053 atom_string(Tag, Dict.name), 3054 tag_version(Tag, Version), 3055 atom_string(VersionURI, Dict.zipball_url). 3056 3057wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *). 3058wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?). 3059 3060ensure_slash(Dir, DirS) :- 3061 ( sub_atom(Dir, _, _, 0, /) 3062 -> DirS = Dir 3063 ; atom_concat(Dir, /, DirS) 3064 ). 3065 3066remove_slash(Dir0, Dir) :- 3067 Dir0 \== '/', 3068 atom_concat(Dir1, /, Dir0), 3069 !, 3070 remove_slash(Dir1, Dir). 3071remove_slash(Dir, Dir). 3072 3073absolute_matching_href(DOM, Pattern, Match) :- 3074 xpath(DOM, //a(@href), HREF), 3075 uri_normalized(HREF, Pattern, Match), 3076 wildcard_match(Pattern, Match). 3077 3078versioned_urls([], []). 3079versioned_urls([H|T0], List) :- 3080 file_base_name(H, File), 3081 ( pack_version_file(_Pack, Version, File) 3082 -> List = [Version-H|T] 3083 ; List = T 3084 ), 3085 versioned_urls(T0, T). 3086 3087 3088 /******************************* 3089 * DEPENDENCIES * 3090 *******************************/
3098pack_provides(Pack, Pack@Version) :- 3099 current_pack(Pack), 3100 once(pack_info(Pack, version, version(Version))). 3101pack_provides(Pack, Provides) :- 3102 findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList), 3103 member(Provides, PrvList). 3104 3105pack_requires(Pack, Requires) :- 3106 current_pack(Pack), 3107 findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList), 3108 member(Requires, ReqList). 3109 3110pack_conflicts(Pack, Conflicts) :- 3111 current_pack(Pack), 3112 findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList), 3113 member(Conflicts, CflList).
3120pack_depends_on(Pack, Dependency) :- 3121 ground(Pack), 3122 !, 3123 pack_requires(Pack, Requires), 3124 \+ is_prolog_token(Requires), 3125 pack_provides(Dependency, Provides), 3126 satisfies_req(Provides, Requires). 3127pack_depends_on(Pack, Dependency) :- 3128 ground(Dependency), 3129 !, 3130 pack_provides(Dependency, Provides), 3131 pack_requires(Pack, Requires), 3132 satisfies_req(Provides, Requires). 3133pack_depends_on(Pack, Dependency) :- 3134 current_pack(Pack), 3135 pack_depends_on(Pack, Dependency).
3142dependents(Pack, Deps) :- 3143 setof(Dep, dependent(Pack, Dep, []), Deps). 3144 3145dependent(Pack, Dep, Seen) :- 3146 pack_depends_on(Dep0, Pack), 3147 \+ memberchk(Dep0, Seen), 3148 ( Dep = Dep0 3149 ; dependent(Dep0, Dep, [Dep0|Seen]) 3150 ).
3156validate_dependencies :- 3157 setof(Issue, pack_dependency_issue(_, Issue), Issues), 3158 !, 3159 print_message(warning, pack(dependency_issues(Issues))). 3160validate_dependencies.
3172pack_dependency_issue(Pack, Issue) :- 3173 current_pack(Pack), 3174 pack_dependency_issue_(Pack, Issue). 3175 3176pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :- 3177 pack_requires(Pack, Requires), 3178 ( is_prolog_token(Requires) 3179 -> \+ prolog_satisfies(Requires) 3180 ; \+ ( pack_provides(_, Provides), 3181 satisfies_req(Provides, Requires) ) 3182 ). 3183pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :- 3184 pack_conflicts(Pack, Conflicts), 3185 ( is_prolog_token(Conflicts) 3186 -> prolog_satisfies(Conflicts) 3187 ; pack_provides(_, Provides), 3188 satisfies_req(Provides, Conflicts) 3189 ). 3190 3191 3192 /******************************* 3193 * RECORD PACK FACTS * 3194 *******************************/
built
if we built it or downloaded
if it was downloaded.true
, pack was installed as dependency.3210pack_assert(PackDir, Fact) :- 3211 must_be(ground, Fact), 3212 findall(Term, pack_status_dir(PackDir, Term), Facts0), 3213 update_facts(Facts0, Fact, Facts), 3214 OpenOptions = [encoding(utf8), lock(exclusive)], 3215 status_file(PackDir, StatusFile), 3216 ( Facts == Facts0 3217 -> true 3218 ; Facts0 \== [], 3219 append(Facts0, New, Facts) 3220 -> setup_call_cleanup( 3221 open(StatusFile, append, Out, OpenOptions), 3222 maplist(write_fact(Out), New), 3223 close(Out)) 3224 ; setup_call_cleanup( 3225 open(StatusFile, write, Out, OpenOptions), 3226 ( write_facts_header(Out), 3227 maplist(write_fact(Out), Facts) 3228 ), 3229 close(Out)) 3230 ). 3231 3232update_facts([], Fact, [Fact]) :- 3233 !. 3234update_facts([H|T], Fact, [Fact|T]) :- 3235 general_pack_fact(Fact, GenFact), 3236 general_pack_fact(H, GenTerm), 3237 GenFact =@= GenTerm, 3238 !. 3239update_facts([H|T0], Fact, [H|T]) :- 3240 update_facts(T0, Fact, T). 3241 3242general_pack_fact(built(Arch, _Version, _How), General) => 3243 General = built(Arch, _, _). 3244general_pack_fact(Term, General), compound(Term) => 3245 compound_name_arity(Term, Name, Arity), 3246 compound_name_arity(General, Name, Arity). 3247general_pack_fact(Term, General) => 3248 General = Term. 3249 3250write_facts_header(Out) :- 3251 format(Out, '% Fact status file. Managed by package manager.~n', []). 3252 3253write_fact(Out, Term) :- 3254 format(Out, '~q.~n', [Term]).
status.db
.3262pack_status(Pack, Fact) :- 3263 current_pack(Pack, PackDir), 3264 pack_status_dir(PackDir, Fact). 3265 3266pack_status_dir(PackDir, Fact) :- 3267 det_if(ground(Fact), pack_status_(PackDir, Fact)). 3268 3269pack_status_(PackDir, Fact) :- 3270 status_file(PackDir, StatusFile), 3271 catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact), 3272 error(existence_error(source_sink, StatusFile), _), 3273 fail). 3274 3275pack_status_term(built(atom, version, oneof([built,downloaded]))). 3276pack_status_term(automatic(boolean)). 3277pack_status_term(archive(atom, atom)).
3287update_automatic(Info) :- 3288 _ = Info.get(dependency_for), 3289 \+ pack_status(Info.installed, automatic(_)), 3290 !, 3291 pack_assert(Info.installed, automatic(true)). 3292update_automatic(Info) :- 3293 pack_assert(Info.installed, automatic(false)). 3294 3295status_file(PackDir, StatusFile) :- 3296 directory_file_path(PackDir, 'status.db', StatusFile). 3297 3298 /******************************* 3299 * USER INTERACTION * 3300 *******************************/ 3301 3302:- multifile prolog:message//1.
3306menu(_Question, _Alternatives, Default, Selection, Options) :- 3307 option(interactive(false), Options), 3308 !, 3309 Selection = Default. 3310menu(Question, Alternatives, Default, Selection, _) :- 3311 length(Alternatives, N), 3312 between(1, 5, _), 3313 print_message(query, Question), 3314 print_menu(Alternatives, Default, 1), 3315 print_message(query, pack(menu(select))), 3316 read_selection(N, Choice), 3317 !, 3318 ( Choice == default 3319 -> Selection = Default 3320 ; nth1(Choice, Alternatives, Selection=_) 3321 -> true 3322 ). 3323 [], _, _) (. 3325print_menu([Value=Label|T], Default, I) :- 3326 ( Value == Default 3327 -> print_message(query, pack(menu(default_item(I, Label)))) 3328 ; print_message(query, pack(menu(item(I, Label)))) 3329 ), 3330 I2 is I + 1, 3331 print_menu(T, Default, I2). 3332 3333read_selection(Max, Choice) :- 3334 get_single_char(Code), 3335 ( answered_default(Code) 3336 -> Choice = default 3337 ; code_type(Code, digit(Choice)), 3338 between(1, Max, Choice) 3339 -> true 3340 ; print_message(warning, pack(menu(reply(1,Max)))), 3341 fail 3342 ).
3350confirm(_Question, Default, Options) :- 3351 Default \== none, 3352 option(interactive(false), Options, true), 3353 !, 3354 Default == yes. 3355confirm(Question, Default, _) :- 3356 between(1, 5, _), 3357 print_message(query, pack(confirm(Question, Default))), 3358 read_yes_no(YesNo, Default), 3359 !, 3360 format(user_error, '~N', []), 3361 YesNo == yes. 3362 3363read_yes_no(YesNo, Default) :- 3364 get_single_char(Code), 3365 code_yes_no(Code, Default, YesNo), 3366 !. 3367 3368code_yes_no(0'y, _, yes). 3369code_yes_no(0'Y, _, yes). 3370code_yes_no(0'n, _, no). 3371code_yes_no(0'N, _, no). 3372code_yes_no(_, none, _) :- !, fail. 3373code_yes_no(C, Default, Default) :- 3374 answered_default(C). 3375 3376answered_default(0'\r). 3377answered_default(0'\n). 3378answered_default(0'\s). 3379 3380 3381 /******************************* 3382 * MESSAGES * 3383 *******************************/ 3384 3385:- multifile prolog:message//1. 3386 3387prologmessage(pack(Message)) --> 3388 message(Message). 3389 3390:- discontiguous 3391 message//1, 3392 label//1. 3393 3394message(invalid_term(pack_info_term, Term)) --> 3395 [ 'Invalid package meta data: ~q'-[Term] ]. 3396message(invalid_term(pack_status_term, Term)) --> 3397 [ 'Invalid package status data: ~q'-[Term] ]. 3398message(directory_exists(Dir)) --> 3399 [ 'Package target directory exists and is not empty:', nl, 3400 '\t~q'-[Dir] 3401 ]. 3402message(already_installed(pack(Pack, Version))) --> 3403 [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ]. 3404message(already_installed(Pack)) --> 3405 [ 'Pack `~w'' is already installed. Package info:'-[Pack] ]. 3406message(kept_foreign(Pack, Arch)) --> 3407 [ 'Found foreign libraries for architecture '-[], 3408 ansi(code, '~q', [Arch]), nl, 3409 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]), 3410 ' to rebuild from sources'-[] 3411 ]. 3412message(no_pack_installed(Pack)) --> 3413 [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ]. 3414message(dependency_issues(Issues)) --> 3415 [ 'The current set of packs has dependency issues:', nl ], 3416 dep_issues(Issues). 3417message(depends(Pack, Deps)) --> 3418 [ 'The following packs depend on `~w\':'-[Pack], nl ], 3419 pack_list(Deps). 3420message(remove(PackDir)) --> 3421 [ 'Removing ~q and contents'-[PackDir] ]. 3422message(remove_existing_pack(PackDir)) --> 3423 [ 'Remove old installation in ~q'-[PackDir] ]. 3424message(download_plan(Plan)) --> 3425 [ ansi(bold, 'Installation plan:', []), nl ], 3426 install_plan(Plan, Actions), 3427 install_label(Actions). 3428message(build_plan(Plan)) --> 3429 [ ansi(bold, 'The following packs have post install scripts:', []), nl ], 3430 msg_build_plan(Plan), 3431 [ nl, ansi(bold, 'Run scripts?', []) ]. 3432message(no_meta_data(BaseDir)) --> 3433 [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ]. 3434message(search_no_matches(Name)) --> 3435 [ 'Search for "~w", returned no matching packages'-[Name] ]. 3436message(rebuild(Pack)) --> 3437 [ 'Checking pack "~w" for rebuild ...'-[Pack] ]. 3438message(up_to_date([Pack])) --> 3439 !, 3440 [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ]. 3441message(up_to_date(Packs)) --> 3442 [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ]. 3443message(installed_can_upgrade(List)) --> 3444 sequence(msg_can_upgrade_target, [nl], List). 3445message(new_dependencies(Deps)) --> 3446 [ 'Found new dependencies after downloading (~p).'-[Deps], nl ]. 3447message(query_versions(URL)) --> 3448 [ 'Querying "~w" to find new versions ...'-[URL] ]. 3449message(no_matching_urls(URL)) --> 3450 [ 'Could not find any matching URL: ~q'-[URL] ]. 3451message(found_versions([Latest-_URL|More])) --> 3452 { length(More, Len) }, 3453 [ ' Latest version: ~w (~D older)'-[Latest, Len] ]. 3454message(build(Pack, PackDir)) --> 3455 [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ]. 3456message(contacting_server(Server)) --> 3457 [ 'Contacting server at ~w ...'-[Server], flush ]. 3458message(server_reply(true(_))) --> 3459 [ at_same_line, ' ok'-[] ]. 3460message(server_reply(false)) --> 3461 [ at_same_line, ' done'-[] ]. 3462message(server_reply(exception(E))) --> 3463 [ 'Server reported the following error:'-[], nl ], 3464 '$messages':translate_message(E). 3465message(cannot_create_dir(Alias)) --> 3466 { findall(PackDir, 3467 absolute_file_name(Alias, PackDir, [solutions(all)]), 3468 PackDirs0), 3469 sort(PackDirs0, PackDirs) 3470 }, 3471 [ 'Cannot find a place to create a package directory.'-[], 3472 'Considered:'-[] 3473 ], 3474 candidate_dirs(PackDirs). 3475message(conflict(version, [PackV, FileV])) --> 3476 ['Version mismatch: pack.pl: '-[]], msg_version(PackV), 3477 [', file claims version '-[]], msg_version(FileV). 3478message(conflict(name, [PackInfo, FileInfo])) --> 3479 ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]], 3480 [', file claims ~w: ~p'-[FileInfo]]. 3481message(no_prolog_response(ContentType, String)) --> 3482 [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl, 3483 '~s'-[String] 3484 ]. 3485message(download(begin, Pack, _URL, _DownloadFile)) --> 3486 [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ]. 3487message(download(end, _, _, File)) --> 3488 { size_file(File, Bytes) }, 3489 [ at_same_line, '~D bytes'-[Bytes] ]. 3490message(no_git(URL)) --> 3491 [ 'Cannot install from git repository ', url(URL), '.', nl, 3492 'Cannot find git program and do not know how to download the code', nl, 3493 'from this git service. Please install git and retry.' 3494 ]. 3495message(git_no_https(GitURL)) --> 3496 [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ]. 3497message(git_branch_not_default(Dir, Default, Current)) --> 3498 [ 'GIT current branch on ', url(Dir), ' is not default.', nl, 3499 ' Current branch: ', ansi(code, '~w', [Current]), 3500 ' default: ', ansi(code, '~w', [Default]) 3501 ]. 3502message(git_not_clean(Dir)) --> 3503 [ 'GIT working directory is dirty: ', url(Dir), nl, 3504 'Your repository must be clean before publishing.' 3505 ]. 3506message(git_push) --> 3507 [ 'Push release to GIT origin?' ]. 3508message(git_tag(Tag)) --> 3509 [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ]. 3510message(git_release_tag_not_at_head(Tag)) --> 3511 [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl, 3512 'If you want to update the tag, please run ', 3513 ansi(code, 'git tag -d ~w', [Tag]) 3514 ]. 3515message(git_tag_out_of_sync(Tag)) --> 3516 [ 'Release tag ', ansi(code, '~w', [Tag]), 3517 ' differs from this tag at the origin' 3518 ]. 3519 3520message(publish_failed(Info, Reason)) --> 3521 [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ], 3522 msg_publish_failed(Reason). 3523 3524msg_publish_failed(throw(error(permission_error(register, 3525 pack(_),_URL),_))) --> 3526 [ ' is already registered with a different URL']. 3527msg_publish_failed(download) --> 3528 [' was already published?']. 3529msg_publish_failed(Status) --> 3530 [ ' failed for unknown reason (~p)'-[Status] ]. 3531 3532candidate_dirs([]) --> []. 3533candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T). 3534 % Questions 3535message(resolve_remove) --> 3536 [ nl, 'Please select an action:', nl, nl ]. 3537message(create_pack_dir) --> 3538 [ nl, 'Create directory for packages', nl ]. 3539message(menu(item(I, Label))) --> 3540 [ '~t(~d)~6| '-[I] ], 3541 label(Label). 3542message(menu(default_item(I, Label))) --> 3543 [ '~t(~d)~6| * '-[I] ], 3544 label(Label). 3545message(menu(select)) --> 3546 [ nl, 'Your choice? ', flush ]. 3547message(confirm(Question, Default)) --> 3548 message(Question), 3549 confirm_default(Default), 3550 [ flush ]. 3551message(menu(reply(Min,Max))) --> 3552 ( { Max =:= Min+1 } 3553 -> [ 'Please enter ~w or ~w'-[Min,Max] ] 3554 ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ] 3555 ). 3556 3557 % support predicates 3558dep_issues(Issues) --> 3559 sequence(dep_issue, [nl], Issues). 3560 3561dep_issue(unsatisfied(Pack, Requires)) --> 3562 [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]]. 3563dep_issue(conflicts(Pack, Conflict)) --> 3564 [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3571install_label([link]) --> 3572 !, 3573 [ ansi(bold, 'Activate pack?', []) ]. 3574install_label([unpack]) --> 3575 !, 3576 [ ansi(bold, 'Unpack archive?', []) ]. 3577install_label(_) --> 3578 [ ansi(bold, 'Download packs?', []) ]. 3579 3580install_plan([], []) --> 3581 []. 3582install_plan([H|T], [AH|AT]) --> 3583 install_step(H, AH), [nl], 3584 install_plan(T, AT). 3585 3586install_step(Info, keep) --> 3587 { Info.get(keep) == true }, 3588 !, 3589 [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ], 3590 msg_can_upgrade(Info). 3591install_step(Info, Action) --> 3592 { From = Info.get(upgrade), 3593 VFrom = From.version, 3594 VTo = Info.get(version), 3595 ( cmp_versions(>=, VTo, VFrom) 3596 -> Label = ansi(bold, ' Upgrade ', []) 3597 ; Label = ansi(warning, ' Downgrade ', []) 3598 ) 3599 }, 3600 [ Label ], msg_pack(Info), 3601 [ ' from version ~w to ~w'- [From.version, Info.get(version)] ], 3602 install_from(Info, Action). 3603install_step(Info, Action) --> 3604 { _From = Info.get(upgrade) }, 3605 [ ' Upgrade ' ], msg_pack(Info), 3606 install_from(Info, Action). 3607install_step(Info, Action) --> 3608 { Dep = Info.get(dependency_for) }, 3609 [ ' Install ' ], msg_pack(Info), 3610 [ ' at version ~w as dependency for '-[Info.version], 3611 ansi(code, '~w', [Dep]) 3612 ], 3613 install_from(Info, Action), 3614 msg_downloads(Info). 3615install_step(Info, Action) --> 3616 { Info.get(commit) == 'HEAD' }, 3617 !, 3618 [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ], 3619 install_from(Info, Action), 3620 msg_downloads(Info). 3621install_step(Info, link) --> 3622 { Info.get(link) == true, 3623 uri_file_name(Info.get(url), Dir) 3624 }, 3625 !, 3626 [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ]. 3627install_step(Info, Action) --> 3628 [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ], 3629 install_from(Info, Action), 3630 msg_downloads(Info). 3631install_step(Info, Action) --> 3632 [ ' Install ' ], msg_pack(Info), 3633 install_from(Info, Action), 3634 msg_downloads(Info). 3635 3636install_from(Info, download) --> 3637 { download_url(Info.url) }, 3638 !, 3639 [ ' from ', url(Info.url) ]. 3640install_from(Info, unpack) --> 3641 [ ' from ', url(Info.url) ]. 3642 3643msg_downloads(Info) --> 3644 { Downloads = Info.get(all_downloads), 3645 Downloads > 0 3646 }, 3647 [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ], 3648 !. 3649msg_downloads(_) --> 3650 []. 3651 3652msg_pack(Pack) --> 3653 { atom(Pack) }, 3654 !, 3655 [ ansi(code, '~w', [Pack]) ]. 3656msg_pack(Info) --> 3657 msg_pack(Info.pack).
3663msg_build_plan(Plan) --> 3664 sequence(build_step, [nl], Plan). 3665 3666build_step(Info) --> 3667 [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ]. 3668 3669msg_can_upgrade_target(Info) --> 3670 [ ' Pack ' ], msg_pack(Info), 3671 [ ' is installed at version ~w'-[Info.version] ], 3672 msg_can_upgrade(Info). 3673 3674pack_list([]) --> []. 3675pack_list([H|T]) --> 3676 [ ' - Pack ' ], msg_pack(H), [nl], 3677 pack_list(T). 3678 3679label(remove_only(Pack)) --> 3680 [ 'Only remove package ~w (break dependencies)'-[Pack] ]. 3681label(remove_deps(Pack, Deps)) --> 3682 { length(Deps, Count) }, 3683 [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ]. 3684label(create_dir(Dir)) --> 3685 [ '~w'-[Dir] ]. 3686label(install_from(git(URL))) --> 3687 !, 3688 [ 'GIT repository at ~w'-[URL] ]. 3689label(install_from(URL)) --> 3690 [ '~w'-[URL] ]. 3691label(cancel) --> 3692 [ 'Cancel' ]. 3693 3694confirm_default(yes) --> 3695 [ ' Y/n? ' ]. 3696confirm_default(no) --> 3697 [ ' y/N? ' ]. 3698confirm_default(none) --> 3699 [ ' y/n? ' ]. 3700 3701msg_version(Version) --> 3702 [ '~w'-[Version] ]. 3703 3704msg_can_upgrade(Info) --> 3705 { Latest = Info.get(latest_version) }, 3706 [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ]. 3707msg_can_upgrade(_) --> 3708 []. 3709 3710 3711 /******************************* 3712 * MISC * 3713 *******************************/ 3714 3715local_uri_file_name(URL, FileName) :- 3716 uri_file_name(URL, FileName), 3717 !. 3718local_uri_file_name(URL, FileName) :- 3719 uri_components(URL, Components), 3720 uri_data(scheme, Components, File), File == file, 3721 uri_data(authority, Components, FileNameEnc), 3722 uri_data(path, Components, ''), 3723 uri_encoded(path, FileName, FileNameEnc). 3724 3725det_if(Cond, Goal) :- 3726 ( 3727 -> , 3728 ! 3729 ; 3730 ). 3731 3732member_nonvar(_, Var) :- 3733 var(Var), 3734 !, 3735 fail. 3736member_nonvar(E, [E|_]). 3737member_nonvar(E, [_|T]) :- 3738 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*/