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 ; \+ option(git(false), Options), 897 is_git_directory(Source) 898 -> remove_existing_pack(PackDir, Options), 899 run_process(path(git), [clone, Source, PackDir], []) 900 ; prepare_pack_dir(PackDir, Options), 901 copy_directory(Source, PackDir) 902 ). 903pack_unpack_from_local(Source, PackTopDir, Name, PackDir, Options) :- 904 exists_file(Source), 905 directory_file_path(PackTopDir, Name, PackDir), 906 prepare_pack_dir(PackDir, Options), 907 pack_unpack(Source, PackDir, Name, Options).
916:- if(exists_source(library(archive))). 917pack_unpack(Source, PackDir, Pack, Options) :- 918 ensure_loaded_archive, 919 pack_archive_info(Source, Pack, _Info, StripOptions), 920 prepare_pack_dir(PackDir, Options), 921 archive_extract(Source, PackDir, 922 [ exclude(['._*']) % MacOS resource forks 923 | StripOptions 924 ]). 925:- else. 926pack_unpack(_,_,_,_) :- 927 existence_error(library, archive). 928:- endif.
936pack_install_local(M:Gen, Dir, Options) :- 937 findall(Pack-PackOptions, call(M:Gen, Pack, PackOptions), Pairs), 938 pack_install_set(Pairs, Dir, Options). 939 940pack_install_set(Pairs, Dir, Options) :- 941 must_be(list(pair), Pairs), 942 ensure_directory(Dir), 943 partition(known_media, Pairs, Local, Remote), 944 maplist(pack_options_to_versions, Local, LocalVersions), 945 ( Remote == [] 946 -> AllVersions = LocalVersions 947 ; pairs_keys(Remote, Packs), 948 prolog_description(Properties), 949 query_pack_server(versions(Packs, Properties), Result, Options), 950 ( Result = true(RemoteVersions) 951 -> append(LocalVersions, RemoteVersions, AllVersions) 952 ; print_message(error, pack(query_failed(Result))), 953 fail 954 ) 955 ), 956 local_packs(Dir, Existing), 957 pack_resolve(Pairs, Existing, AllVersions, Plan, Options), 958 !, % for now, only first plan 959 Options1 = [pack_directory(Dir)|Options], 960 download_plan(Pairs, Plan, PlanB, Options1), 961 register_downloads(PlanB, Options), 962 maplist(update_automatic, PlanB), 963 build_plan(PlanB, Built, Options1), 964 publish_download(PlanB, Options), 965 work_done(Pairs, Plan, PlanB, Built, Options).
974known_media(_-Options) :-
975 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:
993pack_resolve(Pairs, Existing, Versions, Plan, Options) :-
994 insert_existing(Existing, Versions, AllVersions, Options),
995 phrase(select_version(Pairs, AllVersions,
996 [ plan(PlanA), % access to plan
997 dependency_for([]) % dependencies
998 | Options
999 ]),
1000 PlanA),
1001 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.1012:- det(insert_existing/4). 1013insert_existing(Existing, [], Versions, _Options) => 1014 maplist(existing_to_versions, Existing, Versions). 1015insert_existing(Existing, [Pack-Versions|T0], AllPackVersions, Options), 1016 select(Installed, Existing, Existing2), 1017 Installed.pack == Pack => 1018 can_upgrade(Installed, Versions, Installed2), 1019 insert_existing_(Installed2, Versions, AllVersions, Options), 1020 AllPackVersions = [Pack-AllVersions|T], 1021 insert_existing(Existing2, T0, T, Options). 1022insert_existing(Existing, [H|T0], AllVersions, Options) => 1023 AllVersions = [H|T], 1024 insert_existing(Existing, T0, T, Options). 1025 1026existing_to_versions(Installed, Pack-[Version-[Installed]]) :- 1027 Pack = Installed.pack, 1028 Version = Installed.version. 1029 1030insert_existing_(Installed, Versions, AllVersions, Options) :- 1031 option(upgrade(true), Options), 1032 !, 1033 insert_existing_(Installed, Versions, AllVersions). 1034insert_existing_(Installed, Versions, AllVersions, _) :- 1035 AllVersions = [Installed.version-[Installed]|Versions]. 1036 1037insert_existing_(Installed, [H|T0], [H|T]) :- 1038 H = V0-_Infos, 1039 cmp_versions(>, V0, Installed.version), 1040 !, 1041 insert_existing_(Installed, T0, T). 1042insert_existing_(Installed, [H0|T], [H|T]) :- 1043 H0 = V0-Infos, 1044 V0 == Installed.version, 1045 !, 1046 H = V0-[Installed|Infos]. 1047insert_existing_(Installed, Versions, All) :- 1048 All = [Installed.version-[Installed]|Versions].
latest_version
key to Installed if its version is older than
the latest available version.1055can_upgrade(Info, [Version-_|_], Info2) :- 1056 cmp_versions(>, Version, Info.version), 1057 !, 1058 Info2 = Info.put(latest_version, Version). 1059can_upgrade(Info, _, Info).
upgrade:true
to elements of PlanA in Existing that are not the
same.1067mark_installed([], _, []). 1068mark_installed([Info|T], Existing, Plan) :- 1069 ( member(Installed, Existing), 1070 Installed.pack == Info.pack 1071 -> ( ( Installed.git == true 1072 -> Info.git == true, 1073 Installed.hash == Info.hash 1074 ; Version = Info.get(version) 1075 -> Installed.version == Version 1076 ) 1077 -> Plan = [Info.put(keep, true)|PlanT] % up-to-date 1078 ; Plan = [Info.put(upgrade, Installed)|PlanT] % needs upgrade 1079 ) 1080 ; Plan = [Info|PlanT] % new install 1081 ), 1082 mark_installed(T, Existing, PlanT).
1090select_version([], _, _) --> 1091 []. 1092select_version([Pack-PackOptions|More], Versions, Options) --> 1093 { memberchk(Pack-PackVersions, Versions), 1094 member(Version-Infos, PackVersions), 1095 compatible_version(Pack, Version, PackOptions), 1096 member(Info, Infos), 1097 pack_options_compatible_with_info(Info, PackOptions), 1098 pack_satisfies(Pack, Version, Info, Info2, PackOptions), 1099 all_downloads(PackVersions, Downloads) 1100 }, 1101 add_to_plan(Info2.put(_{version: Version, all_downloads:Downloads}), 1102 Versions, Options), 1103 select_version(More, Versions, Options). 1104select_version([Pack-_PackOptions|_More], _Versions, _Options) --> 1105 { existence_error(pack, Pack) }. % or warn and continue? 1106 1107all_downloads(PackVersions, AllDownloads) :- 1108 aggregate_all(sum(Downloads), 1109 ( member(_Version-Infos, PackVersions), 1110 member(Info, Infos), 1111 get_dict(downloads, Info, Downloads) 1112 ), 1113 AllDownloads). 1114 1115add_requirements([], _, _) --> 1116 []. 1117add_requirements([H|T], Versions, Options) --> 1118 { is_prolog_token(H), 1119 !, 1120 prolog_satisfies(H) 1121 }, 1122 add_requirements(T, Versions, Options). 1123add_requirements([H|T], Versions, Options) --> 1124 { member(Pack-PackVersions, Versions), 1125 member(Version-Infos, PackVersions), 1126 member(Info, Infos), 1127 ( Provides = @(Pack,Version) 1128 ; member(Provides, Info.get(provides)) 1129 ), 1130 satisfies_req(Provides, H), 1131 all_downloads(PackVersions, Downloads) 1132 }, 1133 add_to_plan(Info.put(_{version: Version, all_downloads:Downloads}), 1134 Versions, Options), 1135 add_requirements(T, Versions, Options).
1143add_to_plan(Info, _Versions, Options) --> 1144 { option(plan(Plan), Options), 1145 member_nonvar(Planned, Plan), 1146 Planned.pack == Info.pack, 1147 !, 1148 same_version(Planned, Info) % same pack, different version 1149 }. 1150add_to_plan(Info, _Versions, _Options) --> 1151 { member(Conflict, Info.get(conflicts)), 1152 is_prolog_token(Conflict), 1153 prolog_satisfies(Conflict), 1154 !, 1155 fail % incompatible with this Prolog 1156 }. 1157add_to_plan(Info, _Versions, Options) --> 1158 { option(plan(Plan), Options), 1159 member_nonvar(Planned, Plan), 1160 info_conflicts(Info, Planned), % Conflicts with a planned pack 1161 !, 1162 fail 1163 }. 1164add_to_plan(Info, Versions, Options) --> 1165 { select_option(dependency_for(Dep0), Options, Options1), 1166 Options2 = [dependency_for([Info.pack|Dep0])|Options1], 1167 ( Dep0 = [DepFor|_] 1168 -> add_dependency_for(DepFor, Info, Info1) 1169 ; Info1 = Info 1170 ) 1171 }, 1172 [Info1], 1173 add_requirements(Info.get(requires,[]), Versions, Options2). 1174 1175add_dependency_for(Pack, Info, Info) :- 1176 Old = Info.get(dependency_for), 1177 !, 1178 b_set_dict(dependency_for, Info, [Pack|Old]). 1179add_dependency_for(Pack, Info0, Info) :- 1180 Info = Info0.put(dependency_for, [Pack]). 1181 1182same_version(Info, Info) :- 1183 !. 1184same_version(Planned, Info) :- 1185 Hash = Planned.get(hash), 1186 Hash \== (-), 1187 !, 1188 Hash == Info.get(hash). 1189same_version(Planned, Info) :- 1190 Planned.get(version) == Info.get(version).
1196info_conflicts(Info, Planned) :- 1197 info_conflicts_(Info, Planned), 1198 !. 1199info_conflicts(Info, Planned) :- 1200 info_conflicts_(Planned, Info), 1201 !. 1202 1203info_conflicts_(Info, Planned) :- 1204 member(Conflict, Info.get(conflicts)), 1205 \+ is_prolog_token(Conflict), 1206 info_provides(Planned, Provides), 1207 satisfies_req(Provides, Conflict), 1208 !. 1209 1210info_provides(Info, Provides) :- 1211 ( Provides = Info.pack@Info.version 1212 ; member(Provides, Info.get(provides)) 1213 ).
1220pack_satisfies(_Pack, _Version, Info0, Info, Options) :- 1221 option(commit('HEAD'), Options), 1222 !, 1223 Info0.get(git) == true, 1224 Info = Info0.put(commit, 'HEAD'). 1225pack_satisfies(_Pack, _Version, Info, Info, Options) :- 1226 option(commit(Commit), Options), 1227 !, 1228 Commit == Info.get(hash). 1229pack_satisfies(Pack, Version, Info, Info, Options) :- 1230 option(version(ReqVersion), Options), 1231 !, 1232 satisfies_version(Pack, Version, ReqVersion). 1233pack_satisfies(_Pack, _Version, Info, Info, _Options).
1237satisfies_version(Pack, Version, ReqVersion) :-
1238 catch(require_version(pack(Pack), Version, ReqVersion),
1239 error(version_error(pack(Pack), Version, ReqVersion),_),
1240 fail).
1246satisfies_req(Token, Token) => true. 1247satisfies_req(@(Token,_), Token) => true. 1248satisfies_req(@(Token,PrvVersion), Req), cmp(Req, Token, Cmp, ReqVersion) => 1249 cmp_versions(Cmp, PrvVersion, ReqVersion). 1250satisfies_req(_,_) => fail. 1251 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). 1257cmp(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).
1270:- det(pack_options_to_versions/2). 1271pack_options_to_versions(Pack-PackOptions, Pack-Versions) :- 1272 option(versions(Available), PackOptions), !, 1273 maplist(version_url_info(Pack, PackOptions), Available, Versions). 1274pack_options_to_versions(Pack-PackOptions, Pack-[Version-[Info]]) :- 1275 option(url(URL), PackOptions), 1276 findall(Prop, option_info_prop(PackOptions, Prop), Pairs), 1277 dict_create(Info, #, 1278 [ pack-Pack, 1279 url-URL 1280 | Pairs 1281 ]), 1282 Version = Info.get(version, '0.0.0'). 1283 1284version_url_info(Pack, PackOptions, Version-URL, Version-[Info]) :- 1285 findall(Prop, 1286 ( option_info_prop(PackOptions, Prop), 1287 Prop \= version-_ 1288 ), 1289 Pairs), 1290 dict_create(Info, #, 1291 [ pack-Pack, 1292 url-URL, 1293 version-Version 1294 | Pairs 1295 ]). 1296 1297option_info_prop(PackOptions, Prop-Value) :- 1298 option_info(Prop), 1299 Opt =.. [Prop,Value], 1300 option(Opt, PackOptions). 1301 1302option_info(git). 1303option_info(hash). 1304option_info(version). 1305option_info(branch). 1306option_info(link).
1313compatible_version(Pack, Version, PackOptions) :- 1314 option(version(ReqVersion), PackOptions), 1315 !, 1316 satisfies_version(Pack, Version, ReqVersion). 1317compatible_version(_, _, _).
1324pack_options_compatible_with_info(Info, PackOptions) :-
1325 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1326 dict_create(Dict, _, Pairs),
1327 Dict >:< Info.
1337download_plan(_Targets, Plan, Plan, _Options) :- 1338 exclude(installed, Plan, []), 1339 !. 1340download_plan(Targets, Plan0, Plan, Options) :- 1341 confirm(download_plan(Plan0), yes, Options), 1342 maplist(download_from_info(Options), Plan0, Plan1), 1343 plan_unsatisfied_dependencies(Plan1, Deps), 1344 ( Deps == [] 1345 -> Plan = Plan1 1346 ; print_message(informational, pack(new_dependencies(Deps))), 1347 prolog_description(Properties), 1348 query_pack_server(versions(Deps, Properties), Result, []), 1349 ( Result = true(Versions) 1350 -> pack_resolve(Targets, Plan1, Versions, Plan2, Options), 1351 !, 1352 download_plan(Targets, Plan2, Plan, Options) 1353 ; print_message(error, pack(query_failed(Result))), 1354 fail 1355 ) 1356 ).
1363plan_unsatisfied_dependencies(Plan, Deps) :- 1364 phrase(plan_unsatisfied_dependencies(Plan, Plan), Deps). 1365 1366plan_unsatisfied_dependencies([], _) --> 1367 []. 1368plan_unsatisfied_dependencies([Info|Infos], Plan) --> 1369 { Deps = Info.get(requires) }, 1370 plan_unsatisfied_requirements(Deps, Plan), 1371 plan_unsatisfied_dependencies(Infos, Plan). 1372 1373plan_unsatisfied_requirements([], _) --> 1374 []. 1375plan_unsatisfied_requirements([H|T], Plan) --> 1376 { is_prolog_token(H), % Can this fail? 1377 prolog_satisfies(H) 1378 }, 1379 !, 1380 plan_unsatisfied_requirements(T, Plan). 1381plan_unsatisfied_requirements([H|T], Plan) --> 1382 { member(Info, Plan), 1383 ( ( Version = Info.get(version) 1384 -> Provides = @(Info.get(pack), Version) 1385 ; Provides = Info.get(pack) 1386 ) 1387 ; member(Provides, Info.get(provides)) 1388 ), 1389 satisfies_req(Provides, H) 1390 }, !, 1391 plan_unsatisfied_requirements(T, Plan). 1392plan_unsatisfied_requirements([H|T], Plan) --> 1393 [H], 1394 plan_unsatisfied_requirements(T, Plan).
1403build_plan(Plan, Ordered, Options) :- 1404 partition(needs_rebuild_from_info(Options), Plan, ToBuild, NoBuild), 1405 maplist(attach_from_info(Options), NoBuild), 1406 ( ToBuild == [] 1407 -> Ordered = [] 1408 ; order_builds(ToBuild, Ordered), 1409 confirm(build_plan(Ordered), yes, Options), 1410 maplist(exec_plan_rebuild_step(Options), Ordered) 1411 ). 1412 1413needs_rebuild_from_info(Options, Info) :- 1414 needs_rebuild(Info.installed, Options).
1420needs_rebuild(PackDir, Options) :-
1421 ( is_foreign_pack(PackDir, _),
1422 \+ is_built(PackDir, Options)
1423 -> true
1424 ; is_autoload_pack(PackDir, Options),
1425 post_install_autoload(PackDir, Options),
1426 fail
1427 ).
1436is_built(PackDir, _Options) :-
1437 current_prolog_flag(arch, Arch),
1438 prolog_version_dotted(Version), % Major.Minor.Patch
1439 pack_status_dir(PackDir, built(Arch, Version, _)).
1446order_builds(ToBuild, Ordered) :- 1447 findall(Pack-Dep, dep_edge(ToBuild, Pack, Dep), Edges), 1448 maplist(get_dict(pack), ToBuild, Packs), 1449 vertices_edges_to_ugraph(Packs, Edges, Graph), 1450 ugraph_layers(Graph, Layers), 1451 append(Layers, PackNames), 1452 maplist(pack_info_from_name(ToBuild), PackNames, Ordered). 1453 1454dep_edge(Infos, Pack, Dep) :- 1455 member(Info, Infos), 1456 Pack = Info.pack, 1457 member(Dep, Info.get(dependency_for)), 1458 ( member(DepInfo, Infos), 1459 DepInfo.pack == Dep 1460 -> true 1461 ). 1462 1463:- det(pack_info_from_name/3). 1464pack_info_from_name(Infos, Pack, Info) :- 1465 member(Info, Infos), 1466 Info.pack == Pack, 1467 !.
1473exec_plan_rebuild_step(Options, Info) :-
1474 print_message(informational, pack(build(Info.pack, Info.installed))),
1475 pack_post_install(Info.pack, Info.installed, Options),
1476 attach_from_info(Options, Info).
1482attach_from_info(_Options, Info) :- 1483 Info.get(keep) == true, 1484 !. 1485attach_from_info(Options, Info) :- 1486 ( option(pack_directory(_Parent), Options) 1487 -> pack_attach(Info.installed, [duplicate(replace)]) 1488 ; pack_attach(Info.installed, []) 1489 ).
1499download_from_info(Options, Info0, Info), option(dryrun(true), Options) => 1500 print_term(Info0, [nl(true)]), 1501 Info = Info0. 1502download_from_info(_Options, Info0, Info), installed(Info0) => 1503 Info = Info0. 1504download_from_info(_Options, Info0, Info), 1505 _{upgrade:OldInfo, git:true} :< Info0, 1506 is_git_directory(OldInfo.installed) => 1507 PackDir = OldInfo.installed, 1508 git_checkout_version(PackDir, [commit(Info0.hash)]), 1509 reload_info(PackDir, Info0, Info). 1510download_from_info(Options, Info0, Info), 1511 _{upgrade:OldInfo} :< Info0 => 1512 PackDir = OldInfo.installed, 1513 detach_pack(OldInfo.pack, PackDir), 1514 delete_directory_and_contents(PackDir), 1515 del_dict(upgrade, Info0, _, Info1), 1516 download_from_info(Options, Info1, Info). 1517download_from_info(Options, Info0, Info), 1518 _{url:URL, git:true} :< Info0, \+ have_git => 1519 git_archive_url(URL, Archive, Options), 1520 download_from_info([git_url(URL)|Options], 1521 Info0.put(_{ url:Archive, 1522 git:false, 1523 git_url:URL 1524 }), 1525 Info1), 1526 % restore the hash to register the download. 1527 ( Info1.get(version) == Info0.get(version), 1528 Hash = Info0.get(hash) 1529 -> Info = Info1.put(hash, Hash) 1530 ; Info = Info1 1531 ). 1532download_from_info(Options, Info0, Info), 1533 _{url:URL} :< Info0 => 1534 select_option(pack_directory(Dir), Options, Options1), 1535 select_option(version(_), Options1, Options2, _), 1536 download_info_extra(Info0, InstallOptions, Options2), 1537 pack_download_from_url(URL, Dir, Info0.pack, 1538 [ interactive(false), 1539 pack_dir(PackDir) 1540 | InstallOptions 1541 ]), 1542 reload_info(PackDir, Info0, Info). 1543 1544download_info_extra(Info, [git(true),commit(Hash)|Options], Options) :- 1545 Info.get(git) == true, 1546 !, 1547 Hash = Info.get(commit, 'HEAD'). 1548download_info_extra(Info, [link(true)|Options], Options) :- 1549 Info.get(link) == true, 1550 !. 1551download_info_extra(_, Options, Options). 1552 1553installed(Info) :- 1554 _ = Info.get(installed). 1555 1556detach_pack(Pack, PackDir) :- 1557 ( current_pack(Pack, PackDir) 1558 -> '$pack_detach'(Pack, PackDir) 1559 ; true 1560 ).
1569reload_info(_PackDir, Info, Info) :- 1570 _ = Info.get(installed), % we read it from the package 1571 !. 1572reload_info(PackDir, Info0, Info) :- 1573 local_pack_info(PackDir, Info1), 1574 Info = Info0.put(installed, PackDir) 1575 .put(downloaded, Info0.url) 1576 .put(Info1).
1583work_done(_, _, _, _, Options), 1584 option(silent(true), Options) => 1585 true. 1586work_done(Targets, Plan, Plan, [], _Options) => 1587 convlist(can_upgrade_target(Plan), Targets, CanUpgrade), 1588 ( CanUpgrade == [] 1589 -> pairs_keys(Targets, Packs), 1590 print_message(informational, pack(up_to_date(Packs))) 1591 ; print_message(informational, pack(installed_can_upgrade(CanUpgrade))) 1592 ). 1593work_done(_, _, _, _, _) => 1594 true. 1595 1596can_upgrade_target(Plan, Pack-_, Info) => 1597 member(Info, Plan), 1598 Info.pack == Pack, 1599 !, 1600 _ = Info.get(latest_version).
1607local_packs(Dir, Packs) :- 1608 findall(Pack, pack_in_subdir(Dir, Pack), Packs). 1609 1610pack_in_subdir(Dir, Info) :- 1611 directory_member(Dir, PackDir, 1612 [ file_type(directory), 1613 hidden(false) 1614 ]), 1615 local_pack_info(PackDir, Info). 1616 1617local_pack_info(PackDir, 1618 #{ pack: Pack, 1619 version: Version, 1620 title: Title, 1621 hash: Hash, 1622 url: URL, 1623 git: IsGit, 1624 requires: Requires, 1625 provides: Provides, 1626 conflicts: Conflicts, 1627 installed: PackDir 1628 }) :- 1629 directory_file_path(PackDir, 'pack.pl', MetaFile), 1630 exists_file(MetaFile), 1631 file_base_name(PackDir, DirName), 1632 findall(Term, pack_dir_info(PackDir, _, Term), Info), 1633 option(pack(Pack), Info, DirName), 1634 option(title(Title), Info, '<no title>'), 1635 option(version(Version), Info, '<no version>'), 1636 option(download(URL), Info, '<no download url>'), 1637 findall(Req, member(requires(Req), Info), Requires), 1638 findall(Prv, member(provides(Prv), Info), Provides), 1639 findall(Cfl, member(conflicts(Cfl), Info), Conflicts), 1640 ( have_git, 1641 is_git_directory(PackDir) 1642 -> git_hash(Hash, [directory(PackDir)]), 1643 IsGit = true 1644 ; Hash = '-', 1645 IsGit = false 1646 ). 1647 1648 1649 /******************************* 1650 * PROLOG VERSIONS * 1651 *******************************/
prolog(Dialect, Version)
1662prolog_description([prolog(swi(Version))]) :- 1663 prolog_version(Version). 1664 1665prolog_version(Version) :- 1666 current_prolog_flag(version_git, Version), 1667 !. 1668prolog_version(Version) :- 1669 prolog_version_dotted(Version). 1670 1671prolog_version_dotted(Version) :- 1672 current_prolog_flag(version_data, swi(Major, Minor, Patch, _)), 1673 VNumbers = [Major, Minor, Patch], 1674 atomic_list_concat(VNumbers, '.', Version).
1681is_prolog_token(Token), cmp(Token, prolog, _Cmp, _Version) => true. 1682is_prolog_token(prolog:_Feature) => true. 1683is_prolog_token(_) => fail.
requires(Token)
terms for
library(Lib)
1698prolog_satisfies(Token), cmp(Token, prolog, Cmp, ReqVersion) => 1699 prolog_version(CurrentVersion), 1700 cmp_versions(Cmp, CurrentVersion, ReqVersion). 1701prolog_satisfies(prolog:library(Lib)), atom(Lib) => 1702 exists_source(library(Lib)). 1703prolog_satisfies(prolog:Feature), atom(Feature) => 1704 current_prolog_flag(Feature, true). 1705prolog_satisfies(prolog:Feature), flag_value_feature(Feature, Flag, Value) => 1706 current_prolog_flag(Flag, Value). 1707 1708flag_value_feature(Feature, Flag, Value) :- 1709 compound(Feature), 1710 compound_name_arguments(Feature, Flag, [Value]). 1711 1712 1713 /******************************* 1714 * INFO * 1715 *******************************/
Requires library(archive), which is lazily loaded when needed.
1729:- if(exists_source(library(archive))). 1730ensure_loaded_archive :- 1731 current_predicate(archive_open/3), 1732 !. 1733ensure_loaded_archive :- 1734 use_module(library(archive)). 1735 1736pack_archive_info(Archive, Pack, [archive_size(Bytes)|Info], Strip) :- 1737 ensure_loaded_archive, 1738 size_file(Archive, Bytes), 1739 setup_call_cleanup( 1740 archive_open(Archive, Handle, []), 1741 ( repeat, 1742 ( archive_next_header(Handle, InfoFile) 1743 -> true 1744 ; !, fail 1745 ) 1746 ), 1747 archive_close(Handle)), 1748 file_base_name(InfoFile, 'pack.pl'), 1749 atom_concat(Prefix, 'pack.pl', InfoFile), 1750 strip_option(Prefix, Pack, Strip), 1751 setup_call_cleanup( 1752 archive_open_entry(Handle, Stream), 1753 read_stream_to_terms(Stream, Info), 1754 close(Stream)), 1755 !, 1756 must_be(ground, Info), 1757 maplist(valid_term(pack_info_term), Info). 1758:- else. 1759pack_archive_info(_, _, _, _) :- 1760 existence_error(library, archive). 1761:- endif. 1762pack_archive_info(_, _, _, _) :- 1763 existence_error(pack_file, 'pack.pl'). 1764 1765strip_option('', _, []) :- !. 1766strip_option('./', _, []) :- !. 1767strip_option(Prefix, Pack, [remove_prefix(Prefix)]) :- 1768 atom_concat(PrefixDir, /, Prefix), 1769 file_base_name(PrefixDir, Base), 1770 ( Base == Pack 1771 -> true 1772 ; pack_version_file(Pack, _, Base) 1773 -> true 1774 ; \+ sub_atom(PrefixDir, _, _, _, /) 1775 ). 1776 1777read_stream_to_terms(Stream, Terms) :- 1778 read(Stream, Term0), 1779 read_stream_to_terms(Term0, Stream, Terms). 1780 1781read_stream_to_terms(end_of_file, _, []) :- !. 1782read_stream_to_terms(Term0, Stream, [Term0|Terms]) :- 1783 read(Stream, Term1), 1784 read_stream_to_terms(Term1, Stream, Terms).
1792pack_git_info(GitDir, Hash, [git(true), installed_size(Bytes)|Info]) :- 1793 exists_directory(GitDir), 1794 !, 1795 git_ls_tree(Entries, [directory(GitDir)]), 1796 git_hash(Hash, [directory(GitDir)]), 1797 maplist(arg(4), Entries, Sizes), 1798 sum_list(Sizes, Bytes), 1799 dir_metadata(GitDir, Info). 1800 1801dir_metadata(GitDir, Info) :- 1802 directory_file_path(GitDir, 'pack.pl', InfoFile), 1803 read_file_to_terms(InfoFile, Info, [encoding(utf8)]), 1804 must_be(ground, Info), 1805 maplist(valid_term(pack_info_term), Info).
1811download_file_sanity_check(Archive, Pack, Info) :- 1812 info_field(name(PackName), Info), 1813 info_field(version(PackVersion), Info), 1814 pack_version_file(PackFile, FileVersion, Archive), 1815 must_match([Pack, PackName, PackFile], name), 1816 must_match([PackVersion, FileVersion], version). 1817 1818info_field(Field, Info) :- 1819 memberchk(Field, Info), 1820 ground(Field), 1821 !. 1822info_field(Field, _Info) :- 1823 functor(Field, FieldName, _), 1824 print_message(error, pack(missing(FieldName))), 1825 fail. 1826 1827must_match(Values, _Field) :- 1828 sort(Values, [_]), 1829 !. 1830must_match(Values, Field) :- 1831 print_message(error, pack(conflict(Field, Values))), 1832 fail. 1833 1834 1835 /******************************* 1836 * INSTALLATION * 1837 *******************************/
1851prepare_pack_dir(Dir, Options) :- 1852 exists_directory(Dir), 1853 !, 1854 ( empty_directory(Dir) 1855 -> true 1856 ; remove_existing_pack(Dir, Options) 1857 -> make_directory(Dir) 1858 ). 1859prepare_pack_dir(Dir, _) :- 1860 ( read_link(Dir, _, _) 1861 ; access_file(Dir, exist) 1862 ), 1863 !, 1864 delete_file(Dir), 1865 make_directory(Dir). 1866prepare_pack_dir(Dir, _) :- 1867 make_directory(Dir).
1873empty_directory(Dir) :- 1874 \+ ( directory_files(Dir, Entries), 1875 member(Entry, Entries), 1876 \+ special(Entry) 1877 ). 1878 1879special(.). 1880special(..).
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.1889remove_existing_pack(PackDir, Options) :- 1890 exists_directory(PackDir), 1891 !, 1892 ( ( option(upgrade(true), Options) 1893 ; confirm(remove_existing_pack(PackDir), yes, Options) 1894 ) 1895 -> delete_directory_and_contents(PackDir) 1896 ; print_message(error, pack(directory_exists(PackDir))), 1897 fail 1898 ). 1899remove_existing_pack(_, _).
1915pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1916 option(git(true), Options), 1917 !, 1918 directory_file_path(PackTopDir, Pack, PackDir), 1919 prepare_pack_dir(PackDir, Options), 1920 ( option(branch(Branch), Options) 1921 -> Extra = ['--branch', Branch] 1922 ; Extra = [] 1923 ), 1924 run_process(path(git), [clone, URL, PackDir|Extra], []), 1925 git_checkout_version(PackDir, [update(false)|Options]), 1926 option(pack_dir(PackDir), Options, _). 1927pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1928 download_url(URL), 1929 !, 1930 directory_file_path(PackTopDir, Pack, PackDir), 1931 prepare_pack_dir(PackDir, Options), 1932 pack_download_dir(PackTopDir, DownLoadDir), 1933 download_file(URL, Pack, DownloadBase, Options), 1934 directory_file_path(DownLoadDir, DownloadBase, DownloadFile), 1935 ( option(insecure(true), Options, false) 1936 -> TLSOptions = [cert_verify_hook(ssl_verify)] 1937 ; TLSOptions = [] 1938 ), 1939 print_message(informational, pack(download(begin, Pack, URL, DownloadFile))), 1940 setup_call_cleanup( 1941 http_open(URL, In, TLSOptions), 1942 setup_call_cleanup( 1943 open(DownloadFile, write, Out, [type(binary)]), 1944 copy_stream_data(In, Out), 1945 close(Out)), 1946 close(In)), 1947 print_message(informational, pack(download(end, Pack, URL, DownloadFile))), 1948 pack_archive_info(DownloadFile, Pack, Info, _), 1949 ( option(git_url(GitURL), Options) 1950 -> Origin = GitURL % implicit download from git. 1951 ; download_file_sanity_check(DownloadFile, Pack, Info), 1952 Origin = URL 1953 ), 1954 pack_unpack_from_local(DownloadFile, PackTopDir, Pack, PackDir, Options), 1955 pack_assert(PackDir, archive(DownloadFile, Origin)), 1956 option(pack_dir(PackDir), Options, _). 1957pack_download_from_url(URL, PackTopDir, Pack, Options) :- 1958 local_uri_file_name(URL, File), 1959 !, 1960 pack_unpack_from_local(File, PackTopDir, Pack, PackDir, Options), 1961 pack_assert(PackDir, archive(File, URL)), 1962 option(pack_dir(PackDir), Options, _). 1963pack_download_from_url(URL, _PackTopDir, _Pack, _Options) :- 1964 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')
.1988git_checkout_version(PackDir, Options) :- 1989 option(commit('HEAD'), Options), 1990 option(branch(Branch), Options), 1991 !, 1992 git_ensure_on_branch(PackDir, Branch), 1993 run_process(path(git), ['-C', PackDir, pull], []). 1994git_checkout_version(PackDir, Options) :- 1995 option(commit('HEAD'), Options), 1996 git_current_branch(_, [directory(PackDir)]), 1997 !, 1998 run_process(path(git), ['-C', PackDir, pull], []). 1999git_checkout_version(PackDir, Options) :- 2000 option(commit('HEAD'), Options), 2001 !, 2002 git_default_branch(Branch, [directory(PackDir)]), 2003 git_ensure_on_branch(PackDir, Branch), 2004 run_process(path(git), ['-C', PackDir, pull], []). 2005git_checkout_version(PackDir, Options) :- 2006 option(commit(Hash), Options), 2007 run_process(path(git), ['-C', PackDir, fetch], []), 2008 git_branches(Branches, [contains(Hash), directory(PackDir)]), 2009 git_process_output(['-C', PackDir, 'rev-parse' | Branches], 2010 read_lines_to_atoms(Commits), 2011 []), 2012 nth1(I, Commits, Hash), 2013 nth1(I, Branches, Branch), 2014 !, 2015 git_ensure_on_branch(PackDir, Branch). 2016git_checkout_version(PackDir, Options) :- 2017 option(commit(Hash), Options), 2018 !, 2019 run_process(path(git), ['-C', PackDir, checkout, '--quiet', Hash], []). 2020git_checkout_version(PackDir, Options) :- 2021 option(version(Version), Options), 2022 !, 2023 git_tags(Tags, [directory(PackDir)]), 2024 ( memberchk(Version, Tags) 2025 -> Tag = Version 2026 ; member(Tag, Tags), 2027 sub_atom(Tag, B, _, 0, Version), 2028 sub_atom(Tag, 0, B, _, Prefix), 2029 version_prefix(Prefix) 2030 -> true 2031 ; existence_error(version_tag, Version) 2032 ), 2033 run_process(path(git), ['-C', PackDir, checkout, Tag], []). 2034git_checkout_version(_PackDir, Options) :- 2035 option(fresh(true), Options), 2036 !. 2037git_checkout_version(PackDir, _Options) :- 2038 git_current_branch(_, [directory(PackDir)]), 2039 !, 2040 run_process(path(git), ['-C', PackDir, pull], []). 2041git_checkout_version(PackDir, _Options) :- 2042 git_default_branch(Branch, [directory(PackDir)]), 2043 git_ensure_on_branch(PackDir, Branch), 2044 run_process(path(git), ['-C', PackDir, pull], []).
2050git_ensure_on_branch(PackDir, Branch) :- 2051 git_current_branch(Branch, [directory(PackDir)]), 2052 !. 2053git_ensure_on_branch(PackDir, Branch) :- 2054 run_process(path(git), ['-C', PackDir, checkout, Branch], []). 2055 2056read_lines_to_atoms(Atoms, In) :- 2057 read_line_to_string(In, Line), 2058 ( Line == end_of_file 2059 -> Atoms = [] 2060 ; atom_string(Atom, Line), 2061 Atoms = [Atom|T], 2062 read_lines_to_atoms(T, In) 2063 ). 2064 2065version_prefix(Prefix) :- 2066 atom_codes(Prefix, Codes), 2067 phrase(version_prefix, Codes). 2068 2069version_prefix --> 2070 [C], 2071 { code_type(C, alpha) }, 2072 !, 2073 version_prefix. 2074version_prefix --> 2075 "-". 2076version_prefix --> 2077 "_". 2078version_prefix --> 2079 "".
2086download_file(URL, Pack, File, Options) :- 2087 option(version(Version), Options), 2088 !, 2089 file_name_extension(_, Ext, URL), 2090 format(atom(File), '~w-~w.~w', [Pack, Version, Ext]). 2091download_file(URL, Pack, File, _) :- 2092 file_base_name(URL,Basename), 2093 no_int_file_name_extension(Tag,Ext,Basename), 2094 tag_version(Tag,Version), 2095 !, 2096 format(atom(File0), '~w-~w', [Pack, Version]), 2097 file_name_extension(File0, Ext, File). 2098download_file(URL, _, File, _) :- 2099 file_base_name(URL, File).
2107:- public pack_url_file/2. 2108pack_url_file(URL, FileID) :- 2109 github_release_url(URL, Pack, Version), 2110 !, 2111 download_file(URL, Pack, FileID, [version(Version)]). 2112pack_url_file(URL, FileID) :- 2113 file_base_name(URL, FileID). 2114 2115% ssl_verify(+SSL, +ProblemCert, +AllCerts, +FirstCert, +Error) 2116% 2117% Used if insecure(true) is given to pack_install/2. Accepts any 2118% certificate. 2119 2120:- public ssl_verify/5. 2121ssl_verify(_SSL, 2122 _ProblemCertificate, _AllCertificates, _FirstCertificate, 2123 _Error). 2124 2125pack_download_dir(PackTopDir, DownLoadDir) :- 2126 directory_file_path(PackTopDir, 'Downloads', DownLoadDir), 2127 ( exists_directory(DownLoadDir) 2128 -> true 2129 ; make_directory(DownLoadDir) 2130 ), 2131 ( access_file(DownLoadDir, write) 2132 -> true 2133 ; permission_error(write, directory, DownLoadDir) 2134 ).
ftp://
are also download URLs, but we cannot download
from them.2142download_url(URL) :- 2143 atom(URL), 2144 uri_components(URL, Components), 2145 uri_data(scheme, Components, Scheme), 2146 download_scheme(Scheme). 2147 2148download_scheme(http). 2149download_scheme(https).
2159pack_post_install(Pack, PackDir, Options) :-
2160 post_install_foreign(Pack, PackDir, Options),
2161 post_install_autoload(PackDir, Options),
2162 attach_packs(PackDir, [duplicate(warning)]).
2170pack_rebuild :- 2171 forall(current_pack(Pack), 2172 ( print_message(informational, pack(rebuild(Pack))), 2173 pack_rebuild(Pack) 2174 )). 2175 2176pack_rebuild(Pack) :- 2177 current_pack(Pack, PackDir), 2178 !, 2179 post_install_foreign(Pack, PackDir, [rebuild(true)]). 2180pack_rebuild(Pack) :- 2181 unattached_pack(Pack, PackDir), 2182 !, 2183 post_install_foreign(Pack, PackDir, [rebuild(true)]). 2184pack_rebuild(Pack) :- 2185 existence_error(pack, Pack). 2186 2187unattached_pack(Pack, BaseDir) :- 2188 directory_file_path(Pack, 'pack.pl', PackFile), 2189 absolute_file_name(pack(PackFile), PackPath, 2190 [ access(read), 2191 file_errors(fail) 2192 ]), 2193 file_directory_name(PackPath, BaseDir).
2209post_install_foreign(Pack, PackDir, Options) :- 2210 is_foreign_pack(PackDir, _), 2211 !, 2212 ( pack_info_term(PackDir, pack_version(Version)) 2213 -> true 2214 ; Version = 1 2215 ), 2216 option(rebuild(Rebuild), Options, if_absent), 2217 current_prolog_flag(arch, Arch), 2218 prolog_version_dotted(PrologVersion), 2219 ( Rebuild == if_absent, 2220 foreign_present(PackDir, Arch) 2221 -> print_message(informational, pack(kept_foreign(Pack, Arch))), 2222 ( pack_status_dir(PackDir, built(Arch, _, _)) 2223 -> true 2224 ; pack_assert(PackDir, built(Arch, PrologVersion, downloaded)) 2225 ) 2226 ; BuildSteps0 = [[dependencies], [configure], build, install, [test]], 2227 ( Rebuild == true 2228 -> BuildSteps1 = [distclean|BuildSteps0] 2229 ; BuildSteps1 = BuildSteps0 2230 ), 2231 ( option(test(false), Options) 2232 -> delete(BuildSteps1, [test], BuildSteps2) 2233 ; BuildSteps2 = BuildSteps1 2234 ), 2235 ( option(clean(true), Options) 2236 -> append(BuildSteps2, [[clean]], BuildSteps) 2237 ; BuildSteps = BuildSteps2 2238 ), 2239 build_steps(BuildSteps, PackDir, [pack_version(Version)|Options]), 2240 pack_assert(PackDir, built(Arch, PrologVersion, built)) 2241 ). 2242post_install_foreign(_, _, _).
lib
directory for
the current architecture.
2253foreign_present(PackDir, Arch) :-
2254 atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
2255 exists_directory(ForeignBaseDir),
2256 !,
2257 atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
2258 exists_directory(ForeignDir),
2259 current_prolog_flag(shared_object_extension, Ext),
2260 atomic_list_concat([ForeignDir, '/*.', Ext], Pattern),
2261 expand_file_name(Pattern, Files),
2262 Files \== [].
2269is_foreign_pack(PackDir, Type) :- 2270 foreign_file(File, Type), 2271 directory_file_path(PackDir, File, Path), 2272 exists_file(Path). 2273 2274foreign_file('CMakeLists.txt', cmake). 2275foreign_file('configure', configure). 2276foreign_file('configure.in', autoconf). 2277foreign_file('configure.ac', autoconf). 2278foreign_file('Makefile.am', automake). 2279foreign_file('Makefile', make). 2280foreign_file('makefile', make). 2281foreign_file('conanfile.txt', conan). 2282foreign_file('conanfile.py', conan). 2283 2284 2285 /******************************* 2286 * AUTOLOAD * 2287 *******************************/
2293post_install_autoload(PackDir, Options) :- 2294 is_autoload_pack(PackDir, Options), 2295 !, 2296 directory_file_path(PackDir, prolog, PrologLibDir), 2297 make_library_index(PrologLibDir). 2298post_install_autoload(_, _). 2299 2300is_autoload_pack(PackDir, Options) :- 2301 option(autoload(true), Options, true), 2302 pack_info_term(PackDir, autoload(true)). 2303 2304 2305 /******************************* 2306 * UPGRADE * 2307 *******************************/
pack_install(Pack, [upgrade(true)])
.2313pack_upgrade(Pack) :- 2314 pack_install(Pack, [upgrade(true)]). 2315 2316 2317 /******************************* 2318 * REMOVE * 2319 *******************************/
true
delete dependencies without asking.2332pack_remove(Pack) :- 2333 pack_remove(Pack, []). 2334 2335pack_remove(Pack, Options) :- 2336 option(dependencies(false), Options), 2337 !, 2338 pack_remove_forced(Pack). 2339pack_remove(Pack, Options) :- 2340 ( dependents(Pack, Deps) 2341 -> ( option(dependencies(true), Options) 2342 -> true 2343 ; confirm_remove(Pack, Deps, Delete, Options) 2344 ), 2345 forall(member(P, Delete), pack_remove_forced(P)) 2346 ; pack_remove_forced(Pack) 2347 ). 2348 2349pack_remove_forced(Pack) :- 2350 catch('$pack_detach'(Pack, BaseDir), 2351 error(existence_error(pack, Pack), _), 2352 fail), 2353 !, 2354 print_message(informational, pack(remove(BaseDir))), 2355 delete_directory_and_contents(BaseDir). 2356pack_remove_forced(Pack) :- 2357 unattached_pack(Pack, BaseDir), 2358 !, 2359 delete_directory_and_contents(BaseDir). 2360pack_remove_forced(Pack) :- 2361 print_message(informational, error(existence_error(pack, Pack),_)). 2362 2363confirm_remove(Pack, Deps, Delete, Options) :- 2364 print_message(warning, pack(depends(Pack, Deps))), 2365 menu(pack(resolve_remove), 2366 [ [Pack] = remove_only(Pack), 2367 [Pack|Deps] = remove_deps(Pack, Deps), 2368 [] = cancel 2369 ], [], Delete, Options), 2370 Delete \== []. 2371 2372 2373 /******************************* 2374 * PUBLISH * 2375 *******************************/
?- 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 first2428pack_publish(Dir, Options) :- 2429 \+ download_url(Dir), 2430 is_git_directory(Dir), !, 2431 pack_git_info(Dir, _Hash, Metadata), 2432 prepare_repository(Dir, Metadata, Options), 2433 ( memberchk(download(URL), Metadata), 2434 git_url(URL, _) 2435 -> true 2436 ; option(remote(Remote), Options, origin), 2437 git_remote_url(Remote, RemoteURL, [directory(Dir)]), 2438 git_to_https_url(RemoteURL, URL) 2439 ), 2440 memberchk(version(Version), Metadata), 2441 pack_publish_(URL, 2442 [ version(Version) 2443 | Options 2444 ]). 2445pack_publish(Spec, Options) :- 2446 pack_publish_(Spec, Options). 2447 2448pack_publish_(Spec, Options) :- 2449 pack_default_options(Spec, Pack, Options, DefOptions), 2450 option(url(URL), DefOptions), 2451 valid_publish_url(URL, Options), 2452 prepare_build_location(Pack, Dir, Clean, Options), 2453 ( option(register(false), Options) 2454 -> InstallOptions = DefOptions 2455 ; InstallOptions = [publish(Pack)|DefOptions] 2456 ), 2457 call_cleanup(pack_install(Pack, 2458 [ pack(Pack) 2459 | InstallOptions 2460 ]), 2461 cleanup_publish(Clean, Dir)). 2462 2463cleanup_publish(true, Dir) :- 2464 !, 2465 delete_directory_and_contents(Dir). 2466cleanup_publish(_, _). 2467 2468valid_publish_url(URL, Options) :- 2469 option(register(Register), Options, true), 2470 ( Register == false 2471 -> true 2472 ; download_url(URL) 2473 -> true 2474 ; permission_error(publish, pack, URL) 2475 ). 2476 2477prepare_build_location(Pack, Dir, Clean, Options) :- 2478 ( option(pack_directory(Dir), Options) 2479 -> ensure_directory(Dir), 2480 ( option(clean(true), Options, true) 2481 -> delete_directory_contents(Dir) 2482 ; true 2483 ) 2484 ; tmp_file(pack, Dir), 2485 make_directory(Dir), 2486 Clean = true 2487 ), 2488 ( option(isolated(false), Options) 2489 -> detach_pack(Pack, _), 2490 attach_packs(Dir, [search(first)]) 2491 ; attach_packs(Dir, [replace(true)]) 2492 ).
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.2503prepare_repository(_Dir, _Metadata, Options) :- 2504 option(register(false), Options), 2505 !. 2506prepare_repository(Dir, Metadata, Options) :- 2507 git_dir_must_be_clean(Dir), 2508 git_must_be_on_default_branch(Dir, Options), 2509 tag_git_dir(Dir, Metadata, Action, Options), 2510 confirm(git_push, yes, Options), 2511 run_process(path(git), ['-C', file(Dir), push ], []), 2512 ( Action = push_tag(Tag) 2513 -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], []) 2514 ; true 2515 ). 2516 2517git_dir_must_be_clean(Dir) :- 2518 git_describe(Description, [directory(Dir)]), 2519 ( sub_atom(Description, _, _, 0, '-DIRTY') 2520 -> print_message(error, pack(git_not_clean(Dir))), 2521 fail 2522 ; true 2523 ). 2524 2525git_must_be_on_default_branch(Dir, Options) :- 2526 ( option(branch(Default), Options) 2527 -> true 2528 ; git_default_branch(Default, [directory(Dir)]) 2529 ), 2530 git_current_branch(Current, [directory(Dir)]), 2531 ( Default == Current 2532 -> true 2533 ; print_message(error, 2534 pack(git_branch_not_default(Dir, Default, Current))), 2535 fail 2536 ).
2545tag_git_dir(Dir, Metadata, Action, Options) :- 2546 memberchk(version(Version), Metadata), 2547 atom_concat('V', Version, Tag), 2548 git_tags(Tags, [directory(Dir)]), 2549 ( memberchk(Tag, Tags) 2550 -> git_tag_is_consistent(Dir, Tag, Action, Options) 2551 ; format(string(Message), 'Release ~w', [Version]), 2552 findall(Opt, git_tag_option(Opt, Options), Argv, 2553 [ '-m', Message, Tag ]), 2554 confirm(git_tag(Tag), yes, Options), 2555 run_process(path(git), ['-C', file(Dir), tag | Argv ], []), 2556 Action = push_tag(Tag) 2557 ). 2558 2559git_tag_option('-s', Options) :- option(sign(true), Options, true). 2560git_tag_option('-f', Options) :- option(force(true), Options, true). 2561 2562git_tag_is_consistent(Dir, Tag, Action, Options) :- 2563 format(atom(TagRef), 'refs/tags/~w', [Tag]), 2564 format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]), 2565 option(remote(Remote), Options, origin), 2566 git_ls_remote(Dir, LocalTags, [tags(true)]), 2567 memberchk(CommitHash-CommitRef, LocalTags), 2568 ( git_hash(CommitHash, [directory(Dir)]) 2569 -> true 2570 ; print_message(error, pack(git_release_tag_not_at_head(Tag))), 2571 fail 2572 ), 2573 memberchk(TagHash-TagRef, LocalTags), 2574 git_ls_remote(Remote, RemoteTags, [tags(true)]), 2575 ( memberchk(RemoteCommitHash-CommitRef, RemoteTags), 2576 memberchk(RemoteTagHash-TagRef, RemoteTags) 2577 -> ( RemoteCommitHash == CommitHash, 2578 RemoteTagHash == TagHash 2579 -> Action = none 2580 ; print_message(error, pack(git_tag_out_of_sync(Tag))), 2581 fail 2582 ) 2583 ; Action = push_tag(Tag) 2584 ).
2592git_to_https_url(URL, URL) :- 2593 download_url(URL), 2594 !. 2595git_to_https_url(GitURL, URL) :- 2596 atom_concat('git@github.com:', Repo, GitURL), 2597 !, 2598 atom_concat('https://github.com/', Repo, URL). 2599git_to_https_url(GitURL, _) :- 2600 print_message(error, pack(git_no_https(GitURL))), 2601 fail. 2602 2603 2604 /******************************* 2605 * PROPERTIES * 2606 *******************************/
README
file (if present)TODO
file (if present)2629pack_property(Pack, Property) :- 2630 findall(Pack-Property, pack_property_(Pack, Property), List), 2631 member(Pack-Property, List). % make det if applicable 2632 2633pack_property_(Pack, Property) :- 2634 pack_info(Pack, _, Property). 2635pack_property_(Pack, Property) :- 2636 \+ \+ info_file(Property, _), 2637 '$pack':pack(Pack, BaseDir), 2638 access_file(BaseDir, read), 2639 directory_files(BaseDir, Files), 2640 member(File, Files), 2641 info_file(Property, Pattern), 2642 downcase_atom(File, Pattern), 2643 directory_file_path(BaseDir, File, InfoFile), 2644 arg(1, Property, InfoFile). 2645 2646info_file(readme(_), 'readme.txt'). 2647info_file(readme(_), 'readme'). 2648info_file(todo(_), 'todo.txt'). 2649info_file(todo(_), 'todo'). 2650 2651 2652 /******************************* 2653 * VERSION LOGIC * 2654 *******************************/
mypack-1.5
.2663pack_version_file(Pack, Version, GitHubRelease) :- 2664 atomic(GitHubRelease), 2665 github_release_url(GitHubRelease, Pack, Version), 2666 !. 2667pack_version_file(Pack, Version, Path) :- 2668 atomic(Path), 2669 file_base_name(Path, File), 2670 no_int_file_name_extension(Base, _Ext, File), 2671 atom_codes(Base, Codes), 2672 ( phrase(pack_version(Pack, Version), Codes), 2673 safe_pack_name(Pack) 2674 -> true 2675 ). 2676 2677no_int_file_name_extension(Base, Ext, File) :- 2678 file_name_extension(Base0, Ext0, File), 2679 \+ atom_number(Ext0, _), 2680 !, 2681 Base = Base0, 2682 Ext = Ext0. 2683no_int_file_name_extension(File, '', File).
2690safe_pack_name(Name) :- 2691 atom_length(Name, Len), 2692 Len >= 3, % demand at least three length 2693 atom_codes(Name, Codes), 2694 maplist(safe_pack_char, Codes), 2695 !. 2696 2697safe_pack_char(C) :- between(0'a, 0'z, C), !. 2698safe_pack_char(C) :- between(0'A, 0'Z, C), !. 2699safe_pack_char(C) :- between(0'0, 0'9, C), !. 2700safe_pack_char(0'_).
2706pack_version(Pack, Version) --> 2707 string(Codes), "-", 2708 version(Parts), 2709 !, 2710 { atom_codes(Pack, Codes), 2711 atomic_list_concat(Parts, '.', Version) 2712 }. 2713 2714version([H|T]) --> 2715 version_part(H), 2716 ( "." 2717 -> version(T) 2718 ; {T=[]} 2719 ). 2720 2721version_part(*) --> "*", !. 2722version_part(Int) --> integer(Int). 2723 2724 2725 /******************************* 2726 * GIT LOGIC * 2727 *******************************/ 2728 2729have_git :- 2730 process_which(path(git), _).
2737git_url(URL, Pack) :- 2738 uri_components(URL, Components), 2739 uri_data(scheme, Components, Scheme), 2740 nonvar(Scheme), % must be full URL 2741 uri_data(path, Components, Path), 2742 ( Scheme == git 2743 -> true 2744 ; git_download_scheme(Scheme), 2745 file_name_extension(_, git, Path) 2746 ; git_download_scheme(Scheme), 2747 catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail) 2748 -> true 2749 ), 2750 file_base_name(Path, PackExt), 2751 ( file_name_extension(Pack, git, PackExt) 2752 -> true 2753 ; Pack = PackExt 2754 ), 2755 ( safe_pack_name(Pack) 2756 -> true 2757 ; domain_error(pack_name, Pack) 2758 ). 2759 2760git_download_scheme(http). 2761git_download_scheme(https).
https:/github.com/<owner>/<pack>/archive/[vV]?<version>.zip'
2770github_release_url(URL, Pack, Version) :- 2771 uri_components(URL, Components), 2772 uri_data(authority, Components, 'github.com'), 2773 uri_data(scheme, Components, Scheme), 2774 download_scheme(Scheme), 2775 uri_data(path, Components, Path), 2776 github_archive_path(Archive,Pack,File), 2777 atomic_list_concat(Archive, /, Path), 2778 file_name_extension(Tag, Ext, File), 2779 github_archive_extension(Ext), 2780 tag_version(Tag, Version), 2781 !. 2782 2783github_archive_path(['',_User,Pack,archive,File],Pack,File). 2784github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File). 2785 2786github_archive_extension(tgz). 2787github_archive_extension(zip).
[vV]?int(\.int)*
.2794tag_version(Tag, Version) :- 2795 version_tag_prefix(Prefix), 2796 atom_concat(Prefix, Version, Tag), 2797 is_version(Version). 2798 2799version_tag_prefix(v). 2800version_tag_prefix('V'). 2801version_tag_prefix('').
2810git_archive_url(URL, Archive, Options) :- 2811 uri_components(URL, Components), 2812 uri_data(authority, Components, 'github.com'), 2813 uri_data(path, Components, Path), 2814 atomic_list_concat(['', User, RepoGit], /, Path), 2815 $, 2816 remove_git_ext(RepoGit, Repo), 2817 git_archive_version(Version, Options), 2818 atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath), 2819 uri_edit([ path(ArchivePath), 2820 host('codeload.github.com') 2821 ], 2822 URL, Archive). 2823git_archive_url(URL, _, _) :- 2824 print_message(error, pack(no_git(URL))), 2825 fail. 2826 2827remove_git_ext(RepoGit, Repo) :- 2828 file_name_extension(Repo, git, RepoGit), 2829 !. 2830remove_git_ext(Repo, Repo). 2831 2832git_archive_version(Version, Options) :- 2833 option(commit(Version), Options), 2834 !. 2835git_archive_version(Version, Options) :- 2836 option(branch(Version), Options), 2837 !. 2838git_archive_version(Version, Options) :- 2839 option(version(Version), Options), 2840 !. 2841git_archive_version('HEAD', _). 2842 2843 /******************************* 2844 * QUERY CENTRAL DB * 2845 *******************************/
2852register_downloads(_, Options) :- 2853 option(register(false), Options), 2854 \+ option(do_publish(_), Options), 2855 !. 2856register_downloads(Infos, Options) :- 2857 convlist(download_data, Infos, Data), 2858 ( Data == [] 2859 -> true 2860 ; query_pack_server(downloaded(Data), Reply, Options), 2861 ( option(do_publish(Pack), Options) 2862 -> ( member(Info, Infos), 2863 Info.pack == Pack 2864 -> true 2865 ), 2866 ( Reply = true(Actions), 2867 memberchk(Pack-Result, Actions) 2868 -> ( registered(Result) 2869 -> true 2870 ; print_message(error, pack(publish_failed(Info, Result))), 2871 fail 2872 ) 2873 ; print_message(error, pack(publish_failed(Info, false))) 2874 ) 2875 ; true 2876 ) 2877 ). 2878 2879registered(git(_URL)). 2880registered(file(_URL)). 2881 2882publish_download(Infos, Options) :- 2883 select_option(publish(Pack), Options, Options1), 2884 !, 2885 register_downloads(Infos, [do_publish(Pack)|Options1]). 2886publish_download(_Infos, _Options). 2887 2888download_data(Info, Data), 2889 Info.get(git) == true => % Git clone 2890 Data = download(URL, Hash, Metadata), 2891 URL = Info.get(downloaded), 2892 pack_git_info(Info.installed, Hash, Metadata). 2893download_data(Info, Data), 2894 _{git_url:URL,hash:Hash} :< Info, Hash \== (-) => 2895 Data = download(URL, Hash, Metadata), % Git downloaded as zip 2896 dir_metadata(Info.installed, Metadata). 2897download_data(Info, Data) => % Archive download. 2898 Data = download(URL, Hash, Metadata), 2899 URL = Info.get(downloaded), 2900 download_url(URL), 2901 pack_status_dir(Info.installed, archive(Archive, URL)), 2902 file_sha1(Archive, Hash), 2903 pack_archive_info(Archive, _Pack, Metadata, _).
2910query_pack_server(Query, Result, Options) :- 2911 ( option(server(ServerOpt), Options) 2912 -> server_url(ServerOpt, ServerBase) 2913 ; setting(server, ServerBase), 2914 ServerBase \== '' 2915 ), 2916 atom_concat(ServerBase, query, Server), 2917 format(codes(Data), '~q.~n', Query), 2918 info_level(Informational, Options), 2919 print_message(Informational, pack(contacting_server(Server))), 2920 setup_call_cleanup( 2921 http_open(Server, In, 2922 [ post(codes(application/'x-prolog', Data)), 2923 header(content_type, ContentType) 2924 ]), 2925 read_reply(ContentType, In, Result), 2926 close(In)), 2927 message_severity(Result, Level, Informational), 2928 print_message(Level, pack(server_reply(Result))). 2929 2930server_url(URL0, URL) :- 2931 uri_components(URL0, Components), 2932 uri_data(scheme, Components, Scheme), 2933 var(Scheme), 2934 !, 2935 atom_concat('https://', URL0, URL1), 2936 server_url(URL1, URL). 2937server_url(URL0, URL) :- 2938 uri_components(URL0, Components), 2939 uri_data(path, Components, ''), 2940 !, 2941 uri_edit([path('/pack/')], URL0, URL). 2942server_url(URL, URL). 2943 2944read_reply(ContentType, In, Result) :- 2945 sub_atom(ContentType, 0, _, _, 'application/x-prolog'), 2946 !, 2947 set_stream(In, encoding(utf8)), 2948 read(In, Result). 2949read_reply(ContentType, In, _Result) :- 2950 read_string(In, 500, String), 2951 print_message(error, pack(no_prolog_response(ContentType, String))), 2952 fail. 2953 2954info_level(Level, Options) :- 2955 option(silent(true), Options), 2956 !, 2957 Level = silent. 2958info_level(informational, _). 2959 2960message_severity(true(_), Informational, Informational). 2961message_severity(false, warning, _). 2962message_severity(exception(_), error, _). 2963 2964 2965 /******************************* 2966 * WILDCARD URIs * 2967 *******************************/
2976available_download_versions(URL, Versions) :- 2977 wildcard_pattern(URL), 2978 github_url(URL, User, Repo), 2979 !, 2980 findall(Version-VersionURL, 2981 github_version(User, Repo, Version, VersionURL), 2982 Versions). 2983available_download_versions(URL, Versions) :- 2984 wildcard_pattern(URL), 2985 !, 2986 file_directory_name(URL, DirURL0), 2987 ensure_slash(DirURL0, DirURL), 2988 print_message(informational, pack(query_versions(DirURL))), 2989 setup_call_cleanup( 2990 http_open(DirURL, In, []), 2991 load_html(stream(In), DOM, 2992 [ syntax_errors(quiet) 2993 ]), 2994 close(In)), 2995 findall(MatchingURL, 2996 absolute_matching_href(DOM, URL, MatchingURL), 2997 MatchingURLs), 2998 ( MatchingURLs == [] 2999 -> print_message(warning, pack(no_matching_urls(URL))) 3000 ; true 3001 ), 3002 versioned_urls(MatchingURLs, VersionedURLs), 3003 sort_version_pairs(VersionedURLs, Versions), 3004 print_message(informational, pack(found_versions(Versions))). 3005available_download_versions(URL, [Version-URL]) :- 3006 ( pack_version_file(_Pack, Version0, URL) 3007 -> Version = Version0 3008 ; Version = '0.0.0' 3009 ).
3015sort_version_pairs(Pairs, Sorted) :- 3016 map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed), 3017 sort(1, @>=, Keyed, SortedKeyed), 3018 pairs_values(SortedKeyed, Sorted). 3019 3020version_pair_sort_key_(Version-_Data, Key) :- 3021 version_sort_key(Version, Key). 3022 3023version_sort_key(Version, Key) :- 3024 split_string(Version, ".", "", Parts), 3025 maplist(number_string, Key, Parts), 3026 !. 3027version_sort_key(Version, _) :- 3028 domain_error(version, Version).
3034github_url(URL, User, Repo) :-
3035 uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3036 atomic_list_concat(['',User,Repo|_], /, Path).
3044github_version(User, Repo, Version, VersionURI) :- 3045 atomic_list_concat(['',repos,User,Repo,tags], /, Path1), 3046 uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)), 3047 setup_call_cleanup( 3048 http_open(ApiUri, In, 3049 [ request_header('Accept'='application/vnd.github.v3+json') 3050 ]), 3051 json_read_dict(In, Dicts), 3052 close(In)), 3053 member(Dict, Dicts), 3054 atom_string(Tag, Dict.name), 3055 tag_version(Tag, Version), 3056 atom_string(VersionURI, Dict.zipball_url). 3057 3058wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *). 3059wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?). 3060 3061ensure_slash(Dir, DirS) :- 3062 ( sub_atom(Dir, _, _, 0, /) 3063 -> DirS = Dir 3064 ; atom_concat(Dir, /, DirS) 3065 ). 3066 3067remove_slash(Dir0, Dir) :- 3068 Dir0 \== '/', 3069 atom_concat(Dir1, /, Dir0), 3070 !, 3071 remove_slash(Dir1, Dir). 3072remove_slash(Dir, Dir). 3073 3074absolute_matching_href(DOM, Pattern, Match) :- 3075 xpath(DOM, //a(@href), HREF), 3076 uri_normalized(HREF, Pattern, Match), 3077 wildcard_match(Pattern, Match). 3078 3079versioned_urls([], []). 3080versioned_urls([H|T0], List) :- 3081 file_base_name(H, File), 3082 ( pack_version_file(_Pack, Version, File) 3083 -> List = [Version-H|T] 3084 ; List = T 3085 ), 3086 versioned_urls(T0, T). 3087 3088 3089 /******************************* 3090 * DEPENDENCIES * 3091 *******************************/
3099pack_provides(Pack, Pack@Version) :- 3100 current_pack(Pack), 3101 once(pack_info(Pack, version, version(Version))). 3102pack_provides(Pack, Provides) :- 3103 findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList), 3104 member(Provides, PrvList). 3105 3106pack_requires(Pack, Requires) :- 3107 current_pack(Pack), 3108 findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList), 3109 member(Requires, ReqList). 3110 3111pack_conflicts(Pack, Conflicts) :- 3112 current_pack(Pack), 3113 findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList), 3114 member(Conflicts, CflList).
3121pack_depends_on(Pack, Dependency) :- 3122 ground(Pack), 3123 !, 3124 pack_requires(Pack, Requires), 3125 \+ is_prolog_token(Requires), 3126 pack_provides(Dependency, Provides), 3127 satisfies_req(Provides, Requires). 3128pack_depends_on(Pack, Dependency) :- 3129 ground(Dependency), 3130 !, 3131 pack_provides(Dependency, Provides), 3132 pack_requires(Pack, Requires), 3133 satisfies_req(Provides, Requires). 3134pack_depends_on(Pack, Dependency) :- 3135 current_pack(Pack), 3136 pack_depends_on(Pack, Dependency).
3143dependents(Pack, Deps) :- 3144 setof(Dep, dependent(Pack, Dep, []), Deps). 3145 3146dependent(Pack, Dep, Seen) :- 3147 pack_depends_on(Dep0, Pack), 3148 \+ memberchk(Dep0, Seen), 3149 ( Dep = Dep0 3150 ; dependent(Dep0, Dep, [Dep0|Seen]) 3151 ).
3157validate_dependencies :- 3158 setof(Issue, pack_dependency_issue(_, Issue), Issues), 3159 !, 3160 print_message(warning, pack(dependency_issues(Issues))). 3161validate_dependencies.
3173pack_dependency_issue(Pack, Issue) :- 3174 current_pack(Pack), 3175 pack_dependency_issue_(Pack, Issue). 3176 3177pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :- 3178 pack_requires(Pack, Requires), 3179 ( is_prolog_token(Requires) 3180 -> \+ prolog_satisfies(Requires) 3181 ; \+ ( pack_provides(_, Provides), 3182 satisfies_req(Provides, Requires) ) 3183 ). 3184pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :- 3185 pack_conflicts(Pack, Conflicts), 3186 ( is_prolog_token(Conflicts) 3187 -> prolog_satisfies(Conflicts) 3188 ; pack_provides(_, Provides), 3189 satisfies_req(Provides, Conflicts) 3190 ). 3191 3192 3193 /******************************* 3194 * RECORD PACK FACTS * 3195 *******************************/
built
if we built it or downloaded
if it was downloaded.true
, pack was installed as dependency.3211pack_assert(PackDir, Fact) :- 3212 must_be(ground, Fact), 3213 findall(Term, pack_status_dir(PackDir, Term), Facts0), 3214 update_facts(Facts0, Fact, Facts), 3215 OpenOptions = [encoding(utf8), lock(exclusive)], 3216 status_file(PackDir, StatusFile), 3217 ( Facts == Facts0 3218 -> true 3219 ; Facts0 \== [], 3220 append(Facts0, New, Facts) 3221 -> setup_call_cleanup( 3222 open(StatusFile, append, Out, OpenOptions), 3223 maplist(write_fact(Out), New), 3224 close(Out)) 3225 ; setup_call_cleanup( 3226 open(StatusFile, write, Out, OpenOptions), 3227 ( write_facts_header(Out), 3228 maplist(write_fact(Out), Facts) 3229 ), 3230 close(Out)) 3231 ). 3232 3233update_facts([], Fact, [Fact]) :- 3234 !. 3235update_facts([H|T], Fact, [Fact|T]) :- 3236 general_pack_fact(Fact, GenFact), 3237 general_pack_fact(H, GenTerm), 3238 GenFact =@= GenTerm, 3239 !. 3240update_facts([H|T0], Fact, [H|T]) :- 3241 update_facts(T0, Fact, T). 3242 3243general_pack_fact(built(Arch, _Version, _How), General) => 3244 General = built(Arch, _, _). 3245general_pack_fact(Term, General), compound(Term) => 3246 compound_name_arity(Term, Name, Arity), 3247 compound_name_arity(General, Name, Arity). 3248general_pack_fact(Term, General) => 3249 General = Term. 3250 3251write_facts_header(Out) :- 3252 format(Out, '% Fact status file. Managed by package manager.~n', []). 3253 3254write_fact(Out, Term) :- 3255 format(Out, '~q.~n', [Term]).
status.db
.3263pack_status(Pack, Fact) :- 3264 current_pack(Pack, PackDir), 3265 pack_status_dir(PackDir, Fact). 3266 3267pack_status_dir(PackDir, Fact) :- 3268 det_if(ground(Fact), pack_status_(PackDir, Fact)). 3269 3270pack_status_(PackDir, Fact) :- 3271 status_file(PackDir, StatusFile), 3272 catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact), 3273 error(existence_error(source_sink, StatusFile), _), 3274 fail). 3275 3276pack_status_term(built(atom, version, oneof([built,downloaded]))). 3277pack_status_term(automatic(boolean)). 3278pack_status_term(archive(atom, atom)).
3288update_automatic(Info) :- 3289 _ = Info.get(dependency_for), 3290 \+ pack_status(Info.installed, automatic(_)), 3291 !, 3292 pack_assert(Info.installed, automatic(true)). 3293update_automatic(Info) :- 3294 pack_assert(Info.installed, automatic(false)). 3295 3296status_file(PackDir, StatusFile) :- 3297 directory_file_path(PackDir, 'status.db', StatusFile). 3298 3299 /******************************* 3300 * USER INTERACTION * 3301 *******************************/ 3302 3303:- multifile prolog:message//1.
3307menu(_Question, _Alternatives, Default, Selection, Options) :- 3308 option(interactive(false), Options), 3309 !, 3310 Selection = Default. 3311menu(Question, Alternatives, Default, Selection, _) :- 3312 length(Alternatives, N), 3313 between(1, 5, _), 3314 print_message(query, Question), 3315 print_menu(Alternatives, Default, 1), 3316 print_message(query, pack(menu(select))), 3317 read_selection(N, Choice), 3318 !, 3319 ( Choice == default 3320 -> Selection = Default 3321 ; nth1(Choice, Alternatives, Selection=_) 3322 -> true 3323 ). 3324 [], _, _) (. 3326print_menu([Value=Label|T], Default, I) :- 3327 ( Value == Default 3328 -> print_message(query, pack(menu(default_item(I, Label)))) 3329 ; print_message(query, pack(menu(item(I, Label)))) 3330 ), 3331 I2 is I + 1, 3332 print_menu(T, Default, I2). 3333 3334read_selection(Max, Choice) :- 3335 get_single_char(Code), 3336 ( answered_default(Code) 3337 -> Choice = default 3338 ; code_type(Code, digit(Choice)), 3339 between(1, Max, Choice) 3340 -> true 3341 ; print_message(warning, pack(menu(reply(1,Max)))), 3342 fail 3343 ).
3351confirm(_Question, Default, Options) :- 3352 Default \== none, 3353 option(interactive(false), Options, true), 3354 !, 3355 Default == yes. 3356confirm(Question, Default, _) :- 3357 between(1, 5, _), 3358 print_message(query, pack(confirm(Question, Default))), 3359 read_yes_no(YesNo, Default), 3360 !, 3361 format(user_error, '~N', []), 3362 YesNo == yes. 3363 3364read_yes_no(YesNo, Default) :- 3365 get_single_char(Code), 3366 code_yes_no(Code, Default, YesNo), 3367 !. 3368 3369code_yes_no(0'y, _, yes). 3370code_yes_no(0'Y, _, yes). 3371code_yes_no(0'n, _, no). 3372code_yes_no(0'N, _, no). 3373code_yes_no(_, none, _) :- !, fail. 3374code_yes_no(C, Default, Default) :- 3375 answered_default(C). 3376 3377answered_default(0'\r). 3378answered_default(0'\n). 3379answered_default(0'\s). 3380 3381 3382 /******************************* 3383 * MESSAGES * 3384 *******************************/ 3385 3386:- multifile prolog:message//1. 3387 3388prologmessage(pack(Message)) --> 3389 message(Message). 3390 3391:- discontiguous 3392 message//1, 3393 label//1. 3394 3395message(invalid_term(pack_info_term, Term)) --> 3396 [ 'Invalid package meta data: ~q'-[Term] ]. 3397message(invalid_term(pack_status_term, Term)) --> 3398 [ 'Invalid package status data: ~q'-[Term] ]. 3399message(directory_exists(Dir)) --> 3400 [ 'Package target directory exists and is not empty:', nl, 3401 '\t~q'-[Dir] 3402 ]. 3403message(already_installed(pack(Pack, Version))) --> 3404 [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ]. 3405message(already_installed(Pack)) --> 3406 [ 'Pack `~w'' is already installed. Package info:'-[Pack] ]. 3407message(kept_foreign(Pack, Arch)) --> 3408 [ 'Found foreign libraries for architecture '-[], 3409 ansi(code, '~q', [Arch]), nl, 3410 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]), 3411 ' to rebuild from sources'-[] 3412 ]. 3413message(no_pack_installed(Pack)) --> 3414 [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ]. 3415message(dependency_issues(Issues)) --> 3416 [ 'The current set of packs has dependency issues:', nl ], 3417 dep_issues(Issues). 3418message(depends(Pack, Deps)) --> 3419 [ 'The following packs depend on `~w\':'-[Pack], nl ], 3420 pack_list(Deps). 3421message(remove(PackDir)) --> 3422 [ 'Removing ~q and contents'-[PackDir] ]. 3423message(remove_existing_pack(PackDir)) --> 3424 [ 'Remove old installation in ~q'-[PackDir] ]. 3425message(download_plan(Plan)) --> 3426 [ ansi(bold, 'Installation plan:', []), nl ], 3427 install_plan(Plan, Actions), 3428 install_label(Actions). 3429message(build_plan(Plan)) --> 3430 [ ansi(bold, 'The following packs have post install scripts:', []), nl ], 3431 msg_build_plan(Plan), 3432 [ nl, ansi(bold, 'Run scripts?', []) ]. 3433message(no_meta_data(BaseDir)) --> 3434 [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ]. 3435message(search_no_matches(Name)) --> 3436 [ 'Search for "~w", returned no matching packages'-[Name] ]. 3437message(rebuild(Pack)) --> 3438 [ 'Checking pack "~w" for rebuild ...'-[Pack] ]. 3439message(up_to_date([Pack])) --> 3440 !, 3441 [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ]. 3442message(up_to_date(Packs)) --> 3443 [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ]. 3444message(installed_can_upgrade(List)) --> 3445 sequence(msg_can_upgrade_target, [nl], List). 3446message(new_dependencies(Deps)) --> 3447 [ 'Found new dependencies after downloading (~p).'-[Deps], nl ]. 3448message(query_versions(URL)) --> 3449 [ 'Querying "~w" to find new versions ...'-[URL] ]. 3450message(no_matching_urls(URL)) --> 3451 [ 'Could not find any matching URL: ~q'-[URL] ]. 3452message(found_versions([Latest-_URL|More])) --> 3453 { length(More, Len) }, 3454 [ ' Latest version: ~w (~D older)'-[Latest, Len] ]. 3455message(build(Pack, PackDir)) --> 3456 [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ]. 3457message(contacting_server(Server)) --> 3458 [ 'Contacting server at ~w ...'-[Server], flush ]. 3459message(server_reply(true(_))) --> 3460 [ at_same_line, ' ok'-[] ]. 3461message(server_reply(false)) --> 3462 [ at_same_line, ' done'-[] ]. 3463message(server_reply(exception(E))) --> 3464 [ 'Server reported the following error:'-[], nl ], 3465 '$messages':translate_message(E). 3466message(cannot_create_dir(Alias)) --> 3467 { findall(PackDir, 3468 absolute_file_name(Alias, PackDir, [solutions(all)]), 3469 PackDirs0), 3470 sort(PackDirs0, PackDirs) 3471 }, 3472 [ 'Cannot find a place to create a package directory.'-[], 3473 'Considered:'-[] 3474 ], 3475 candidate_dirs(PackDirs). 3476message(conflict(version, [PackV, FileV])) --> 3477 ['Version mismatch: pack.pl: '-[]], msg_version(PackV), 3478 [', file claims version '-[]], msg_version(FileV). 3479message(conflict(name, [PackInfo, FileInfo])) --> 3480 ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]], 3481 [', file claims ~w: ~p'-[FileInfo]]. 3482message(no_prolog_response(ContentType, String)) --> 3483 [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl, 3484 '~s'-[String] 3485 ]. 3486message(download(begin, Pack, _URL, _DownloadFile)) --> 3487 [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ]. 3488message(download(end, _, _, File)) --> 3489 { size_file(File, Bytes) }, 3490 [ at_same_line, '~D bytes'-[Bytes] ]. 3491message(no_git(URL)) --> 3492 [ 'Cannot install from git repository ', url(URL), '.', nl, 3493 'Cannot find git program and do not know how to download the code', nl, 3494 'from this git service. Please install git and retry.' 3495 ]. 3496message(git_no_https(GitURL)) --> 3497 [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ]. 3498message(git_branch_not_default(Dir, Default, Current)) --> 3499 [ 'GIT current branch on ', url(Dir), ' is not default.', nl, 3500 ' Current branch: ', ansi(code, '~w', [Current]), 3501 ' default: ', ansi(code, '~w', [Default]) 3502 ]. 3503message(git_not_clean(Dir)) --> 3504 [ 'GIT working directory is dirty: ', url(Dir), nl, 3505 'Your repository must be clean before publishing.' 3506 ]. 3507message(git_push) --> 3508 [ 'Push release to GIT origin?' ]. 3509message(git_tag(Tag)) --> 3510 [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ]. 3511message(git_release_tag_not_at_head(Tag)) --> 3512 [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl, 3513 'If you want to update the tag, please run ', 3514 ansi(code, 'git tag -d ~w', [Tag]) 3515 ]. 3516message(git_tag_out_of_sync(Tag)) --> 3517 [ 'Release tag ', ansi(code, '~w', [Tag]), 3518 ' differs from this tag at the origin' 3519 ]. 3520 3521message(publish_failed(Info, Reason)) --> 3522 [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ], 3523 msg_publish_failed(Reason). 3524 3525msg_publish_failed(throw(error(permission_error(register, 3526 pack(_),_URL),_))) --> 3527 [ ' is already registered with a different URL']. 3528msg_publish_failed(download) --> 3529 [' was already published?']. 3530msg_publish_failed(Status) --> 3531 [ ' failed for unknown reason (~p)'-[Status] ]. 3532 3533candidate_dirs([]) --> []. 3534candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T). 3535 % Questions 3536message(resolve_remove) --> 3537 [ nl, 'Please select an action:', nl, nl ]. 3538message(create_pack_dir) --> 3539 [ nl, 'Create directory for packages', nl ]. 3540message(menu(item(I, Label))) --> 3541 [ '~t(~d)~6| '-[I] ], 3542 label(Label). 3543message(menu(default_item(I, Label))) --> 3544 [ '~t(~d)~6| * '-[I] ], 3545 label(Label). 3546message(menu(select)) --> 3547 [ nl, 'Your choice? ', flush ]. 3548message(confirm(Question, Default)) --> 3549 message(Question), 3550 confirm_default(Default), 3551 [ flush ]. 3552message(menu(reply(Min,Max))) --> 3553 ( { Max =:= Min+1 } 3554 -> [ 'Please enter ~w or ~w'-[Min,Max] ] 3555 ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ] 3556 ). 3557 3558 % support predicates 3559dep_issues(Issues) --> 3560 sequence(dep_issue, [nl], Issues). 3561 3562dep_issue(unsatisfied(Pack, Requires)) --> 3563 [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]]. 3564dep_issue(conflicts(Pack, Conflict)) --> 3565 [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3572install_label([link]) --> 3573 !, 3574 [ ansi(bold, 'Activate pack?', []) ]. 3575install_label([unpack]) --> 3576 !, 3577 [ ansi(bold, 'Unpack archive?', []) ]. 3578install_label(_) --> 3579 [ ansi(bold, 'Download packs?', []) ]. 3580 3581install_plan([], []) --> 3582 []. 3583install_plan([H|T], [AH|AT]) --> 3584 install_step(H, AH), [nl], 3585 install_plan(T, AT). 3586 3587install_step(Info, keep) --> 3588 { Info.get(keep) == true }, 3589 !, 3590 [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ], 3591 msg_can_upgrade(Info). 3592install_step(Info, Action) --> 3593 { From = Info.get(upgrade), 3594 VFrom = From.version, 3595 VTo = Info.get(version), 3596 ( cmp_versions(>=, VTo, VFrom) 3597 -> Label = ansi(bold, ' Upgrade ', []) 3598 ; Label = ansi(warning, ' Downgrade ', []) 3599 ) 3600 }, 3601 [ Label ], msg_pack(Info), 3602 [ ' from version ~w to ~w'- [From.version, Info.get(version)] ], 3603 install_from(Info, Action). 3604install_step(Info, Action) --> 3605 { _From = Info.get(upgrade) }, 3606 [ ' Upgrade ' ], msg_pack(Info), 3607 install_from(Info, Action). 3608install_step(Info, Action) --> 3609 { Dep = Info.get(dependency_for) }, 3610 [ ' Install ' ], msg_pack(Info), 3611 [ ' at version ~w as dependency for '-[Info.version], 3612 ansi(code, '~w', [Dep]) 3613 ], 3614 install_from(Info, Action), 3615 msg_downloads(Info). 3616install_step(Info, Action) --> 3617 { Info.get(commit) == 'HEAD' }, 3618 !, 3619 [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ], 3620 install_from(Info, Action), 3621 msg_downloads(Info). 3622install_step(Info, link) --> 3623 { Info.get(link) == true, 3624 uri_file_name(Info.get(url), Dir) 3625 }, 3626 !, 3627 [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ]. 3628install_step(Info, Action) --> 3629 [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ], 3630 install_from(Info, Action), 3631 msg_downloads(Info). 3632install_step(Info, Action) --> 3633 [ ' Install ' ], msg_pack(Info), 3634 install_from(Info, Action), 3635 msg_downloads(Info). 3636 3637install_from(Info, download) --> 3638 { download_url(Info.url) }, 3639 !, 3640 [ ' from ', url(Info.url) ]. 3641install_from(Info, unpack) --> 3642 [ ' from ', url(Info.url) ]. 3643 3644msg_downloads(Info) --> 3645 { Downloads = Info.get(all_downloads), 3646 Downloads > 0 3647 }, 3648 [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ], 3649 !. 3650msg_downloads(_) --> 3651 []. 3652 3653msg_pack(Pack) --> 3654 { atom(Pack) }, 3655 !, 3656 [ ansi(code, '~w', [Pack]) ]. 3657msg_pack(Info) --> 3658 msg_pack(Info.pack).
3664msg_build_plan(Plan) --> 3665 sequence(build_step, [nl], Plan). 3666 3667build_step(Info) --> 3668 [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ]. 3669 3670msg_can_upgrade_target(Info) --> 3671 [ ' Pack ' ], msg_pack(Info), 3672 [ ' is installed at version ~w'-[Info.version] ], 3673 msg_can_upgrade(Info). 3674 3675pack_list([]) --> []. 3676pack_list([H|T]) --> 3677 [ ' - Pack ' ], msg_pack(H), [nl], 3678 pack_list(T). 3679 3680label(remove_only(Pack)) --> 3681 [ 'Only remove package ~w (break dependencies)'-[Pack] ]. 3682label(remove_deps(Pack, Deps)) --> 3683 { length(Deps, Count) }, 3684 [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ]. 3685label(create_dir(Dir)) --> 3686 [ '~w'-[Dir] ]. 3687label(install_from(git(URL))) --> 3688 !, 3689 [ 'GIT repository at ~w'-[URL] ]. 3690label(install_from(URL)) --> 3691 [ '~w'-[URL] ]. 3692label(cancel) --> 3693 [ 'Cancel' ]. 3694 3695confirm_default(yes) --> 3696 [ ' Y/n? ' ]. 3697confirm_default(no) --> 3698 [ ' y/N? ' ]. 3699confirm_default(none) --> 3700 [ ' y/n? ' ]. 3701 3702msg_version(Version) --> 3703 [ '~w'-[Version] ]. 3704 3705msg_can_upgrade(Info) --> 3706 { Latest = Info.get(latest_version) }, 3707 [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ]. 3708msg_can_upgrade(_) --> 3709 []. 3710 3711 3712 /******************************* 3713 * MISC * 3714 *******************************/ 3715 3716local_uri_file_name(URL, FileName) :- 3717 uri_file_name(URL, FileName), 3718 !. 3719local_uri_file_name(URL, FileName) :- 3720 uri_components(URL, Components), 3721 uri_data(scheme, Components, File), File == file, 3722 uri_data(authority, Components, FileNameEnc), 3723 uri_data(path, Components, ''), 3724 uri_encoded(path, FileName, FileNameEnc). 3725 3726det_if(Cond, Goal) :- 3727 ( 3728 -> , 3729 ! 3730 ; 3731 ). 3732 3733member_nonvar(_, Var) :- 3734 var(Var), 3735 !, 3736 fail. 3737member_nonvar(E, [E|_]). 3738member_nonvar(E, [_|T]) :- 3739 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*/