36
37:- module(prolog_pack,
38 [ pack_list_installed/0,
39 pack_info/1, 40 pack_list/1, 41 pack_list/2, 42 pack_search/1, 43 pack_install/1, 44 pack_install/2, 45 pack_install_local/3, 46 pack_upgrade/1, 47 pack_rebuild/1, 48 pack_rebuild/0, 49 pack_remove/1, 50 pack_remove/2, 51 pack_publish/2, 52 pack_property/2 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),
70 [pairs_keys/2, map_list_to_pairs/3, pairs_values/2]). 71:- autoload(library(git)). 72:- autoload(library(sgml)). 73:- autoload(library(sha)). 74:- autoload(library(build/tools)). 75:- autoload(library(ansi_term), [ansi_format/3]). 76:- autoload(library(pprint), [print_term/2]). 77:- autoload(library(prolog_versions), [require_version/3, cmp_versions/3]). 78:- autoload(library(ugraphs), [vertices_edges_to_ugraph/3, ugraph_layers/2]). 79:- autoload(library(process), [process_which/2]). 80:- autoload(library(aggregate), [aggregate_all/3]). 81
82:- meta_predicate
83 pack_install_local(2, +, +). 84
97
98 101
102:- setting(server, atom, 'https://www.swi-prolog.org/pack/',
103 'Server to exchange pack information'). 104
105
106 109
110:- op(900, xfx, @). 111
112:- meta_predicate det_if(0,0). 113
114 117
122
123current_pack(Pack) :-
124 current_pack(Pack, _).
125
126current_pack(Pack, Dir) :-
127 '$pack':pack(Pack, Dir).
128
133
134pack_list_installed :-
135 pack_list('', [installed(true)]),
136 validate_dependencies.
137
141
142pack_info(Name) :-
143 pack_info(info, Name).
144
145pack_info(Level, Name) :-
146 must_be(atom, Name),
147 findall(Info, pack_info(Name, Level, Info), Infos0),
148 ( Infos0 == []
149 -> print_message(warning, pack(no_pack_installed(Name))),
150 fail
151 ; true
152 ),
153 findall(Def, pack_default(Level, Infos, Def), Defs),
154 append(Infos0, Defs, Infos1),
155 sort(Infos1, Infos),
156 show_info(Name, Infos, [info(Level)]).
157
158
159show_info(_Name, _Properties, Options) :-
160 option(silent(true), Options),
161 !.
162show_info(_Name, _Properties, Options) :-
163 option(show_info(false), Options),
164 !.
165show_info(Name, Properties, Options) :-
166 option(info(list), Options),
167 !,
168 memberchk(title(Title), Properties),
169 memberchk(version(Version), Properties),
170 format('i ~w@~w ~28|- ~w~n', [Name, Version, Title]).
171show_info(Name, Properties, _) :-
172 !,
173 print_property_value('Package'-'~w', [Name]),
174 findall(Term, pack_level_info(info, Term, _, _), Terms),
175 maplist(print_property(Properties), Terms).
176
177print_property(_, nl) :-
178 !,
179 format('~n').
180print_property(Properties, Term) :-
181 findall(Term, member(Term, Properties), Terms),
182 Terms \== [],
183 !,
184 pack_level_info(_, Term, LabelFmt, _Def),
185 ( LabelFmt = Label-FmtElem
186 -> true
187 ; Label = LabelFmt,
188 FmtElem = '~w'
189 ),
190 multi_valued(Terms, FmtElem, FmtList, Values),
191 atomic_list_concat(FmtList, ', ', Fmt),
192 print_property_value(Label-Fmt, Values).
193print_property(_, _).
194
195multi_valued([H], LabelFmt, [LabelFmt], Values) :-
196 !,
197 H =.. [_|Values].
198multi_valued([H|T], LabelFmt, [LabelFmt|LT], Values) :-
199 H =.. [_|VH],
200 append(VH, MoreValues, Values),
201 multi_valued(T, LabelFmt, LT, MoreValues).
202
203
204pvalue_column(29).
205print_property_value(Prop-Fmt, Values) :-
206 !,
207 pvalue_column(C),
208 atomic_list_concat(['~w:~t~*|', Fmt, '~n'], Format),
209 format(Format, [Prop,C|Values]).
210
211pack_info(Name, Level, Info) :-
212 '$pack':pack(Name, BaseDir),
213 pack_dir_info(BaseDir, Level, Info).
214
215pack_dir_info(BaseDir, Level, Info) :-
216 ( Info = directory(BaseDir)
217 ; pack_info_term(BaseDir, Info)
218 ),
219 pack_level_info(Level, Info, _Format, _Default).
220
221:- public pack_level_info/4. 222
223pack_level_info(_, title(_), 'Title', '<no title>').
224pack_level_info(_, version(_), 'Installed version', '<unknown>').
225pack_level_info(info, automatic(_), 'Automatic (dependency only)', -).
226pack_level_info(info, directory(_), 'Installed in directory', -).
227pack_level_info(info, link(_), 'Installed as link to'-'~w', -).
228pack_level_info(info, built(_,_), 'Built on'-'~w for SWI-Prolog ~w', -).
229pack_level_info(info, author(_, _), 'Author'-'~w <~w>', -).
230pack_level_info(info, maintainer(_, _), 'Maintainer'-'~w <~w>', -).
231pack_level_info(info, packager(_, _), 'Packager'-'~w <~w>', -).
232pack_level_info(info, home(_), 'Home page', -).
233pack_level_info(info, download(_), 'Download URL', -).
234pack_level_info(_, provides(_), 'Provides', -).
235pack_level_info(_, requires(_), 'Requires', -).
236pack_level_info(_, conflicts(_), 'Conflicts with', -).
237pack_level_info(_, replaces(_), 'Replaces packages', -).
238pack_level_info(info, library(_), 'Provided libraries', -).
239
240pack_default(Level, Infos, Def) :-
241 pack_level_info(Level, ITerm, _Format, Def),
242 Def \== (-),
243 \+ memberchk(ITerm, Infos).
244
248
249pack_info_term(BaseDir, Info) :-
250 directory_file_path(BaseDir, 'pack.pl', InfoFile),
251 catch(
252 term_in_file(valid_term(pack_info_term), InfoFile, Info),
253 error(existence_error(source_sink, InfoFile), _),
254 ( print_message(error, pack(no_meta_data(BaseDir))),
255 fail
256 )).
257pack_info_term(BaseDir, library(Lib)) :-
258 atom_concat(BaseDir, '/prolog/', LibDir),
259 atom_concat(LibDir, '*.pl', Pattern),
260 expand_file_name(Pattern, Files),
261 maplist(atom_concat(LibDir), Plain, Files),
262 convlist(base_name, Plain, Libs),
263 member(Lib, Libs).
264pack_info_term(BaseDir, automatic(Boolean)) :-
265 once(pack_status_dir(BaseDir, automatic(Boolean))).
266pack_info_term(BaseDir, built(Arch, Prolog)) :-
267 pack_status_dir(BaseDir, built(Arch, Prolog, _How)).
268pack_info_term(BaseDir, link(Dest)) :-
269 read_link(BaseDir, _, Dest).
270
271base_name(File, Base) :-
272 file_name_extension(Base, pl, File).
273
277
278:- meta_predicate
279 term_in_file(1, +, -). 280
281term_in_file(Valid, File, Term) :-
282 exists_file(File),
283 setup_call_cleanup(
284 open(File, read, In, [encoding(utf8)]),
285 term_in_stream(Valid, In, Term),
286 close(In)).
287
288term_in_stream(Valid, In, Term) :-
289 repeat,
290 read_term(In, Term0, []),
291 ( Term0 == end_of_file
292 -> !, fail
293 ; Term = Term0,
294 call(Valid, Term0)
295 ).
296
297:- meta_predicate
298 valid_term(1,+). 299
300valid_term(Type, Term) :-
301 Term =.. [Name|Args],
302 same_length(Args, Types),
303 Decl =.. [Name|Types],
304 ( call(Type, Decl)
305 -> maplist(valid_info_arg, Types, Args)
306 ; print_message(warning, pack(invalid_term(Type, Term))),
307 fail
308 ).
309
310valid_info_arg(Type, Arg) :-
311 must_be(Type, Arg).
312
317
318pack_info_term(name(atom)). 319pack_info_term(title(atom)).
320pack_info_term(keywords(list(atom))).
321pack_info_term(description(list(atom))).
322pack_info_term(version(version)).
323pack_info_term(author(atom, email_or_url_or_empty)). 324pack_info_term(maintainer(atom, email_or_url)).
325pack_info_term(packager(atom, email_or_url)).
326pack_info_term(pack_version(nonneg)). 327pack_info_term(home(atom)). 328pack_info_term(download(atom)). 329pack_info_term(provides(atom)). 330pack_info_term(requires(dependency)).
331pack_info_term(conflicts(dependency)). 332pack_info_term(replaces(atom)). 333pack_info_term(autoload(boolean)). 334
335:- multifile
336 error:has_type/2. 337
338error:has_type(version, Version) :-
339 atom(Version),
340 is_version(Version).
341error:has_type(email_or_url, Address) :-
342 atom(Address),
343 ( sub_atom(Address, _, _, _, @)
344 -> true
345 ; uri_is_global(Address)
346 ).
347error:has_type(email_or_url_or_empty, Address) :-
348 ( Address == ''
349 -> true
350 ; error:has_type(email_or_url, Address)
351 ).
352error:has_type(dependency, Value) :-
353 is_dependency(Value).
354
355is_version(Version) :-
356 split_string(Version, ".", "", Parts),
357 maplist(number_string, _, Parts).
358
359is_dependency(Var) :-
360 var(Var),
361 !,
362 fail.
363is_dependency(Token) :-
364 atom(Token),
365 !.
366is_dependency(Term) :-
367 compound(Term),
368 compound_name_arguments(Term, Op, [Token,Version]),
369 atom(Token),
370 cmp(Op, _),
371 is_version(Version),
372 !.
373is_dependency(PrologToken) :-
374 is_prolog_token(PrologToken).
375
376cmp(<, @<).
377cmp(=<, @=<).
378cmp(==, ==).
379cmp(>=, @>=).
380cmp(>, @>).
381
382
383 386
426
427pack_list(Query) :-
428 pack_list(Query, []).
429
430pack_search(Query) :-
431 pack_list(Query, []).
432
433pack_list(Query, Options) :-
434 ( option(installed(true), Options)
435 ; option(outdated(true), Options)
436 ; option(server(false), Options)
437 ),
438 !,
439 local_search(Query, Local),
440 maplist(arg(1), Local, Packs),
441 ( option(server(false), Options)
442 -> Hits = []
443 ; query_pack_server(info(Packs), true(Hits), Options)
444 ),
445 list_hits(Hits, Local, Options).
446pack_list(Query, Options) :-
447 query_pack_server(search(Query), Result, Options),
448 ( Result == false
449 -> ( local_search(Query, Packs),
450 Packs \== []
451 -> forall(member(pack(Pack, Stat, Title, Version, _), Packs),
452 format('~w ~w@~w ~28|- ~w~n',
453 [Stat, Pack, Version, Title]))
454 ; print_message(warning, pack(search_no_matches(Query)))
455 )
456 ; Result = true(Hits), 457 local_search(Query, Local),
458 list_hits(Hits, Local, [])
459 ).
460
461list_hits(Hits, Local, Options) :-
462 append(Hits, Local, All),
463 sort(All, Sorted),
464 join_status(Sorted, Packs0),
465 include(filtered(Options), Packs0, Packs),
466 maplist(list_hit(Options), Packs).
467
468filtered(Options, pack(_,Tag,_,_,_)) :-
469 option(outdated(true), Options),
470 !,
471 Tag == 'U'.
472filtered(_, _).
473
474list_hit(_Options, pack(Pack, Tag, Title, Version, _URL)) =>
475 list_tag(Tag),
476 ansi_format(code, '~w', [Pack]),
477 format('@'),
478 list_version(Tag, Version),
479 format('~35|- ', []),
480 ansi_format(comment, '~w~n', [Title]).
481
482list_tag(Tag) :-
483 tag_color(Tag, Color),
484 ansi_format(Color, '~w ', [Tag]).
485
486list_version(Tag, VersionI-VersionS) =>
487 tag_color(Tag, Color),
488 ansi_format(Color, '~w', [VersionI]),
489 ansi_format(bold, '(~w)', [VersionS]).
490list_version(_Tag, Version) =>
491 ansi_format([], '~w', [Version]).
492
493tag_color('U', warning) :- !.
494tag_color('A', comment) :- !.
495tag_color(_, []).
496
503
504join_status([], []).
505join_status([ pack(Pack, i, Title, Version, URL),
506 pack(Pack, p, Title, Version, _)
507 | T0
508 ],
509 [ pack(Pack, Tag, Title, Version, URL)
510 | T
511 ]) :-
512 !,
513 ( pack_status(Pack, automatic(true))
514 -> Tag = a
515 ; Tag = i
516 ),
517 join_status(T0, T).
518join_status([ pack(Pack, i, Title, VersionI, URLI),
519 pack(Pack, p, _, VersionS, URLS)
520 | T0
521 ],
522 [ pack(Pack, Tag, Title, VersionI-VersionS, URLI-URLS)
523 | T
524 ]) :-
525 !,
526 version_sort_key(VersionI, VDI),
527 version_sort_key(VersionS, VDS),
528 ( VDI @< VDS
529 -> Tag = 'U'
530 ; Tag = 'A'
531 ),
532 join_status(T0, T).
533join_status([ pack(Pack, i, Title, VersionI, URL)
534 | T0
535 ],
536 [ pack(Pack, l, Title, VersionI, URL)
537 | T
538 ]) :-
539 !,
540 join_status(T0, T).
541join_status([H|T0], [H|T]) :-
542 join_status(T0, T).
543
547
548local_search(Query, Packs) :-
549 findall(Pack, matching_installed_pack(Query, Pack), Packs).
550
551matching_installed_pack(Query, pack(Pack, i, Title, Version, URL)) :-
552 current_pack(Pack),
553 findall(Term,
554 ( pack_info(Pack, _, Term),
555 search_info(Term)
556 ), Info),
557 ( sub_atom_icasechk(Pack, _, Query)
558 -> true
559 ; memberchk(title(Title), Info),
560 sub_atom_icasechk(Title, _, Query)
561 ),
562 option(title(Title), Info, '<no title>'),
563 option(version(Version), Info, '<no version>'),
564 option(download(URL), Info, '<no download url>').
565
566search_info(title(_)).
567search_info(version(_)).
568search_info(download(_)).
569
570
571 574
672
673pack_install(Spec) :-
674 pack_default_options(Spec, Pack, [], Options),
675 pack_install(Pack, [pack(Pack)|Options]).
676
677pack_install(Specs, Options) :-
678 is_list(Specs),
679 !,
680 maplist(pack_options(Options), Specs, Pairs),
681 pack_install_dir(PackTopDir, Options),
682 pack_install_set(Pairs, PackTopDir, Options).
683pack_install(Spec, Options) :-
684 pack_default_options(Spec, Pack, Options, DefOptions),
685 ( option(already_installed(Installed), DefOptions)
686 -> print_message(informational, pack(already_installed(Installed)))
687 ; merge_options(Options, DefOptions, PackOptions),
688 pack_install_dir(PackTopDir, PackOptions),
689 pack_install_set([Pack-PackOptions], PackTopDir, Options)
690 ).
691
692pack_options(Options, Spec, Pack-PackOptions) :-
693 pack_default_options(Spec, Pack, Options, DefOptions),
694 merge_options(Options, DefOptions, PackOptions).
695
718
719
720pack_default_options(_Spec, Pack, OptsIn, Options) :- 721 option(already_installed(pack(Pack,_Version)), OptsIn),
722 !,
723 Options = OptsIn.
724pack_default_options(_Spec, Pack, OptsIn, Options) :- 725 option(url(URL), OptsIn),
726 !,
727 ( option(git(_), OptsIn)
728 -> Options = OptsIn
729 ; git_url(URL, Pack)
730 -> Options = [git(true)|OptsIn]
731 ; Options = OptsIn
732 ),
733 ( nonvar(Pack)
734 -> true
735 ; option(pack(Pack), Options)
736 -> true
737 ; pack_version_file(Pack, _Version, URL)
738 ).
739pack_default_options(Archive, Pack, OptsIn, Options) :- 740 must_be(atom, Archive),
741 \+ uri_is_global(Archive),
742 expand_file_name(Archive, [File]),
743 exists_file(File),
744 !,
745 ( pack_version_file(Pack, Version, File)
746 -> uri_file_name(FileURL, File),
747 merge_options([url(FileURL), version(Version)], OptsIn, Options)
748 ; domain_error(pack_file_name, Archive)
749 ).
750pack_default_options(URL, Pack, OptsIn, Options) :- 751 git_url(URL, Pack),
752 !,
753 merge_options([git(true), url(URL)], OptsIn, Options).
754pack_default_options(FileURL, Pack, _, Options) :- 755 uri_file_name(FileURL, Dir),
756 exists_directory(Dir),
757 pack_info_term(Dir, name(Pack)),
758 !,
759 ( pack_info_term(Dir, version(Version))
760 -> uri_file_name(DirURL, Dir),
761 Options = [url(DirURL), version(Version)]
762 ; throw(error(existence_error(key, version, Dir),_))
763 ).
764pack_default_options('.', Pack, OptsIn, Options) :- 765 pack_info_term('.', name(Pack)),
766 !,
767 working_directory(Dir, Dir),
768 ( pack_info_term(Dir, version(Version))
769 -> uri_file_name(DirURL, Dir),
770 NewOptions = [url(DirURL), version(Version) | Options1],
771 ( current_prolog_flag(windows, true)
772 -> Options1 = []
773 ; Options1 = [link(true), rebuild(make)]
774 ),
775 merge_options(NewOptions, OptsIn, Options)
776 ; throw(error(existence_error(key, version, Dir),_))
777 ).
778pack_default_options(URL, Pack, OptsIn, Options) :- 779 pack_version_file(Pack, Version, URL),
780 download_url(URL),
781 !,
782 available_download_versions(URL, Available, Options),
783 Available = [URLVersion-LatestURL|_],
784 NewOptions = [url(LatestURL)|VersionOptions],
785 version_options(Version, URLVersion, Available, VersionOptions),
786 merge_options(NewOptions, OptsIn, Options).
787pack_default_options(Pack, Pack, Options, Options) :- 788 \+ uri_is_global(Pack).
789
790version_options(Version, Version, _, [version(Version)]) :- !.
791version_options(Version, _, Available, [versions(Available)]) :-
792 sub_atom(Version, _, _, _, *),
793 !.
794version_options(_, _, _, []).
795
813
814pack_install_dir(PackDir, Options) :-
815 option(pack_directory(PackDir), Options),
816 ensure_directory(PackDir),
817 !.
818pack_install_dir(PackDir, Options) :-
819 base_alias(Alias, Options),
820 absolute_file_name(Alias, PackDir,
821 [ file_type(directory),
822 access(write),
823 file_errors(fail)
824 ]),
825 !.
826pack_install_dir(PackDir, Options) :-
827 pack_create_install_dir(PackDir, Options).
828
829base_alias(Alias, Options) :-
830 option(global(true), Options),
831 !,
832 Alias = common_app_data(pack).
833base_alias(Alias, Options) :-
834 option(global(false), Options),
835 !,
836 Alias = user_app_data(pack).
837base_alias(Alias, _Options) :-
838 Alias = pack('.').
839
840pack_create_install_dir(PackDir, Options) :-
841 base_alias(Alias, Options),
842 findall(Candidate = create_dir(Candidate),
843 ( absolute_file_name(Alias, Candidate, [solutions(all)]),
844 \+ exists_file(Candidate),
845 \+ exists_directory(Candidate),
846 file_directory_name(Candidate, Super),
847 ( exists_directory(Super)
848 -> access_file(Super, write)
849 ; true
850 )
851 ),
852 Candidates0),
853 list_to_set(Candidates0, Candidates), 854 pack_create_install_dir(Candidates, PackDir, Options).
855
856pack_create_install_dir(Candidates, PackDir, Options) :-
857 Candidates = [Default=_|_],
858 !,
859 append(Candidates, [cancel=cancel], Menu),
860 menu(pack(create_pack_dir), Menu, Default, Selected, Options),
861 Selected \== cancel,
862 ( catch(make_directory_path(Selected), E,
863 (print_message(warning, E), fail))
864 -> PackDir = Selected
865 ; delete(Candidates, PackDir=create_dir(PackDir), Remaining),
866 pack_create_install_dir(Remaining, PackDir, Options)
867 ).
868pack_create_install_dir(_, _, _) :-
869 print_message(error, pack(cannot_create_dir(pack(.)))),
870 fail.
871
883
884pack_unpack_from_local(Source0, PackTopDir, Name, PackDir, Options) :-
885 exists_directory(Source0),
886 remove_slash(Source0, Source),
887 !,
888 directory_file_path(PackTopDir, Name, PackDir),
889 ( option(link(true), Options)
890 -> ( same_file(Source, PackDir)
891 -> true
892 ; remove_existing_pack(PackDir, Options),
893 atom_concat(PackTopDir, '/', PackTopDirS),
894 relative_file_name(Source, PackTopDirS, RelPath),
895 link_file(RelPath, PackDir, symbolic),
896 assertion(same_file(Source, PackDir))
897 )
898 ; \+ option(git(false), Options),
899 is_git_directory(Source)
900 -> remove_existing_pack(PackDir, Options),
901 run_process(path(git), [clone, Source, PackDir], [])
902 ; prepare_pack_dir(PackDir, Options),
903 copy_directory(Source, PackDir)
904 ).
905pack_unpack_from_local(Source, PackTopDir, Name, PackDir, Options) :-
906 exists_file(Source),
907 directory_file_path(PackTopDir, Name, PackDir),
908 prepare_pack_dir(PackDir, Options),
909 pack_unpack(Source, PackDir, Name, Options).
910
917
918:- if(exists_source(library(archive))). 919pack_unpack(Source, PackDir, Pack, Options) :-
920 ensure_loaded_archive,
921 pack_archive_info(Source, Pack, _Info, StripOptions),
922 prepare_pack_dir(PackDir, Options),
923 archive_extract(Source, PackDir,
924 [ exclude(['._*']) 925 | StripOptions
926 ]).
927:- else. 928pack_unpack(_,_,_,_) :-
929 existence_error(library, archive).
930:- endif. 931
937
938pack_install_local(M:Gen, Dir, Options) :-
939 findall(Pack-PackOptions, call(M:Gen, Pack, PackOptions), Pairs),
940 pack_install_set(Pairs, Dir, Options).
941
942pack_install_set(Pairs, Dir, Options) :-
943 must_be(list(pair), Pairs),
944 ensure_directory(Dir),
945 partition(known_media, Pairs, Local, Remote),
946 maplist(pack_options_to_versions, Local, LocalVersions),
947 ( Remote == []
948 -> AllVersions = LocalVersions
949 ; pairs_keys(Remote, Packs),
950 prolog_description(Properties),
951 query_pack_server(versions(Packs, Properties), Result, Options),
952 ( Result = true(RemoteVersions)
953 -> append(LocalVersions, RemoteVersions, AllVersions)
954 ; print_message(error, pack(query_failed(Result))),
955 fail
956 )
957 ),
958 local_packs(Dir, Existing),
959 pack_resolve(Pairs, Existing, AllVersions, Plan0, Options),
960 !, 961 maplist(hsts_info(Options), Plan0, Plan),
962 Options1 = [pack_directory(Dir)|Options],
963 download_plan(Pairs, Plan, PlanB, Options1),
964 register_downloads(PlanB, Options),
965 maplist(update_automatic, PlanB),
966 build_plan(PlanB, Built, Options1),
967 publish_download(PlanB, Options),
968 work_done(Pairs, Plan, PlanB, Built, Options).
969
970hsts_info(Options, Info0, Info) :-
971 hsts(Info0.get(url), URL, Options),
972 !,
973 Info = Info0.put(url, URL).
974hsts_info(_Options, Info, Info).
975
982
983known_media(_-Options) :-
984 option(url(_), Options).
985
1001
1002pack_resolve(Pairs, Existing, Versions, Plan, Options) :-
1003 insert_existing(Existing, Versions, AllVersions, Options),
1004 phrase(select_version(Pairs, AllVersions,
1005 [ plan(PlanA), 1006 dependency_for([]) 1007 | Options
1008 ]),
1009 PlanA),
1010 mark_installed(PlanA, Existing, Plan).
1011
1020
1021:- det(insert_existing/4). 1022insert_existing(Existing, [], Versions, _Options) =>
1023 maplist(existing_to_versions, Existing, Versions).
1024insert_existing(Existing, [Pack-Versions|T0], AllPackVersions, Options),
1025 select(Installed, Existing, Existing2),
1026 Installed.pack == Pack =>
1027 can_upgrade(Installed, Versions, Installed2),
1028 insert_existing_(Installed2, Versions, AllVersions, Options),
1029 AllPackVersions = [Pack-AllVersions|T],
1030 insert_existing(Existing2, T0, T, Options).
1031insert_existing(Existing, [H|T0], AllVersions, Options) =>
1032 AllVersions = [H|T],
1033 insert_existing(Existing, T0, T, Options).
1034
1035existing_to_versions(Installed, Pack-[Version-[Installed]]) :-
1036 Pack = Installed.pack,
1037 Version = Installed.version.
1038
1039insert_existing_(Installed, Versions, AllVersions, Options) :-
1040 option(upgrade(true), Options),
1041 !,
1042 insert_existing_(Installed, Versions, AllVersions).
1043insert_existing_(Installed, Versions, AllVersions, _) :-
1044 AllVersions = [Installed.version-[Installed]|Versions].
1045
1046insert_existing_(Installed, [H|T0], [H|T]) :-
1047 H = V0-_Infos,
1048 cmp_versions(>, V0, Installed.version),
1049 !,
1050 insert_existing_(Installed, T0, T).
1051insert_existing_(Installed, [H0|T], [H|T]) :-
1052 H0 = V0-Infos,
1053 V0 == Installed.version,
1054 !,
1055 H = V0-[Installed|Infos].
1056insert_existing_(Installed, Versions, All) :-
1057 All = [Installed.version-[Installed]|Versions].
1058
1063
1064can_upgrade(Info, [Version-_|_], Info2) :-
1065 cmp_versions(>, Version, Info.version),
1066 !,
1067 Info2 = Info.put(latest_version, Version).
1068can_upgrade(Info, _, Info).
1069
1075
1076mark_installed([], _, []).
1077mark_installed([Info|T], Existing, Plan) :-
1078 ( member(Installed, Existing),
1079 Installed.pack == Info.pack
1080 -> ( ( Installed.git == true
1081 -> Info.git == true,
1082 Installed.hash == Info.hash
1083 ; Version = Info.get(version)
1084 -> Installed.version == Version
1085 )
1086 -> Plan = [Info.put(keep, true)|PlanT] 1087 ; Plan = [Info.put(upgrade, Installed)|PlanT] 1088 )
1089 ; Plan = [Info|PlanT] 1090 ),
1091 mark_installed(T, Existing, PlanT).
1092
1098
1099select_version([], _, _) -->
1100 [].
1101select_version([Pack-PackOptions|More], Versions, Options) -->
1102 { memberchk(Pack-PackVersions, Versions),
1103 member(Version-Infos, PackVersions),
1104 compatible_version(Pack, Version, PackOptions),
1105 member(Info, Infos),
1106 pack_options_compatible_with_info(Info, PackOptions),
1107 pack_satisfies(Pack, Version, Info, Info2, PackOptions),
1108 all_downloads(PackVersions, Downloads)
1109 },
1110 add_to_plan(Info2.put(_{version: Version, all_downloads:Downloads}),
1111 Versions, Options),
1112 select_version(More, Versions, Options).
1113select_version([Pack-_PackOptions|_More], _Versions, _Options) -->
1114 { existence_error(pack, Pack) }. 1115
1116all_downloads(PackVersions, AllDownloads) :-
1117 aggregate_all(sum(Downloads),
1118 ( member(_Version-Infos, PackVersions),
1119 member(Info, Infos),
1120 get_dict(downloads, Info, Downloads)
1121 ),
1122 AllDownloads).
1123
1124add_requirements([], _, _) -->
1125 [].
1126add_requirements([H|T], Versions, Options) -->
1127 { is_prolog_token(H),
1128 !,
1129 prolog_satisfies(H)
1130 },
1131 add_requirements(T, Versions, Options).
1132add_requirements([H|T], Versions, Options) -->
1133 { member(Pack-PackVersions, Versions),
1134 member(Version-Infos, PackVersions),
1135 member(Info, Infos),
1136 ( Provides = @(Pack,Version)
1137 ; member(Provides, Info.get(provides))
1138 ),
1139 satisfies_req(Provides, H),
1140 all_downloads(PackVersions, Downloads)
1141 },
1142 add_to_plan(Info.put(_{version: Version, all_downloads:Downloads}),
1143 Versions, Options),
1144 add_requirements(T, Versions, Options).
1145
1151
1152add_to_plan(Info, _Versions, Options) -->
1153 { option(plan(Plan), Options),
1154 member_nonvar(Planned, Plan),
1155 Planned.pack == Info.pack,
1156 !,
1157 same_version(Planned, Info) 1158 }.
1159add_to_plan(Info, _Versions, _Options) -->
1160 { member(Conflict, Info.get(conflicts)),
1161 is_prolog_token(Conflict),
1162 prolog_satisfies(Conflict),
1163 !,
1164 fail 1165 }.
1166add_to_plan(Info, _Versions, Options) -->
1167 { option(plan(Plan), Options),
1168 member_nonvar(Planned, Plan),
1169 info_conflicts(Info, Planned), 1170 !,
1171 fail
1172 }.
1173add_to_plan(Info, Versions, Options) -->
1174 { select_option(dependency_for(Dep0), Options, Options1),
1175 Options2 = [dependency_for([Info.pack|Dep0])|Options1],
1176 ( Dep0 = [DepFor|_]
1177 -> add_dependency_for(DepFor, Info, Info1)
1178 ; Info1 = Info
1179 )
1180 },
1181 [Info1],
1182 add_requirements(Info.get(requires,[]), Versions, Options2).
1183
1184add_dependency_for(Pack, Info, Info) :-
1185 Old = Info.get(dependency_for),
1186 !,
1187 b_set_dict(dependency_for, Info, [Pack|Old]).
1188add_dependency_for(Pack, Info0, Info) :-
1189 Info = Info0.put(dependency_for, [Pack]).
1190
1191same_version(Info, Info) :-
1192 !.
1193same_version(Planned, Info) :-
1194 Hash = Planned.get(hash),
1195 Hash \== (-),
1196 !,
1197 Hash == Info.get(hash).
1198same_version(Planned, Info) :-
1199 Planned.get(version) == Info.get(version).
1200
1204
1205info_conflicts(Info, Planned) :-
1206 info_conflicts_(Info, Planned),
1207 !.
1208info_conflicts(Info, Planned) :-
1209 info_conflicts_(Planned, Info),
1210 !.
1211
1212info_conflicts_(Info, Planned) :-
1213 member(Conflict, Info.get(conflicts)),
1214 \+ is_prolog_token(Conflict),
1215 info_provides(Planned, Provides),
1216 satisfies_req(Provides, Conflict),
1217 !.
1218
1219info_provides(Info, Provides) :-
1220 ( Provides = Info.pack@Info.version
1221 ; member(Provides, Info.get(provides))
1222 ).
1223
1228
1229pack_satisfies(_Pack, _Version, Info0, Info, Options) :-
1230 option(commit('HEAD'), Options),
1231 !,
1232 Info0.get(git) == true,
1233 Info = Info0.put(commit, 'HEAD').
1234pack_satisfies(_Pack, _Version, Info, Info, Options) :-
1235 option(commit(Commit), Options),
1236 !,
1237 Commit == Info.get(hash).
1238pack_satisfies(Pack, Version, Info, Info, Options) :-
1239 option(version(ReqVersion), Options),
1240 !,
1241 satisfies_version(Pack, Version, ReqVersion).
1242pack_satisfies(_Pack, _Version, Info, Info, _Options).
1243
1245
1246satisfies_version(Pack, Version, ReqVersion) :-
1247 catch(require_version(pack(Pack), Version, ReqVersion),
1248 error(version_error(pack(Pack), Version, ReqVersion),_),
1249 fail).
1250
1254
1255satisfies_req(Token, Token) => true.
1256satisfies_req(@(Token,_), Token) => true.
1257satisfies_req(@(Token,PrvVersion), Req), cmp(Req, Token, Cmp, ReqVersion) =>
1258 cmp_versions(Cmp, PrvVersion, ReqVersion).
1259satisfies_req(_,_) => fail.
1260
1261cmp(Token < Version, Token, <, Version).
1262cmp(Token =< Version, Token, =<, Version).
1263cmp(Token = Version, Token, =, Version).
1264cmp(Token == Version, Token, ==, Version).
1265cmp(Token >= Version, Token, >=, Version).
1266cmp(Token > Version, Token, >, Version).
1267
1278
1279:- det(pack_options_to_versions/2). 1280pack_options_to_versions(Pack-PackOptions, Pack-Versions) :-
1281 option(versions(Available), PackOptions), !,
1282 maplist(version_url_info(Pack, PackOptions), Available, Versions).
1283pack_options_to_versions(Pack-PackOptions, Pack-[Version-[Info]]) :-
1284 option(url(URL), PackOptions),
1285 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1286 dict_create(Info, #,
1287 [ pack-Pack,
1288 url-URL
1289 | Pairs
1290 ]),
1291 Version = Info.get(version, '0.0.0').
1292
1293version_url_info(Pack, PackOptions, Version-URL, Version-[Info]) :-
1294 findall(Prop,
1295 ( option_info_prop(PackOptions, Prop),
1296 Prop \= version-_
1297 ),
1298 Pairs),
1299 dict_create(Info, #,
1300 [ pack-Pack,
1301 url-URL,
1302 version-Version
1303 | Pairs
1304 ]).
1305
1306option_info_prop(PackOptions, Prop-Value) :-
1307 option_info(Prop),
1308 Opt =.. [Prop,Value],
1309 option(Opt, PackOptions).
1310
1311option_info(git).
1312option_info(hash).
1313option_info(version).
1314option_info(branch).
1315option_info(link).
1316
1321
1322compatible_version(Pack, Version, PackOptions) :-
1323 option(version(ReqVersion), PackOptions),
1324 !,
1325 satisfies_version(Pack, Version, ReqVersion).
1326compatible_version(_, _, _).
1327
1332
1333pack_options_compatible_with_info(Info, PackOptions) :-
1334 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1335 dict_create(Dict, _, Pairs),
1336 Dict >:< Info.
1337
1345
1346download_plan(_Targets, Plan, Plan, _Options) :-
1347 exclude(installed, Plan, []),
1348 !.
1349download_plan(Targets, Plan0, Plan, Options) :-
1350 confirm(download_plan(Plan0), yes, Options),
1351 maplist(download_from_info(Options), Plan0, Plan1),
1352 plan_unsatisfied_dependencies(Plan1, Deps),
1353 ( Deps == []
1354 -> Plan = Plan1
1355 ; print_message(informational, pack(new_dependencies(Deps))),
1356 prolog_description(Properties),
1357 query_pack_server(versions(Deps, Properties), Result, []),
1358 ( Result = true(Versions)
1359 -> pack_resolve(Targets, Plan1, Versions, Plan2, Options),
1360 !,
1361 download_plan(Targets, Plan2, Plan, Options)
1362 ; print_message(error, pack(query_failed(Result))),
1363 fail
1364 )
1365 ).
1366
1371
1372plan_unsatisfied_dependencies(Plan, Deps) :-
1373 phrase(plan_unsatisfied_dependencies(Plan, Plan), Deps).
1374
1375plan_unsatisfied_dependencies([], _) -->
1376 [].
1377plan_unsatisfied_dependencies([Info|Infos], Plan) -->
1378 { Deps = Info.get(requires) },
1379 plan_unsatisfied_requirements(Deps, Plan),
1380 plan_unsatisfied_dependencies(Infos, Plan).
1381
1382plan_unsatisfied_requirements([], _) -->
1383 [].
1384plan_unsatisfied_requirements([H|T], Plan) -->
1385 { is_prolog_token(H), 1386 prolog_satisfies(H)
1387 },
1388 !,
1389 plan_unsatisfied_requirements(T, Plan).
1390plan_unsatisfied_requirements([H|T], Plan) -->
1391 { member(Info, Plan),
1392 ( ( Version = Info.get(version)
1393 -> Provides = @(Info.get(pack), Version)
1394 ; Provides = Info.get(pack)
1395 )
1396 ; member(Provides, Info.get(provides))
1397 ),
1398 satisfies_req(Provides, H)
1399 }, !,
1400 plan_unsatisfied_requirements(T, Plan).
1401plan_unsatisfied_requirements([H|T], Plan) -->
1402 [H],
1403 plan_unsatisfied_requirements(T, Plan).
1404
1405
1411
1412build_plan(Plan, Ordered, Options) :-
1413 partition(needs_rebuild_from_info(Options), Plan, ToBuild, NoBuild),
1414 maplist(attach_from_info(Options), NoBuild),
1415 ( ToBuild == []
1416 -> Ordered = []
1417 ; order_builds(ToBuild, Ordered),
1418 confirm(build_plan(Ordered), yes, Options),
1419 maplist(exec_plan_rebuild_step(Options), Ordered)
1420 ).
1421
1422needs_rebuild_from_info(Options, Info) :-
1423 needs_rebuild(Info.installed, Options).
1424
1428
1429needs_rebuild(PackDir, Options) :-
1430 ( is_foreign_pack(PackDir, _),
1431 \+ is_built(PackDir, Options)
1432 -> true
1433 ; is_autoload_pack(PackDir, Options),
1434 post_install_autoload(PackDir, Options),
1435 fail
1436 ).
1437
1444
1445is_built(PackDir, _Options) :-
1446 current_prolog_flag(arch, Arch),
1447 prolog_version_dotted(Version), 1448 pack_status_dir(PackDir, built(Arch, Version, _)).
1449
1454
1455order_builds(ToBuild, Ordered) :-
1456 findall(DepForPack-Pack, dep_edge(ToBuild, Pack, DepForPack), Edges),
1457 maplist(get_dict(pack), ToBuild, Packs),
1458 vertices_edges_to_ugraph(Packs, Edges, Graph),
1459 ugraph_layers(Graph, Layers),
1460 append(Layers, PackNames),
1461 maplist(pack_info_from_name(ToBuild), PackNames, Ordered).
1462
1467
1468dep_edge(Infos, Pack, DepForPack) :-
1469 member(Info, Infos),
1470 Pack = Info.pack,
1471 member(DepForPack, Info.get(dependency_for)),
1472 ( member(DepInfo, Infos),
1473 DepInfo.pack == DepForPack
1474 -> true
1475 ).
1476
1477:- det(pack_info_from_name/3). 1478pack_info_from_name(Infos, Pack, Info) :-
1479 member(Info, Infos),
1480 Info.pack == Pack,
1481 !.
1482
1486
1487exec_plan_rebuild_step(Options, Info) :-
1488 print_message(informational, pack(build(Info.pack, Info.installed))),
1489 pack_post_install(Info.pack, Info.installed, Options),
1490 attach_from_info(Options, Info).
1491
1495
1496attach_from_info(_Options, Info) :-
1497 Info.get(keep) == true,
1498 !.
1499attach_from_info(Options, Info) :-
1500 ( option(pack_directory(_Parent), Options)
1501 -> pack_attach(Info.installed, [duplicate(replace)])
1502 ; pack_attach(Info.installed, [])
1503 ).
1504
1512
1513download_from_info(Options, Info0, Info), option(dryrun(true), Options) =>
1514 print_term(Info0, [nl(true)]),
1515 Info = Info0.
1516download_from_info(_Options, Info0, Info), installed(Info0) =>
1517 Info = Info0.
1518download_from_info(_Options, Info0, Info),
1519 _{upgrade:OldInfo, git:true} :< Info0,
1520 is_git_directory(OldInfo.installed) =>
1521 PackDir = OldInfo.installed,
1522 git_checkout_version(PackDir, [commit(Info0.hash)]),
1523 reload_info(PackDir, Info0, Info).
1524download_from_info(Options, Info0, Info),
1525 _{upgrade:OldInfo} :< Info0 =>
1526 PackDir = OldInfo.installed,
1527 detach_pack(OldInfo.pack, PackDir),
1528 delete_directory_and_contents(PackDir),
1529 del_dict(upgrade, Info0, _, Info1),
1530 download_from_info(Options, Info1, Info).
1531download_from_info(Options, Info0, Info),
1532 _{url:URL, git:true} :< Info0, \+ have_git =>
1533 git_archive_url(URL, Archive, Options),
1534 download_from_info([git_url(URL)|Options],
1535 Info0.put(_{ url:Archive,
1536 git:false,
1537 git_url:URL
1538 }),
1539 Info1),
1540 1541 ( Info1.get(version) == Info0.get(version),
1542 Hash = Info0.get(hash)
1543 -> Info = Info1.put(hash, Hash)
1544 ; Info = Info1
1545 ).
1546download_from_info(Options, Info0, Info),
1547 _{url:URL} :< Info0 =>
1548 select_option(pack_directory(Dir), Options, Options1),
1549 select_option(version(_), Options1, Options2, _),
1550 download_info_extra(Info0, InstallOptions, Options2),
1551 pack_download_from_url(URL, Dir, Info0.pack,
1552 [ interactive(false),
1553 pack_dir(PackDir)
1554 | InstallOptions
1555 ]),
1556 reload_info(PackDir, Info0, Info).
1557
(Info, [git(true),commit(Hash)|Options], Options) :-
1559 Info.get(git) == true,
1560 !,
1561 Hash = Info.get(commit, 'HEAD').
1562download_info_extra(Info, [link(true)|Options], Options) :-
1563 Info.get(link) == true,
1564 !.
1565download_info_extra(_, Options, Options).
1566
1567installed(Info) :-
1568 _ = Info.get(installed).
1569
1570detach_pack(Pack, PackDir) :-
1571 ( current_pack(Pack, PackDir)
1572 -> '$pack_detach'(Pack, PackDir)
1573 ; true
1574 ).
1575
1582
1583reload_info(_PackDir, Info, Info) :-
1584 _ = Info.get(installed), 1585 !.
1586reload_info(PackDir, Info0, Info) :-
1587 local_pack_info(PackDir, Info1),
1588 Info = Info0.put(installed, PackDir)
1589 .put(downloaded, Info0.url)
1590 .put(Info1).
1591
1596
1597work_done(_, _, _, _, Options),
1598 option(silent(true), Options) =>
1599 true.
1600work_done(Targets, Plan, Plan, [], _Options) =>
1601 convlist(can_upgrade_target(Plan), Targets, CanUpgrade),
1602 ( CanUpgrade == []
1603 -> pairs_keys(Targets, Packs),
1604 print_message(informational, pack(up_to_date(Packs)))
1605 ; print_message(informational, pack(installed_can_upgrade(CanUpgrade)))
1606 ).
1607work_done(_, _, _, _, _) =>
1608 true.
1609
1610can_upgrade_target(Plan, Pack-_, Info) =>
1611 member(Info, Plan),
1612 Info.pack == Pack,
1613 !,
1614 _ = Info.get(latest_version).
1615
1620
1621local_packs(Dir, Packs) :-
1622 findall(Pack, pack_in_subdir(Dir, Pack), Packs).
1623
1624pack_in_subdir(Dir, Info) :-
1625 directory_member(Dir, PackDir,
1626 [ file_type(directory),
1627 hidden(false)
1628 ]),
1629 local_pack_info(PackDir, Info).
1630
1631local_pack_info(PackDir,
1632 #{ pack: Pack,
1633 version: Version,
1634 title: Title,
1635 hash: Hash,
1636 url: URL,
1637 git: IsGit,
1638 requires: Requires,
1639 provides: Provides,
1640 conflicts: Conflicts,
1641 installed: PackDir
1642 }) :-
1643 directory_file_path(PackDir, 'pack.pl', MetaFile),
1644 exists_file(MetaFile),
1645 file_base_name(PackDir, DirName),
1646 findall(Term, pack_dir_info(PackDir, _, Term), Info),
1647 option(pack(Pack), Info, DirName),
1648 option(title(Title), Info, '<no title>'),
1649 option(version(Version), Info, '<no version>'),
1650 option(download(URL), Info, '<no download url>'),
1651 findall(Req, member(requires(Req), Info), Requires),
1652 findall(Prv, member(provides(Prv), Info), Provides),
1653 findall(Cfl, member(conflicts(Cfl), Info), Conflicts),
1654 ( have_git,
1655 is_git_directory(PackDir)
1656 -> git_hash(Hash, [directory(PackDir)]),
1657 IsGit = true
1658 ; Hash = '-',
1659 IsGit = false
1660 ).
1661
1662
1663 1666
1675
1676prolog_description([prolog(swi(Version))]) :-
1677 prolog_version(Version).
1678
1679prolog_version(Version) :-
1680 current_prolog_flag(version_git, Version),
1681 !.
1682prolog_version(Version) :-
1683 prolog_version_dotted(Version).
1684
1685prolog_version_dotted(Version) :-
1686 current_prolog_flag(version_data, swi(Major, Minor, Patch, _)),
1687 VNumbers = [Major, Minor, Patch],
1688 atomic_list_concat(VNumbers, '.', Version).
1689
1694
1695is_prolog_token(Token), cmp(Token, prolog, _Cmp, _Version) => true.
1696is_prolog_token(prolog:Feature), atom(Feature) => true.
1697is_prolog_token(prolog:Feature), flag_value_feature(Feature, _Flag, _Value) =>
1698 true.
1699is_prolog_token(_) => fail.
1700
1713
1714prolog_satisfies(Token), cmp(Token, prolog, Cmp, ReqVersion) =>
1715 prolog_version(CurrentVersion),
1716 cmp_versions(Cmp, CurrentVersion, ReqVersion).
1717prolog_satisfies(prolog:library(Lib)), atom(Lib) =>
1718 exists_source(library(Lib)).
1719prolog_satisfies(prolog:Feature), atom(Feature) =>
1720 current_prolog_flag(Feature, true).
1721prolog_satisfies(prolog:Feature), flag_value_feature(Feature, Flag, Value) =>
1722 current_prolog_flag(Flag, Value).
1723
1724flag_value_feature(Feature, Flag, Value) :-
1725 compound(Feature),
1726 compound_name_arguments(Feature, Flag, [Value]),
1727 atom(Flag).
1728
1729
1730 1733
1745
1746:- if(exists_source(library(archive))). 1747ensure_loaded_archive :-
1748 current_predicate(archive_open/3),
1749 !.
1750ensure_loaded_archive :-
1751 use_module(library(archive)).
1752
1753pack_archive_info(Archive, Pack, [archive_size(Bytes)|Info], Strip) :-
1754 ensure_loaded_archive,
1755 size_file(Archive, Bytes),
1756 setup_call_cleanup(
1757 archive_open(Archive, Handle, []),
1758 ( repeat,
1759 ( archive_next_header(Handle, InfoFile)
1760 -> true
1761 ; !, fail
1762 )
1763 ),
1764 archive_close(Handle)),
1765 file_base_name(InfoFile, 'pack.pl'),
1766 atom_concat(Prefix, 'pack.pl', InfoFile),
1767 strip_option(Prefix, Pack, Strip),
1768 setup_call_cleanup(
1769 archive_open_entry(Handle, Stream),
1770 read_stream_to_terms(Stream, Info),
1771 close(Stream)),
1772 !,
1773 must_be(ground, Info),
1774 maplist(valid_term(pack_info_term), Info).
1775:- else. 1776pack_archive_info(_, _, _, _) :-
1777 existence_error(library, archive).
1778:- endif. 1779pack_archive_info(_, _, _, _) :-
1780 existence_error(pack_file, 'pack.pl').
1781
1782strip_option('', _, []) :- !.
1783strip_option('./', _, []) :- !.
1784strip_option(Prefix, Pack, [remove_prefix(Prefix)]) :-
1785 atom_concat(PrefixDir, /, Prefix),
1786 file_base_name(PrefixDir, Base),
1787 ( Base == Pack
1788 -> true
1789 ; pack_version_file(Pack, _, Base)
1790 -> true
1791 ; \+ sub_atom(PrefixDir, _, _, _, /)
1792 ).
1793
1794read_stream_to_terms(Stream, Terms) :-
1795 read(Stream, Term0),
1796 read_stream_to_terms(Term0, Stream, Terms).
1797
1798read_stream_to_terms(end_of_file, _, []) :- !.
1799read_stream_to_terms(Term0, Stream, [Term0|Terms]) :-
1800 read(Stream, Term1),
1801 read_stream_to_terms(Term1, Stream, Terms).
1802
1803
1808
1809pack_git_info(GitDir, Hash, [git(true), installed_size(Bytes)|Info]) :-
1810 exists_directory(GitDir),
1811 !,
1812 git_ls_tree(Entries, [directory(GitDir)]),
1813 git_hash(Hash, [directory(GitDir)]),
1814 maplist(arg(4), Entries, Sizes),
1815 sum_list(Sizes, Bytes),
1816 dir_metadata(GitDir, Info).
1817
1818dir_metadata(GitDir, Info) :-
1819 directory_file_path(GitDir, 'pack.pl', InfoFile),
1820 read_file_to_terms(InfoFile, Info, [encoding(utf8)]),
1821 maplist(valid_term(pack_info_term), Info).
1822
1826
1827download_file_sanity_check(Archive, Pack, Info) :-
1828 info_field(name(PackName), Info),
1829 info_field(version(PackVersion), Info),
1830 pack_version_file(PackFile, FileVersion, Archive),
1831 must_match([Pack, PackName, PackFile], name),
1832 must_match([PackVersion, FileVersion], version).
1833
1834info_field(Field, Info) :-
1835 memberchk(Field, Info),
1836 ground(Field),
1837 !.
1838info_field(Field, _Info) :-
1839 functor(Field, FieldName, _),
1840 print_message(error, pack(missing(FieldName))),
1841 fail.
1842
1843must_match(Values, _Field) :-
1844 sort(Values, [_]),
1845 !.
1846must_match(Values, Field) :-
1847 print_message(error, pack(conflict(Field, Values))),
1848 fail.
1849
1850
1851 1854
1866
1867prepare_pack_dir(Dir, Options) :-
1868 exists_directory(Dir),
1869 !,
1870 ( empty_directory(Dir)
1871 -> true
1872 ; remove_existing_pack(Dir, Options)
1873 -> make_directory(Dir)
1874 ).
1875prepare_pack_dir(Dir, _) :-
1876 ( read_link(Dir, _, _)
1877 ; access_file(Dir, exist)
1878 ),
1879 !,
1880 delete_file(Dir),
1881 make_directory(Dir).
1882prepare_pack_dir(Dir, _) :-
1883 make_directory(Dir).
1884
1888
1889empty_directory(Dir) :-
1890 \+ ( directory_files(Dir, Entries),
1891 member(Entry, Entries),
1892 \+ special(Entry)
1893 ).
1894
1895special(.).
1896special(..).
1897
1904
1905remove_existing_pack(PackDir, Options) :-
1906 exists_directory(PackDir),
1907 !,
1908 ( ( option(upgrade(true), Options)
1909 ; confirm(remove_existing_pack(PackDir), yes, Options)
1910 )
1911 -> delete_directory_and_contents(PackDir)
1912 ; print_message(error, pack(directory_exists(PackDir))),
1913 fail
1914 ).
1915remove_existing_pack(_, _).
1916
1930
1931pack_download_from_url(URL, PackTopDir, Pack, Options) :-
1932 option(git(true), Options),
1933 !,
1934 directory_file_path(PackTopDir, Pack, PackDir),
1935 prepare_pack_dir(PackDir, Options),
1936 ( option(branch(Branch), Options)
1937 -> Extra = ['--branch', Branch]
1938 ; Extra = []
1939 ),
1940 run_process(path(git), [clone, URL, PackDir|Extra], []),
1941 git_checkout_version(PackDir, [update(false)|Options]),
1942 option(pack_dir(PackDir), Options, _).
1943pack_download_from_url(URL0, PackTopDir, Pack, Options) :-
1944 download_url(URL0),
1945 !,
1946 hsts(URL0, URL, Options),
1947 directory_file_path(PackTopDir, Pack, PackDir),
1948 prepare_pack_dir(PackDir, Options),
1949 pack_download_dir(PackTopDir, DownLoadDir),
1950 download_file(URL, Pack, DownloadBase, Options),
1951 directory_file_path(DownLoadDir, DownloadBase, DownloadFile),
1952 ( option(insecure(true), Options, false)
1953 -> TLSOptions = [cert_verify_hook(ssl_verify)]
1954 ; TLSOptions = []
1955 ),
1956 print_message(informational, pack(download(begin, Pack, URL, DownloadFile))),
1957 setup_call_cleanup(
1958 http_open(URL, In, TLSOptions),
1959 setup_call_cleanup(
1960 open(DownloadFile, write, Out, [type(binary)]),
1961 copy_stream_data(In, Out),
1962 close(Out)),
1963 close(In)),
1964 print_message(informational, pack(download(end, Pack, URL, DownloadFile))),
1965 pack_archive_info(DownloadFile, Pack, Info, _),
1966 ( option(git_url(GitURL), Options)
1967 -> Origin = GitURL 1968 ; download_file_sanity_check(DownloadFile, Pack, Info),
1969 Origin = URL
1970 ),
1971 pack_unpack_from_local(DownloadFile, PackTopDir, Pack, PackDir, Options),
1972 pack_assert(PackDir, archive(DownloadFile, Origin)),
1973 option(pack_dir(PackDir), Options, _).
1974pack_download_from_url(URL, PackTopDir, Pack, Options) :-
1975 local_uri_file_name(URL, File),
1976 !,
1977 pack_unpack_from_local(File, PackTopDir, Pack, PackDir, Options),
1978 pack_assert(PackDir, archive(File, URL)),
1979 option(pack_dir(PackDir), Options, _).
1980pack_download_from_url(URL, _PackTopDir, _Pack, _Options) :-
1981 domain_error(url, URL).
1982
2004
2005git_checkout_version(PackDir, Options) :-
2006 option(commit('HEAD'), Options),
2007 option(branch(Branch), Options),
2008 !,
2009 git_ensure_on_branch(PackDir, Branch),
2010 run_process(path(git), ['-C', PackDir, pull], []).
2011git_checkout_version(PackDir, Options) :-
2012 option(commit('HEAD'), Options),
2013 git_current_branch(_, [directory(PackDir)]),
2014 !,
2015 run_process(path(git), ['-C', PackDir, pull], []).
2016git_checkout_version(PackDir, Options) :-
2017 option(commit('HEAD'), Options),
2018 !,
2019 git_default_branch(Branch, [directory(PackDir)]),
2020 git_ensure_on_branch(PackDir, Branch),
2021 run_process(path(git), ['-C', PackDir, pull], []).
2022git_checkout_version(PackDir, Options) :-
2023 option(commit(Hash), Options),
2024 run_process(path(git), ['-C', PackDir, fetch], []),
2025 git_branches(Branches, [contains(Hash), directory(PackDir)]),
2026 git_process_output(['-C', PackDir, 'rev-parse' | Branches],
2027 read_lines_to_atoms(Commits),
2028 []),
2029 nth1(I, Commits, Hash),
2030 nth1(I, Branches, Branch),
2031 !,
2032 git_ensure_on_branch(PackDir, Branch).
2033git_checkout_version(PackDir, Options) :-
2034 option(commit(Hash), Options),
2035 !,
2036 run_process(path(git), ['-C', PackDir, checkout, '--quiet', Hash], []).
2037git_checkout_version(PackDir, Options) :-
2038 option(version(Version), Options),
2039 !,
2040 git_tags(Tags, [directory(PackDir)]),
2041 ( memberchk(Version, Tags)
2042 -> Tag = Version
2043 ; member(Tag, Tags),
2044 sub_atom(Tag, B, _, 0, Version),
2045 sub_atom(Tag, 0, B, _, Prefix),
2046 version_prefix(Prefix)
2047 -> true
2048 ; existence_error(version_tag, Version)
2049 ),
2050 run_process(path(git), ['-C', PackDir, checkout, Tag], []).
2051git_checkout_version(_PackDir, Options) :-
2052 option(fresh(true), Options),
2053 !.
2054git_checkout_version(PackDir, _Options) :-
2055 git_current_branch(_, [directory(PackDir)]),
2056 !,
2057 run_process(path(git), ['-C', PackDir, pull], []).
2058git_checkout_version(PackDir, _Options) :-
2059 git_default_branch(Branch, [directory(PackDir)]),
2060 git_ensure_on_branch(PackDir, Branch),
2061 run_process(path(git), ['-C', PackDir, pull], []).
2062
2066
2067git_ensure_on_branch(PackDir, Branch) :-
2068 git_current_branch(Branch, [directory(PackDir)]),
2069 !.
2070git_ensure_on_branch(PackDir, Branch) :-
2071 run_process(path(git), ['-C', PackDir, checkout, Branch], []).
2072
2073read_lines_to_atoms(Atoms, In) :-
2074 read_line_to_string(In, Line),
2075 ( Line == end_of_file
2076 -> Atoms = []
2077 ; atom_string(Atom, Line),
2078 Atoms = [Atom|T],
2079 read_lines_to_atoms(T, In)
2080 ).
2081
2082version_prefix(Prefix) :-
2083 atom_codes(Prefix, Codes),
2084 phrase(version_prefix, Codes).
2085
2086version_prefix -->
2087 [C],
2088 { code_type(C, alpha) },
2089 !,
2090 version_prefix.
2091version_prefix -->
2092 "-".
2093version_prefix -->
2094 "_".
2095version_prefix -->
2096 "".
2097
2102
2103download_file(URL, Pack, File, Options) :-
2104 option(version(Version), Options),
2105 !,
2106 file_name_extension(_, Ext, URL),
2107 format(atom(File), '~w-~w.~w', [Pack, Version, Ext]).
2108download_file(URL, Pack, File, _) :-
2109 file_base_name(URL,Basename),
2110 no_int_file_name_extension(Tag,Ext,Basename),
2111 tag_version(Tag,Version),
2112 !,
2113 format(atom(File0), '~w-~w', [Pack, Version]),
2114 file_name_extension(File0, Ext, File).
2115download_file(URL, _, File, _) :-
2116 file_base_name(URL, File).
2117
2123
2124:- public pack_url_file/2. 2125pack_url_file(URL, FileID) :-
2126 github_release_url(URL, Pack, Version),
2127 !,
2128 download_file(URL, Pack, FileID, [version(Version)]).
2129pack_url_file(URL, FileID) :-
2130 file_base_name(URL, FileID).
2131
2136
2137:- public ssl_verify/5. 2138ssl_verify(_SSL,
2139 _ProblemCertificate, _AllCertificates, _FirstCertificate,
2140 _Error).
2141
2142pack_download_dir(PackTopDir, DownLoadDir) :-
2143 directory_file_path(PackTopDir, 'Downloads', DownLoadDir),
2144 ( exists_directory(DownLoadDir)
2145 -> true
2146 ; make_directory(DownLoadDir)
2147 ),
2148 ( access_file(DownLoadDir, write)
2149 -> true
2150 ; permission_error(write, directory, DownLoadDir)
2151 ).
2152
2158
2159download_url(URL) :-
2160 url_scheme(URL, Scheme),
2161 download_scheme(Scheme).
2162
2163url_scheme(URL, Scheme) :-
2164 atom(URL),
2165 uri_components(URL, Components),
2166 uri_data(scheme, Components, Scheme),
2167 atom(Scheme).
2168
2169download_scheme(http).
2170download_scheme(https).
2171
2180
2181hsts(URL0, URL, Options) :-
2182 option(insecure(true), Options, false),
2183 !,
2184 URL = URL0.
2185hsts(URL0, URL, _Options) :-
2186 url_scheme(URL0, http),
2187 !,
2188 uri_edit(scheme(https), URL0, URL).
2189hsts(URL, URL, _Options).
2190
2191
2199
2200pack_post_install(Pack, PackDir, Options) :-
2201 post_install_foreign(Pack, PackDir, Options),
2202 post_install_autoload(PackDir, Options),
2203 attach_packs(PackDir, [duplicate(warning)]).
2204
2210
2211pack_rebuild :-
2212 forall(current_pack(Pack),
2213 ( print_message(informational, pack(rebuild(Pack))),
2214 pack_rebuild(Pack)
2215 )).
2216
2217pack_rebuild(Pack) :-
2218 current_pack(Pack, PackDir),
2219 !,
2220 post_install_foreign(Pack, PackDir, [rebuild(true)]).
2221pack_rebuild(Pack) :-
2222 unattached_pack(Pack, PackDir),
2223 !,
2224 post_install_foreign(Pack, PackDir, [rebuild(true)]).
2225pack_rebuild(Pack) :-
2226 existence_error(pack, Pack).
2227
2228unattached_pack(Pack, BaseDir) :-
2229 directory_file_path(Pack, 'pack.pl', PackFile),
2230 absolute_file_name(pack(PackFile), PackPath,
2231 [ access(read),
2232 file_errors(fail)
2233 ]),
2234 file_directory_name(PackPath, BaseDir).
2235
2236
2237
2249
2250post_install_foreign(Pack, PackDir, Options) :-
2251 is_foreign_pack(PackDir, _),
2252 !,
2253 ( pack_info_term(PackDir, pack_version(Version))
2254 -> true
2255 ; Version = 1
2256 ),
2257 option(rebuild(Rebuild), Options, if_absent),
2258 current_prolog_flag(arch, Arch),
2259 prolog_version_dotted(PrologVersion),
2260 ( Rebuild == if_absent,
2261 foreign_present(PackDir, Arch)
2262 -> print_message(informational, pack(kept_foreign(Pack, Arch))),
2263 ( pack_status_dir(PackDir, built(Arch, _, _))
2264 -> true
2265 ; pack_assert(PackDir, built(Arch, PrologVersion, downloaded))
2266 )
2267 ; BuildSteps0 = [[dependencies], [configure], build, install, [test]],
2268 ( Rebuild == true
2269 -> BuildSteps1 = [distclean|BuildSteps0]
2270 ; BuildSteps1 = BuildSteps0
2271 ),
2272 ( option(test(false), Options)
2273 -> delete(BuildSteps1, [test], BuildSteps2)
2274 ; BuildSteps2 = BuildSteps1
2275 ),
2276 ( option(clean(true), Options)
2277 -> append(BuildSteps2, [[clean]], BuildSteps)
2278 ; BuildSteps = BuildSteps2
2279 ),
2280 build_steps(BuildSteps, PackDir, [pack_version(Version)|Options]),
2281 pack_assert(PackDir, built(Arch, PrologVersion, built))
2282 ).
2283post_install_foreign(_, _, _).
2284
2285
2293
2294foreign_present(PackDir, Arch) :-
2295 atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
2296 exists_directory(ForeignBaseDir),
2297 !,
2298 atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
2299 exists_directory(ForeignDir),
2300 current_prolog_flag(shared_object_extension, Ext),
2301 atomic_list_concat([ForeignDir, '/*.', Ext], Pattern),
2302 expand_file_name(Pattern, Files),
2303 Files \== [].
2304
2309
2310is_foreign_pack(PackDir, Type) :-
2311 foreign_file(File, Type),
2312 directory_file_path(PackDir, File, Path),
2313 exists_file(Path).
2314
2315foreign_file('CMakeLists.txt', cmake).
2316foreign_file('configure', configure).
2317foreign_file('configure.in', autoconf).
2318foreign_file('configure.ac', autoconf).
2319foreign_file('Makefile.am', automake).
2320foreign_file('Makefile', make).
2321foreign_file('makefile', make).
2322foreign_file('conanfile.txt', conan).
2323foreign_file('conanfile.py', conan).
2324
2325
2326 2329
2333
2334post_install_autoload(PackDir, Options) :-
2335 is_autoload_pack(PackDir, Options),
2336 !,
2337 directory_file_path(PackDir, prolog, PrologLibDir),
2338 make_library_index(PrologLibDir).
2339post_install_autoload(_, _).
2340
2341is_autoload_pack(PackDir, Options) :-
2342 option(autoload(true), Options, true),
2343 pack_info_term(PackDir, autoload(true)).
2344
2345
2346 2349
2353
2354pack_upgrade(Pack) :-
2355 pack_install(Pack, [upgrade(true)]).
2356
2357
2358 2361
2372
2373pack_remove(Pack) :-
2374 pack_remove(Pack, []).
2375
2376pack_remove(Pack, Options) :-
2377 option(dependencies(false), Options),
2378 !,
2379 pack_remove_forced(Pack).
2380pack_remove(Pack, Options) :-
2381 ( dependents(Pack, Deps)
2382 -> ( option(dependencies(true), Options)
2383 -> true
2384 ; confirm_remove(Pack, Deps, Delete, Options)
2385 ),
2386 forall(member(P, Delete), pack_remove_forced(P))
2387 ; pack_remove_forced(Pack)
2388 ).
2389
2390pack_remove_forced(Pack) :-
2391 catch('$pack_detach'(Pack, BaseDir),
2392 error(existence_error(pack, Pack), _),
2393 fail),
2394 !,
2395 print_message(informational, pack(remove(BaseDir))),
2396 delete_directory_and_contents(BaseDir).
2397pack_remove_forced(Pack) :-
2398 unattached_pack(Pack, BaseDir),
2399 !,
2400 delete_directory_and_contents(BaseDir).
2401pack_remove_forced(Pack) :-
2402 print_message(informational, error(existence_error(pack, Pack),_)).
2403
2404confirm_remove(Pack, Deps, Delete, Options) :-
2405 print_message(warning, pack(depends(Pack, Deps))),
2406 menu(pack(resolve_remove),
2407 [ [Pack] = remove_only(Pack),
2408 [Pack|Deps] = remove_deps(Pack, Deps),
2409 [] = cancel
2410 ], [], Delete, Options),
2411 Delete \== [].
2412
2413
2414 2417
2468
2469pack_publish(Dir, Options) :-
2470 \+ download_url(Dir),
2471 is_git_directory(Dir), !,
2472 pack_git_info(Dir, _Hash, Metadata),
2473 prepare_repository(Dir, Metadata, Options),
2474 ( memberchk(download(URL), Metadata),
2475 git_url(URL, _)
2476 -> true
2477 ; option(remote(Remote), Options, origin),
2478 git_remote_url(Remote, RemoteURL, [directory(Dir)]),
2479 git_to_https_url(RemoteURL, URL)
2480 ),
2481 memberchk(version(Version), Metadata),
2482 pack_publish_(URL,
2483 [ version(Version)
2484 | Options
2485 ]).
2486pack_publish(Spec, Options) :-
2487 pack_publish_(Spec, Options).
2488
2489pack_publish_(Spec, Options) :-
2490 pack_default_options(Spec, Pack, Options, DefOptions),
2491 option(url(URL), DefOptions),
2492 valid_publish_url(URL, Options),
2493 prepare_build_location(Pack, Dir, Clean, Options),
2494 ( option(register(false), Options)
2495 -> InstallOptions = DefOptions
2496 ; InstallOptions = [publish(Pack)|DefOptions]
2497 ),
2498 call_cleanup(pack_install(Pack,
2499 [ pack(Pack)
2500 | InstallOptions
2501 ]),
2502 cleanup_publish(Clean, Dir)).
2503
2504cleanup_publish(true, Dir) :-
2505 !,
2506 delete_directory_and_contents(Dir).
2507cleanup_publish(_, _).
2508
2509valid_publish_url(URL, Options) :-
2510 option(register(Register), Options, true),
2511 ( Register == false
2512 -> true
2513 ; download_url(URL)
2514 -> true
2515 ; permission_error(publish, pack, URL)
2516 ).
2517
2518prepare_build_location(Pack, Dir, Clean, Options) :-
2519 ( option(pack_directory(Dir), Options)
2520 -> ensure_directory(Dir),
2521 ( option(clean(true), Options, true)
2522 -> delete_directory_contents(Dir)
2523 ; true
2524 )
2525 ; tmp_file(pack, Dir),
2526 make_directory(Dir),
2527 Clean = true
2528 ),
2529 ( option(isolated(false), Options)
2530 -> detach_pack(Pack, _),
2531 attach_packs(Dir, [search(first)])
2532 ; attach_packs(Dir, [replace(true)])
2533 ).
2534
2535
2536
2543
2544prepare_repository(_Dir, _Metadata, Options) :-
2545 option(register(false), Options),
2546 !.
2547prepare_repository(Dir, Metadata, Options) :-
2548 git_dir_must_be_clean(Dir),
2549 git_must_be_on_default_branch(Dir, Options),
2550 tag_git_dir(Dir, Metadata, Action, Options),
2551 confirm(git_push, yes, Options),
2552 run_process(path(git), ['-C', file(Dir), push ], []),
2553 ( Action = push_tag(Tag)
2554 -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], [])
2555 ; true
2556 ).
2557
2558git_dir_must_be_clean(Dir) :-
2559 git_describe(Description, [directory(Dir)]),
2560 ( sub_atom(Description, _, _, 0, '-DIRTY')
2561 -> print_message(error, pack(git_not_clean(Dir))),
2562 fail
2563 ; true
2564 ).
2565
2566git_must_be_on_default_branch(Dir, Options) :-
2567 ( option(branch(Default), Options)
2568 -> true
2569 ; git_default_branch(Default, [directory(Dir)])
2570 ),
2571 git_current_branch(Current, [directory(Dir)]),
2572 ( Default == Current
2573 -> true
2574 ; print_message(error,
2575 pack(git_branch_not_default(Dir, Default, Current))),
2576 fail
2577 ).
2578
2579
2585
2586tag_git_dir(Dir, Metadata, Action, Options) :-
2587 memberchk(version(Version), Metadata),
2588 atom_concat('V', Version, Tag),
2589 git_tags(Tags, [directory(Dir)]),
2590 ( memberchk(Tag, Tags)
2591 -> git_tag_is_consistent(Dir, Tag, Action, Options)
2592 ; format(string(Message), 'Release ~w', [Version]),
2593 findall(Opt, git_tag_option(Opt, Options), Argv,
2594 [ '-m', Message, Tag ]),
2595 confirm(git_tag(Tag), yes, Options),
2596 run_process(path(git), ['-C', file(Dir), tag | Argv ], []),
2597 Action = push_tag(Tag)
2598 ).
2599
2600git_tag_option('-s', Options) :- option(sign(true), Options, true).
2601git_tag_option('-f', Options) :- option(force(true), Options, true).
2602
2603git_tag_is_consistent(Dir, Tag, Action, Options) :-
2604 format(atom(TagRef), 'refs/tags/~w', [Tag]),
2605 format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]),
2606 option(remote(Remote), Options, origin),
2607 git_ls_remote(Dir, LocalTags, [tags(true)]),
2608 memberchk(CommitHash-CommitRef, LocalTags),
2609 ( git_hash(CommitHash, [directory(Dir)])
2610 -> true
2611 ; print_message(error, pack(git_release_tag_not_at_head(Tag))),
2612 fail
2613 ),
2614 memberchk(TagHash-TagRef, LocalTags),
2615 git_ls_remote(Remote, RemoteTags, [tags(true)]),
2616 ( memberchk(RemoteCommitHash-CommitRef, RemoteTags),
2617 memberchk(RemoteTagHash-TagRef, RemoteTags)
2618 -> ( RemoteCommitHash == CommitHash,
2619 RemoteTagHash == TagHash
2620 -> Action = none
2621 ; print_message(error, pack(git_tag_out_of_sync(Tag))),
2622 fail
2623 )
2624 ; Action = push_tag(Tag)
2625 ).
2626
2632
2633git_to_https_url(URL, URL) :-
2634 download_url(URL),
2635 !.
2636git_to_https_url(GitURL, URL) :-
2637 atom_concat('git@github.com:', Repo, GitURL),
2638 !,
2639 atom_concat('https://github.com/', Repo, URL).
2640git_to_https_url(GitURL, _) :-
2641 print_message(error, pack(git_no_https(GitURL))),
2642 fail.
2643
2644
2645 2648
2669
2670pack_property(Pack, Property) :-
2671 findall(Pack-Property, pack_property_(Pack, Property), List),
2672 member(Pack-Property, List). 2673
2674pack_property_(Pack, Property) :-
2675 pack_info(Pack, _, Property).
2676pack_property_(Pack, Property) :-
2677 \+ \+ info_file(Property, _),
2678 '$pack':pack(Pack, BaseDir),
2679 access_file(BaseDir, read),
2680 directory_files(BaseDir, Files),
2681 member(File, Files),
2682 info_file(Property, Pattern),
2683 downcase_atom(File, Pattern),
2684 directory_file_path(BaseDir, File, InfoFile),
2685 arg(1, Property, InfoFile).
2686
2687info_file(readme(_), 'readme.txt').
2688info_file(readme(_), 'readme').
2689info_file(todo(_), 'todo.txt').
2690info_file(todo(_), 'todo').
2691
2692
2693 2696
2703
2704pack_version_file(Pack, Version, GitHubRelease) :-
2705 atomic(GitHubRelease),
2706 github_release_url(GitHubRelease, Pack, Version),
2707 !.
2708pack_version_file(Pack, Version, Path) :-
2709 atomic(Path),
2710 file_base_name(Path, File),
2711 no_int_file_name_extension(Base, _Ext, File),
2712 atom_codes(Base, Codes),
2713 ( phrase(pack_version(Pack, Version), Codes),
2714 safe_pack_name(Pack)
2715 -> true
2716 ).
2717
2718no_int_file_name_extension(Base, Ext, File) :-
2719 file_name_extension(Base0, Ext0, File),
2720 \+ atom_number(Ext0, _),
2721 !,
2722 Base = Base0,
2723 Ext = Ext0.
2724no_int_file_name_extension(File, '', File).
2725
2730
2731safe_pack_name(Name) :-
2732 atom_length(Name, Len),
2733 Len >= 3, 2734 atom_codes(Name, Codes),
2735 maplist(safe_pack_char, Codes),
2736 !.
2737
2738safe_pack_char(C) :- between(0'a, 0'z, C), !.
2739safe_pack_char(C) :- between(0'A, 0'Z, C), !.
2740safe_pack_char(C) :- between(0'0, 0'9, C), !.
2741safe_pack_char(0'_).
2742
2746
2747pack_version(Pack, Version) -->
2748 string(Codes), "-",
2749 version(Parts),
2750 !,
2751 { atom_codes(Pack, Codes),
2752 atomic_list_concat(Parts, '.', Version)
2753 }.
2754
2755version([H|T]) -->
2756 version_part(H),
2757 ( "."
2758 -> version(T)
2759 ; {T=[]}
2760 ).
2761
2762version_part(*) --> "*", !.
2763version_part(Int) --> integer(Int).
2764
2765
2766 2769
2770have_git :-
2771 process_which(path(git), _).
2772
2773
2777
2778git_url(URL, Pack) :-
2779 uri_components(URL, Components),
2780 uri_data(scheme, Components, Scheme),
2781 nonvar(Scheme), 2782 uri_data(path, Components, Path),
2783 ( Scheme == git
2784 -> true
2785 ; git_download_scheme(Scheme),
2786 file_name_extension(_, git, Path)
2787 ; git_download_scheme(Scheme),
2788 catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail)
2789 -> true
2790 ),
2791 file_base_name(Path, PackExt),
2792 ( file_name_extension(Pack, git, PackExt)
2793 -> true
2794 ; Pack = PackExt
2795 ),
2796 ( safe_pack_name(Pack)
2797 -> true
2798 ; domain_error(pack_name, Pack)
2799 ).
2800
2801git_download_scheme(http).
2802git_download_scheme(https).
2803
2810
2811github_release_url(URL, Pack, Version) :-
2812 uri_components(URL, Components),
2813 uri_data(authority, Components, 'github.com'),
2814 uri_data(scheme, Components, Scheme),
2815 download_scheme(Scheme),
2816 uri_data(path, Components, Path),
2817 github_archive_path(Archive,Pack,File),
2818 atomic_list_concat(Archive, /, Path),
2819 file_name_extension(Tag, Ext, File),
2820 github_archive_extension(Ext),
2821 tag_version(Tag, Version),
2822 !.
2823
2824github_archive_path(['',_User,Pack,archive,File],Pack,File).
2825github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File).
2826
2827github_archive_extension(tgz).
2828github_archive_extension(zip).
2829
2834
2835tag_version(Tag, Version) :-
2836 version_tag_prefix(Prefix),
2837 atom_concat(Prefix, Version, Tag),
2838 is_version(Version).
2839
2840version_tag_prefix(v).
2841version_tag_prefix('V').
2842version_tag_prefix('').
2843
2844
2850
2851git_archive_url(URL, Archive, Options) :-
2852 uri_components(URL, Components),
2853 uri_data(authority, Components, 'github.com'),
2854 uri_data(path, Components, Path),
2855 atomic_list_concat(['', User, RepoGit], /, Path),
2856 $,
2857 remove_git_ext(RepoGit, Repo),
2858 git_archive_version(Version, Options),
2859 atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath),
2860 uri_edit([ path(ArchivePath),
2861 host('codeload.github.com')
2862 ],
2863 URL, Archive).
2864git_archive_url(URL, _, _) :-
2865 print_message(error, pack(no_git(URL))),
2866 fail.
2867
2868remove_git_ext(RepoGit, Repo) :-
2869 file_name_extension(Repo, git, RepoGit),
2870 !.
2871remove_git_ext(Repo, Repo).
2872
2873git_archive_version(Version, Options) :-
2874 option(commit(Version), Options),
2875 !.
2876git_archive_version(Version, Options) :-
2877 option(branch(Version), Options),
2878 !.
2879git_archive_version(Version, Options) :-
2880 option(version(Version), Options),
2881 !.
2882git_archive_version('HEAD', _).
2883
2884 2887
2900
2901register_downloads(_, Options) :-
2902 option(register(false), Options),
2903 !.
2904register_downloads(_, Options) :-
2905 option(publish(_), Options),
2906 !.
2907register_downloads(Infos, Options) :-
2908 convlist(download_data, Infos, Data),
2909 ( Data == []
2910 -> true
2911 ; query_pack_server(downloaded(Data), Reply, Options),
2912 ( option(do_publish(Pack), Options)
2913 -> ( member(Info, Infos),
2914 Info.pack == Pack
2915 -> true
2916 ),
2917 ( Reply = true(Actions),
2918 memberchk(Pack-Result, Actions)
2919 -> ( registered(Result)
2920 -> print_message(informational, pack(published(Info, Result)))
2921 ; print_message(error, pack(publish_failed(Info, Result))),
2922 fail
2923 )
2924 ; print_message(error, pack(publish_failed(Info, false)))
2925 )
2926 ; true
2927 )
2928 ).
2929
2930registered(git(_URL)).
2931registered(file(_URL)).
2932
2933publish_download(Infos, Options) :-
2934 select_option(publish(Pack), Options, Options1),
2935 !,
2936 register_downloads(Infos, [do_publish(Pack)|Options1]).
2937publish_download(_Infos, _Options).
2938
2949
2950download_data(Info, Data),
2951 Info.get(git) == true => 2952 Data = download(URL, Hash, Metadata),
2953 URL = Info.get(downloaded),
2954 pack_git_info(Info.installed, Hash, Metadata).
2955download_data(Info, Data),
2956 _{git_url:URL,hash:Hash} :< Info, Hash \== (-) =>
2957 Data = download(URL, Hash, Metadata), 2958 dir_metadata(Info.installed, Metadata).
2959download_data(Info, Data) => 2960 Data = download(URL, Hash, Metadata),
2961 URL = Info.get(downloaded),
2962 download_url(URL),
2963 pack_status_dir(Info.installed, archive(Archive, URL)),
2964 file_sha1(Archive, Hash),
2965 pack_archive_info(Archive, _Pack, Metadata, _).
2966
2971
2972query_pack_server(Query, Result, Options) :-
2973 ( option(server(ServerOpt), Options)
2974 -> server_url(ServerOpt, ServerBase)
2975 ; setting(server, ServerBase),
2976 ServerBase \== ''
2977 ),
2978 atom_concat(ServerBase, query, Server),
2979 format(codes(Data), '~q.~n', Query),
2980 info_level(Informational, Options),
2981 print_message(Informational, pack(contacting_server(Server))),
2982 setup_call_cleanup(
2983 http_open(Server, In,
2984 [ post(codes(application/'x-prolog', Data)),
2985 header(content_type, ContentType)
2986 ]),
2987 read_reply(ContentType, In, Result),
2988 close(In)),
2989 message_severity(Result, Level, Informational),
2990 print_message(Level, pack(server_reply(Result))).
2991
2992server_url(URL0, URL) :-
2993 uri_components(URL0, Components),
2994 uri_data(scheme, Components, Scheme),
2995 var(Scheme),
2996 !,
2997 atom_concat('https://', URL0, URL1),
2998 server_url(URL1, URL).
2999server_url(URL0, URL) :-
3000 uri_components(URL0, Components),
3001 uri_data(path, Components, ''),
3002 !,
3003 uri_edit([path('/pack/')], URL0, URL).
3004server_url(URL, URL).
3005
3006read_reply(ContentType, In, Result) :-
3007 sub_atom(ContentType, 0, _, _, 'application/x-prolog'),
3008 !,
3009 set_stream(In, encoding(utf8)),
3010 read(In, Result).
3011read_reply(ContentType, In, _Result) :-
3012 read_string(In, 500, String),
3013 print_message(error, pack(no_prolog_response(ContentType, String))),
3014 fail.
3015
3016info_level(Level, Options) :-
3017 option(silent(true), Options),
3018 !,
3019 Level = silent.
3020info_level(informational, _).
3021
3022message_severity(true(_), Informational, Informational).
3023message_severity(false, warning, _).
3024message_severity(exception(_), error, _).
3025
3026
3027 3030
3037
3038available_download_versions(URL, Versions, _Options) :-
3039 wildcard_pattern(URL),
3040 github_url(URL, User, Repo), 3041 !,
3042 findall(Version-VersionURL,
3043 github_version(User, Repo, Version, VersionURL),
3044 Versions).
3045available_download_versions(URL0, Versions, Options) :-
3046 wildcard_pattern(URL0),
3047 !,
3048 hsts(URL0, URL, Options),
3049 file_directory_name(URL, DirURL0),
3050 ensure_slash(DirURL0, DirURL),
3051 print_message(informational, pack(query_versions(DirURL))),
3052 setup_call_cleanup(
3053 http_open(DirURL, In, []),
3054 load_html(stream(In), DOM,
3055 [ syntax_errors(quiet)
3056 ]),
3057 close(In)),
3058 findall(MatchingURL,
3059 absolute_matching_href(DOM, URL, MatchingURL),
3060 MatchingURLs),
3061 ( MatchingURLs == []
3062 -> print_message(warning, pack(no_matching_urls(URL)))
3063 ; true
3064 ),
3065 versioned_urls(MatchingURLs, VersionedURLs),
3066 sort_version_pairs(VersionedURLs, Versions),
3067 print_message(informational, pack(found_versions(Versions))).
3068available_download_versions(URL, [Version-URL], _Options) :-
3069 ( pack_version_file(_Pack, Version0, URL)
3070 -> Version = Version0
3071 ; Version = '0.0.0'
3072 ).
3073
3077
3078sort_version_pairs(Pairs, Sorted) :-
3079 map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed),
3080 sort(1, @>=, Keyed, SortedKeyed),
3081 pairs_values(SortedKeyed, Sorted).
3082
3083version_pair_sort_key_(Version-_Data, Key) :-
3084 version_sort_key(Version, Key).
3085
3086version_sort_key(Version, Key) :-
3087 split_string(Version, ".", "", Parts),
3088 maplist(number_string, Key, Parts),
3089 !.
3090version_sort_key(Version, _) :-
3091 domain_error(version, Version).
3092
3096
3097github_url(URL, User, Repo) :-
3098 uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3099 atomic_list_concat(['',User,Repo|_], /, Path).
3100
3101
3106
3107github_version(User, Repo, Version, VersionURI) :-
3108 atomic_list_concat(['',repos,User,Repo,tags], /, Path1),
3109 uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)),
3110 setup_call_cleanup(
3111 http_open(ApiUri, In,
3112 [ request_header('Accept'='application/vnd.github.v3+json')
3113 ]),
3114 json_read_dict(In, Dicts),
3115 close(In)),
3116 member(Dict, Dicts),
3117 atom_string(Tag, Dict.name),
3118 tag_version(Tag, Version),
3119 atom_string(VersionURI, Dict.zipball_url).
3120
3121wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *).
3122wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?).
3123
3124ensure_slash(Dir, DirS) :-
3125 ( sub_atom(Dir, _, _, 0, /)
3126 -> DirS = Dir
3127 ; atom_concat(Dir, /, DirS)
3128 ).
3129
3130remove_slash(Dir0, Dir) :-
3131 Dir0 \== '/',
3132 atom_concat(Dir1, /, Dir0),
3133 !,
3134 remove_slash(Dir1, Dir).
3135remove_slash(Dir, Dir).
3136
3137absolute_matching_href(DOM, Pattern, Match) :-
3138 xpath(DOM, //a(@href), HREF),
3139 uri_normalized(HREF, Pattern, Match),
3140 wildcard_match(Pattern, Match).
3141
3142versioned_urls([], []).
3143versioned_urls([H|T0], List) :-
3144 file_base_name(H, File),
3145 ( pack_version_file(_Pack, Version, File)
3146 -> List = [Version-H|T]
3147 ; List = T
3148 ),
3149 versioned_urls(T0, T).
3150
3151
3152 3155
3161
3162pack_provides(Pack, Pack@Version) :-
3163 current_pack(Pack),
3164 once(pack_info(Pack, version, version(Version))).
3165pack_provides(Pack, Provides) :-
3166 findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList),
3167 member(Provides, PrvList).
3168
3169pack_requires(Pack, Requires) :-
3170 current_pack(Pack),
3171 findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList),
3172 member(Requires, ReqList).
3173
3174pack_conflicts(Pack, Conflicts) :-
3175 current_pack(Pack),
3176 findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList),
3177 member(Conflicts, CflList).
3178
3183
3184pack_depends_on(Pack, Dependency) :-
3185 ground(Pack),
3186 !,
3187 pack_requires(Pack, Requires),
3188 \+ is_prolog_token(Requires),
3189 pack_provides(Dependency, Provides),
3190 satisfies_req(Provides, Requires).
3191pack_depends_on(Pack, Dependency) :-
3192 ground(Dependency),
3193 !,
3194 pack_provides(Dependency, Provides),
3195 pack_requires(Pack, Requires),
3196 satisfies_req(Provides, Requires).
3197pack_depends_on(Pack, Dependency) :-
3198 current_pack(Pack),
3199 pack_depends_on(Pack, Dependency).
3200
3205
3206dependents(Pack, Deps) :-
3207 setof(Dep, dependent(Pack, Dep, []), Deps).
3208
3209dependent(Pack, Dep, Seen) :-
3210 pack_depends_on(Dep0, Pack),
3211 \+ memberchk(Dep0, Seen),
3212 ( Dep = Dep0
3213 ; dependent(Dep0, Dep, [Dep0|Seen])
3214 ).
3215
3219
3220validate_dependencies :-
3221 setof(Issue, pack_dependency_issue(_, Issue), Issues),
3222 !,
3223 print_message(warning, pack(dependency_issues(Issues))).
3224validate_dependencies.
3225
3235
3236pack_dependency_issue(Pack, Issue) :-
3237 current_pack(Pack),
3238 pack_dependency_issue_(Pack, Issue).
3239
3240pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :-
3241 pack_requires(Pack, Requires),
3242 ( is_prolog_token(Requires)
3243 -> \+ prolog_satisfies(Requires)
3244 ; \+ ( pack_provides(_, Provides),
3245 satisfies_req(Provides, Requires) )
3246 ).
3247pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :-
3248 pack_conflicts(Pack, Conflicts),
3249 ( is_prolog_token(Conflicts)
3250 -> prolog_satisfies(Conflicts)
3251 ; pack_provides(_, Provides),
3252 satisfies_req(Provides, Conflicts)
3253 ).
3254
3255
3256 3259
3273
3274pack_assert(PackDir, Fact) :-
3275 must_be(ground, Fact),
3276 findall(Term, pack_status_dir(PackDir, Term), Facts0),
3277 update_facts(Facts0, Fact, Facts),
3278 OpenOptions = [encoding(utf8), lock(exclusive)],
3279 status_file(PackDir, StatusFile),
3280 ( Facts == Facts0
3281 -> true
3282 ; Facts0 \== [],
3283 append(Facts0, New, Facts)
3284 -> setup_call_cleanup(
3285 open(StatusFile, append, Out, OpenOptions),
3286 maplist(write_fact(Out), New),
3287 close(Out))
3288 ; setup_call_cleanup(
3289 open(StatusFile, write, Out, OpenOptions),
3290 ( write_facts_header(Out),
3291 maplist(write_fact(Out), Facts)
3292 ),
3293 close(Out))
3294 ).
3295
3296update_facts([], Fact, [Fact]) :-
3297 !.
3298update_facts([H|T], Fact, [Fact|T]) :-
3299 general_pack_fact(Fact, GenFact),
3300 general_pack_fact(H, GenTerm),
3301 GenFact =@= GenTerm,
3302 !.
3303update_facts([H|T0], Fact, [H|T]) :-
3304 update_facts(T0, Fact, T).
3305
3306general_pack_fact(built(Arch, _Version, _How), General) =>
3307 General = built(Arch, _, _).
3308general_pack_fact(Term, General), compound(Term) =>
3309 compound_name_arity(Term, Name, Arity),
3310 compound_name_arity(General, Name, Arity).
3311general_pack_fact(Term, General) =>
3312 General = Term.
3313
(Out) :-
3315 format(Out, '% Fact status file. Managed by package manager.~n', []).
3316
3317write_fact(Out, Term) :-
3318 format(Out, '~q.~n', [Term]).
3319
3325
3326pack_status(Pack, Fact) :-
3327 current_pack(Pack, PackDir),
3328 pack_status_dir(PackDir, Fact).
3329
3330pack_status_dir(PackDir, Fact) :-
3331 det_if(ground(Fact), pack_status_(PackDir, Fact)).
3332
3333pack_status_(PackDir, Fact) :-
3334 status_file(PackDir, StatusFile),
3335 catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact),
3336 error(existence_error(source_sink, StatusFile), _),
3337 fail).
3338
3339pack_status_term(built(atom, version, oneof([built,downloaded]))).
3340pack_status_term(automatic(boolean)).
3341pack_status_term(archive(atom, atom)).
3342
3343
3350
3351update_automatic(Info) :-
3352 _ = Info.get(dependency_for),
3353 \+ pack_status(Info.installed, automatic(_)),
3354 !,
3355 pack_assert(Info.installed, automatic(true)).
3356update_automatic(Info) :-
3357 pack_assert(Info.installed, automatic(false)).
3358
3359status_file(PackDir, StatusFile) :-
3360 directory_file_path(PackDir, 'status.db', StatusFile).
3361
3362 3365
3366:- multifile prolog:message//1. 3367
3369
(_Question, _Alternatives, Default, Selection, Options) :-
3371 option(interactive(false), Options),
3372 !,
3373 Selection = Default.
3374menu(Question, Alternatives, Default, Selection, _) :-
3375 length(Alternatives, N),
3376 between(1, 5, _),
3377 print_message(query, Question),
3378 print_menu(Alternatives, Default, 1),
3379 print_message(query, pack(menu(select))),
3380 read_selection(N, Choice),
3381 !,
3382 ( Choice == default
3383 -> Selection = Default
3384 ; nth1(Choice, Alternatives, Selection=_)
3385 -> true
3386 ).
3387
([], _, _).
3389print_menu([Value=Label|T], Default, I) :-
3390 ( Value == Default
3391 -> print_message(query, pack(menu(default_item(I, Label))))
3392 ; print_message(query, pack(menu(item(I, Label))))
3393 ),
3394 I2 is I + 1,
3395 print_menu(T, Default, I2).
3396
3397read_selection(Max, Choice) :-
3398 get_single_char(Code),
3399 ( answered_default(Code)
3400 -> Choice = default
3401 ; code_type(Code, digit(Choice)),
3402 between(1, Max, Choice)
3403 -> true
3404 ; print_message(warning, pack(menu(reply(1,Max)))),
3405 fail
3406 ).
3407
3413
3414confirm(_Question, Default, Options) :-
3415 Default \== none,
3416 option(interactive(false), Options, true),
3417 !,
3418 Default == yes.
3419confirm(Question, Default, _) :-
3420 between(1, 5, _),
3421 print_message(query, pack(confirm(Question, Default))),
3422 read_yes_no(YesNo, Default),
3423 !,
3424 format(user_error, '~N', []),
3425 YesNo == yes.
3426
3427read_yes_no(YesNo, Default) :-
3428 get_single_char(Code),
3429 code_yes_no(Code, Default, YesNo),
3430 !.
3431
3432code_yes_no(0'y, _, yes).
3433code_yes_no(0'Y, _, yes).
3434code_yes_no(0'n, _, no).
3435code_yes_no(0'N, _, no).
3436code_yes_no(_, none, _) :- !, fail.
3437code_yes_no(C, Default, Default) :-
3438 answered_default(C).
3439
3440answered_default(0'\r).
3441answered_default(0'\n).
3442answered_default(0'\s).
3443
3444
3445 3448
3449:- multifile prolog:message//1. 3450
3451prolog:message(pack(Message)) -->
3452 message(Message).
3453
3454:- discontiguous
3455 message//1,
3456 label//1. 3457
3458message(invalid_term(pack_info_term, Term)) -->
3459 [ 'Invalid package meta data: ~q'-[Term] ].
3460message(invalid_term(pack_status_term, Term)) -->
3461 [ 'Invalid package status data: ~q'-[Term] ].
3462message(directory_exists(Dir)) -->
3463 [ 'Package target directory exists and is not empty:', nl,
3464 '\t~q'-[Dir]
3465 ].
3466message(already_installed(pack(Pack, Version))) -->
3467 [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ].
3468message(already_installed(Pack)) -->
3469 [ 'Pack `~w'' is already installed. Package info:'-[Pack] ].
3470message(kept_foreign(Pack, Arch)) -->
3471 [ 'Found foreign libraries for architecture '-[],
3472 ansi(code, '~q', [Arch]), nl,
3473 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]),
3474 ' to rebuild from sources'-[]
3475 ].
3476message(no_pack_installed(Pack)) -->
3477 [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ].
3478message(dependency_issues(Issues)) -->
3479 [ 'The current set of packs has dependency issues:', nl ],
3480 dep_issues(Issues).
3481message(depends(Pack, Deps)) -->
3482 [ 'The following packs depend on `~w\':'-[Pack], nl ],
3483 pack_list(Deps).
3484message(remove(PackDir)) -->
3485 [ 'Removing ~q and contents'-[PackDir] ].
3486message(remove_existing_pack(PackDir)) -->
3487 [ 'Remove old installation in ~q'-[PackDir] ].
3488message(download_plan(Plan)) -->
3489 [ ansi(bold, 'Installation plan:', []), nl ],
3490 install_plan(Plan, Actions),
3491 install_label(Actions).
3492message(build_plan(Plan)) -->
3493 [ ansi(bold, 'The following packs have post install scripts:', []), nl ],
3494 msg_build_plan(Plan),
3495 [ nl, ansi(bold, 'Run scripts?', []) ].
3496message(no_meta_data(BaseDir)) -->
3497 [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ].
3498message(search_no_matches(Name)) -->
3499 [ 'Search for "~w", returned no matching packages'-[Name] ].
3500message(rebuild(Pack)) -->
3501 [ 'Checking pack "~w" for rebuild ...'-[Pack] ].
3502message(up_to_date([Pack])) -->
3503 !,
3504 [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ].
3505message(up_to_date(Packs)) -->
3506 [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ].
3507message(installed_can_upgrade(List)) -->
3508 sequence(msg_can_upgrade_target, [nl], List).
3509message(new_dependencies(Deps)) -->
3510 [ 'Found new dependencies after downloading (~p).'-[Deps], nl ].
3511message(query_versions(URL)) -->
3512 [ 'Querying "~w" to find new versions ...'-[URL] ].
3513message(no_matching_urls(URL)) -->
3514 [ 'Could not find any matching URL: ~q'-[URL] ].
3515message(found_versions([Latest-_URL|More])) -->
3516 { length(More, Len) },
3517 [ ' Latest version: ~w (~D older)'-[Latest, Len] ].
3518message(build(Pack, PackDir)) -->
3519 [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ].
3520message(contacting_server(Server)) -->
3521 [ 'Contacting server at ~w ...'-[Server], flush ].
3522message(server_reply(true(_))) -->
3523 [ at_same_line, ' ok'-[] ].
3524message(server_reply(false)) -->
3525 [ at_same_line, ' done'-[] ].
3526message(server_reply(exception(E))) -->
3527 [ 'Server reported the following error:'-[], nl ],
3528 '$messages':translate_message(E).
3529message(cannot_create_dir(Alias)) -->
3530 { findall(PackDir,
3531 absolute_file_name(Alias, PackDir, [solutions(all)]),
3532 PackDirs0),
3533 sort(PackDirs0, PackDirs)
3534 },
3535 [ 'Cannot find a place to create a package directory.'-[],
3536 'Considered:'-[]
3537 ],
3538 candidate_dirs(PackDirs).
3539message(conflict(version, [PackV, FileV])) -->
3540 ['Version mismatch: pack.pl: '-[]], msg_version(PackV),
3541 [', file claims version '-[]], msg_version(FileV).
3542message(conflict(name, [PackInfo, FileInfo])) -->
3543 ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]],
3544 [', file claims ~w: ~p'-[FileInfo]].
3545message(no_prolog_response(ContentType, String)) -->
3546 [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl,
3547 '~s'-[String]
3548 ].
3549message(download(begin, Pack, _URL, _DownloadFile)) -->
3550 [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ].
3551message(download(end, _, _, File)) -->
3552 { size_file(File, Bytes) },
3553 [ at_same_line, '~D bytes'-[Bytes] ].
3554message(no_git(URL)) -->
3555 [ 'Cannot install from git repository ', url(URL), '.', nl,
3556 'Cannot find git program and do not know how to download the code', nl,
3557 'from this git service. Please install git and retry.'
3558 ].
3559message(git_no_https(GitURL)) -->
3560 [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ].
3561message(git_branch_not_default(Dir, Default, Current)) -->
3562 [ 'GIT current branch on ', url(Dir), ' is not default.', nl,
3563 ' Current branch: ', ansi(code, '~w', [Current]),
3564 ' default: ', ansi(code, '~w', [Default])
3565 ].
3566message(git_not_clean(Dir)) -->
3567 [ 'GIT working directory is dirty: ', url(Dir), nl,
3568 'Your repository must be clean before publishing.'
3569 ].
3570message(git_push) -->
3571 [ 'Push release to GIT origin?' ].
3572message(git_tag(Tag)) -->
3573 [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ].
3574message(git_release_tag_not_at_head(Tag)) -->
3575 [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl,
3576 'If you want to update the tag, please run ',
3577 ansi(code, 'git tag -d ~w', [Tag])
3578 ].
3579message(git_tag_out_of_sync(Tag)) -->
3580 [ 'Release tag ', ansi(code, '~w', [Tag]),
3581 ' differs from this tag at the origin'
3582 ].
3583
3584message(published(Info, At)) -->
3585 [ 'Published pack ' ], msg_pack(Info), msg_info_version(Info),
3586 [' to be installed from '],
3587 msg_published_address(At).
3588message(publish_failed(Info, Reason)) -->
3589 [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3590 msg_publish_failed(Reason).
3591
3592msg_publish_failed(throw(error(permission_error(register,
3593 pack(_),_URL),_))) -->
3594 [ ' is already registered with a different URL'].
3595msg_publish_failed(download) -->
3596 [' was already published?'].
3597msg_publish_failed(Status) -->
3598 [ ' failed for unknown reason (~p)'-[Status] ].
3599
3600msg_published_address(git(URL)) -->
3601 msg_url(URL, _).
3602msg_published_address(file(URL)) -->
3603 msg_url(URL, _).
3604
3605candidate_dirs([]) --> [].
3606candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T).
3607 3608message(resolve_remove) -->
3609 [ nl, 'Please select an action:', nl, nl ].
3610message(create_pack_dir) -->
3611 [ nl, 'Create directory for packages', nl ].
3612message(menu(item(I, Label))) -->
3613 [ '~t(~d)~6| '-[I] ],
3614 label(Label).
3615message(menu(default_item(I, Label))) -->
3616 [ '~t(~d)~6| * '-[I] ],
3617 label(Label).
3618message(menu(select)) -->
3619 [ nl, 'Your choice? ', flush ].
3620message(confirm(Question, Default)) -->
3621 message(Question),
3622 confirm_default(Default),
3623 [ flush ].
3624message(menu(reply(Min,Max))) -->
3625 ( { Max =:= Min+1 }
3626 -> [ 'Please enter ~w or ~w'-[Min,Max] ]
3627 ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ]
3628 ).
3629
3630 3631dep_issues(Issues) -->
3632 sequence(dep_issue, [nl], Issues).
3633
3634dep_issue(unsatisfied(Pack, Requires)) -->
3635 [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]].
3636dep_issue(conflicts(Pack, Conflict)) -->
3637 [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3638
3643
3644install_label([link]) -->
3645 !,
3646 [ ansi(bold, 'Activate pack?', []) ].
3647install_label([unpack]) -->
3648 !,
3649 [ ansi(bold, 'Unpack archive?', []) ].
3650install_label(_) -->
3651 [ ansi(bold, 'Download packs?', []) ].
3652
3653
3654install_plan(Plan, Actions) -->
3655 install_plan(Plan, Actions, Sec),
3656 sec_warning(Sec).
3657
3658install_plan([], [], _) -->
3659 [].
3660install_plan([H|T], [AH|AT], Sec) -->
3661 install_step(H, AH, Sec), [nl],
3662 install_plan(T, AT, Sec).
3663
3664install_step(Info, keep, _Sec) -->
3665 { Info.get(keep) == true },
3666 !,
3667 [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3668 msg_can_upgrade(Info).
3669install_step(Info, Action, Sec) -->
3670 { From = Info.get(upgrade),
3671 VFrom = From.version,
3672 VTo = Info.get(version),
3673 ( cmp_versions(>=, VTo, VFrom)
3674 -> Label = ansi(bold, ' Upgrade ', [])
3675 ; Label = ansi(warning, ' Downgrade ', [])
3676 )
3677 },
3678 [ Label ], msg_pack(Info),
3679 [ ' from version ~w to ~w'- [From.version, Info.get(version)] ],
3680 install_from(Info, Action, Sec).
3681install_step(Info, Action, Sec) -->
3682 { _From = Info.get(upgrade) },
3683 [ ' Upgrade ' ], msg_pack(Info),
3684 install_from(Info, Action, Sec).
3685install_step(Info, Action, Sec) -->
3686 { Dep = Info.get(dependency_for) },
3687 [ ' Install ' ], msg_pack(Info),
3688 [ ' at version ~w as dependency for '-[Info.version],
3689 ansi(code, '~w', [Dep])
3690 ],
3691 install_from(Info, Action, Sec),
3692 msg_downloads(Info).
3693install_step(Info, Action, Sec) -->
3694 { Info.get(commit) == 'HEAD' },
3695 !,
3696 [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ],
3697 install_from(Info, Action, Sec),
3698 msg_downloads(Info).
3699install_step(Info, link, _Sec) -->
3700 { Info.get(link) == true,
3701 uri_file_name(Info.get(url), Dir)
3702 },
3703 !,
3704 [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ].
3705install_step(Info, Action, Sec) -->
3706 [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ],
3707 install_from(Info, Action, Sec),
3708 msg_downloads(Info).
3709install_step(Info, Action, Sec) -->
3710 [ ' Install ' ], msg_pack(Info),
3711 install_from(Info, Action, Sec),
3712 msg_downloads(Info).
3713
3714install_from(Info, download, Sec) -->
3715 { download_url(Info.url) },
3716 !,
3717 [ ' from ' ], msg_url(Info.url, Sec).
3718install_from(Info, unpack, Sec) -->
3719 [ ' from ' ], msg_url(Info.url, Sec).
3720
3721msg_url(URL, unsafe) -->
3722 { atomic(URL),
3723 atom_concat('http://', Rest, URL)
3724 },
3725 [ ansi(error, '~w', ['http://']), '~w'-[Rest] ].
3726msg_url(URL, _) -->
3727 [ url(URL) ].
3728
3729sec_warning(Sec) -->
3730 { var(Sec) },
3731 !.
3732sec_warning(unsafe) -->
3733 [ ansi(warning, ' WARNING: The installation plan includes downloads \c
3734 from insecure HTTP servers.', []), nl
3735 ].
3736
3737msg_downloads(Info) -->
3738 { Downloads = Info.get(all_downloads),
3739 Downloads > 0
3740 },
3741 [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ],
3742 !.
3743msg_downloads(_) -->
3744 [].
3745
3746msg_pack(Pack) -->
3747 { atom(Pack) },
3748 !,
3749 [ ansi(code, '~w', [Pack]) ].
3750msg_pack(Info) -->
3751 msg_pack(Info.pack).
3752
3753msg_info_version(Info) -->
3754 [ ansi(code, '@~w', [Info.get(version)]) ],
3755 !.
3756msg_info_version(_Info) -->
3757 [].
3758
3762
3763msg_build_plan(Plan) -->
3764 sequence(build_step, [nl], Plan).
3765
3766build_step(Info) -->
3767 [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ].
3768
3769msg_can_upgrade_target(Info) -->
3770 [ ' Pack ' ], msg_pack(Info),
3771 [ ' is installed at version ~w'-[Info.version] ],
3772 msg_can_upgrade(Info).
3773
3774pack_list([]) --> [].
3775pack_list([H|T]) -->
3776 [ ' - Pack ' ], msg_pack(H), [nl],
3777 pack_list(T).
3778
3779label(remove_only(Pack)) -->
3780 [ 'Only remove package ~w (break dependencies)'-[Pack] ].
3781label(remove_deps(Pack, Deps)) -->
3782 { length(Deps, Count) },
3783 [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ].
3784label(create_dir(Dir)) -->
3785 [ '~w'-[Dir] ].
3786label(install_from(git(URL))) -->
3787 !,
3788 [ 'GIT repository at ~w'-[URL] ].
3789label(install_from(URL)) -->
3790 [ '~w'-[URL] ].
3791label(cancel) -->
3792 [ 'Cancel' ].
3793
3794confirm_default(yes) -->
3795 [ ' Y/n? ' ].
3796confirm_default(no) -->
3797 [ ' y/N? ' ].
3798confirm_default(none) -->
3799 [ ' y/n? ' ].
3800
3801msg_version(Version) -->
3802 [ '~w'-[Version] ].
3803
3804msg_can_upgrade(Info) -->
3805 { Latest = Info.get(latest_version) },
3806 [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ].
3807msg_can_upgrade(_) -->
3808 [].
3809
3810
3811 3814
3815local_uri_file_name(URL, FileName) :-
3816 uri_file_name(URL, FileName),
3817 !.
3818local_uri_file_name(URL, FileName) :-
3819 uri_components(URL, Components),
3820 uri_data(scheme, Components, File), File == file,
3821 uri_data(authority, Components, FileNameEnc),
3822 uri_data(path, Components, ''),
3823 uri_encoded(path, FileName, FileNameEnc).
3824
3825det_if(Cond, Goal) :-
3826 ( Cond
3827 -> Goal,
3828 !
3829 ; Goal
3830 ).
3831
3832member_nonvar(_, Var) :-
3833 var(Var),
3834 !,
3835 fail.
3836member_nonvar(E, [E|_]).
3837member_nonvar(E, [_|T]) :-
3838 member_nonvar(E, T)