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(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(31).
205print_property_value(Prop-Fmt, Values) :-
206 !,
207 pvalue_column(C),
208 ansi_format(comment, '% ~w:~t~*|', [Prop, C]),
209 ansi_format(code, Fmt, Values),
210 ansi_format([], '~n', []).
211
212pack_info(Name, Level, Info) :-
213 '$pack':pack(Name, BaseDir),
214 pack_dir_info(BaseDir, Level, Info).
215
216pack_dir_info(BaseDir, Level, Info) :-
217 ( Info = directory(BaseDir)
218 ; pack_info_term(BaseDir, Info)
219 ),
220 pack_level_info(Level, Info, _Format, _Default).
221
222:- public pack_level_info/4. 223
224pack_level_info(_, title(_), 'Title', '<no title>').
225pack_level_info(_, version(_), 'Installed version', '<unknown>').
226pack_level_info(info, automatic(_), 'Automatic (dependency only)', -).
227pack_level_info(info, directory(_), 'Installed in directory', -).
228pack_level_info(info, link(_), 'Installed as link to'-'~w', -).
229pack_level_info(info, built(_,_), 'Built on'-'~w for SWI-Prolog ~w', -).
230pack_level_info(info, author(_, _), 'Author'-'~w <~w>', -).
231pack_level_info(info, maintainer(_, _), 'Maintainer'-'~w <~w>', -).
232pack_level_info(info, packager(_, _), 'Packager'-'~w <~w>', -).
233pack_level_info(info, home(_), 'Home page', -).
234pack_level_info(info, download(_), 'Download URL', -).
235pack_level_info(_, provides(_), 'Provides', -).
236pack_level_info(_, requires(_), 'Requires', -).
237pack_level_info(_, conflicts(_), 'Conflicts with', -).
238pack_level_info(_, replaces(_), 'Replaces packages', -).
239pack_level_info(info, library(_), 'Provided libraries', -).
240pack_level_info(info, autoload(_), 'Autoload', -).
241
242pack_default(Level, Infos, Def) :-
243 pack_level_info(Level, ITerm, _Format, Def),
244 Def \== (-),
245 \+ memberchk(ITerm, Infos).
246
250
251pack_info_term(BaseDir, Info) :-
252 directory_file_path(BaseDir, 'pack.pl', InfoFile),
253 catch(
254 term_in_file(valid_term(pack_info_term), InfoFile, Info),
255 error(existence_error(source_sink, InfoFile), _),
256 ( print_message(error, pack(no_meta_data(BaseDir))),
257 fail
258 )).
259pack_info_term(BaseDir, library(Lib)) :-
260 atom_concat(BaseDir, '/prolog/', LibDir),
261 atom_concat(LibDir, '*.pl', Pattern),
262 expand_file_name(Pattern, Files),
263 maplist(atom_concat(LibDir), Plain, Files),
264 convlist(base_name, Plain, Libs),
265 member(Lib, Libs),
266 Lib \== 'INDEX'.
267pack_info_term(BaseDir, autoload(true)) :-
268 atom_concat(BaseDir, '/prolog/INDEX.pl', IndexFile),
269 exists_file(IndexFile).
270pack_info_term(BaseDir, automatic(Boolean)) :-
271 once(pack_status_dir(BaseDir, automatic(Boolean))).
272pack_info_term(BaseDir, built(Arch, Prolog)) :-
273 pack_status_dir(BaseDir, built(Arch, Prolog, _How)).
274pack_info_term(BaseDir, link(Dest)) :-
275 read_link(BaseDir, _, Dest).
276
277base_name(File, Base) :-
278 file_name_extension(Base, pl, File).
279
283
284:- meta_predicate
285 term_in_file(1, +, -). 286
287term_in_file(Valid, File, Term) :-
288 exists_file(File),
289 setup_call_cleanup(
290 open(File, read, In, [encoding(utf8)]),
291 term_in_stream(Valid, In, Term),
292 close(In)).
293
294term_in_stream(Valid, In, Term) :-
295 repeat,
296 read_term(In, Term0, []),
297 ( Term0 == end_of_file
298 -> !, fail
299 ; Term = Term0,
300 call(Valid, Term0)
301 ).
302
303:- meta_predicate
304 valid_term(1,+). 305
306valid_term(Type, Term) :-
307 Term =.. [Name|Args],
308 same_length(Args, Types),
309 Decl =.. [Name|Types],
310 ( call(Type, Decl)
311 -> maplist(valid_info_arg, Types, Args)
312 ; print_message(warning, pack(invalid_term(Type, Term))),
313 fail
314 ).
315
316valid_info_arg(Type, Arg) :-
317 must_be(Type, Arg).
318
323
324pack_info_term(name(atom)). 325pack_info_term(title(atom)).
326pack_info_term(keywords(list(atom))).
327pack_info_term(description(list(atom))).
328pack_info_term(version(version)).
329pack_info_term(author(atom, email_or_url_or_empty)). 330pack_info_term(maintainer(atom, email_or_url)).
331pack_info_term(packager(atom, email_or_url)).
332pack_info_term(pack_version(nonneg)). 333pack_info_term(home(atom)). 334pack_info_term(download(atom)). 335pack_info_term(provides(atom)). 336pack_info_term(requires(dependency)).
337pack_info_term(conflicts(dependency)). 338pack_info_term(replaces(atom)). 339pack_info_term(autoload(boolean)). 340
341:- multifile
342 error:has_type/2. 343
344error:has_type(version, Version) :-
345 atom(Version),
346 is_version(Version).
347error:has_type(email_or_url, Address) :-
348 atom(Address),
349 ( sub_atom(Address, _, _, _, @)
350 -> true
351 ; uri_is_global(Address)
352 ).
353error:has_type(email_or_url_or_empty, Address) :-
354 ( Address == ''
355 -> true
356 ; error:has_type(email_or_url, Address)
357 ).
358error:has_type(dependency, Value) :-
359 is_dependency(Value).
360
361is_version(Version) :-
362 split_string(Version, ".", "", Parts),
363 maplist(number_string, _, Parts).
364
365is_dependency(Var) :-
366 var(Var),
367 !,
368 fail.
369is_dependency(Token) :-
370 atom(Token),
371 !.
372is_dependency(Term) :-
373 compound(Term),
374 compound_name_arguments(Term, Op, [Token,Version]),
375 atom(Token),
376 cmp(Op, _),
377 is_version(Version),
378 !.
379is_dependency(PrologToken) :-
380 is_prolog_token(PrologToken).
381
382cmp(<, @<).
383cmp(=<, @=<).
384cmp(==, ==).
385cmp(>=, @>=).
386cmp(>, @>).
387
388
389 392
432
433pack_list(Query) :-
434 pack_list(Query, []).
435
436pack_search(Query) :-
437 pack_list(Query, []).
438
439pack_list(Query, Options) :-
440 ( option(installed(true), Options)
441 ; option(outdated(true), Options)
442 ; option(server(false), Options)
443 ),
444 !,
445 local_search(Query, Local),
446 maplist(arg(1), Local, Packs),
447 ( option(server(false), Options)
448 -> Hits = []
449 ; query_pack_server(info(Packs), true(Hits), Options)
450 ),
451 list_hits(Hits, Local, Options).
452pack_list(Query, Options) :-
453 query_pack_server(search(Query), Result, Options),
454 ( Result == false
455 -> ( local_search(Query, Packs),
456 Packs \== []
457 -> forall(member(pack(Pack, Stat, Title, Version, _), Packs),
458 format('~w ~w@~w ~28|- ~w~n',
459 [Stat, Pack, Version, Title]))
460 ; print_message(warning, pack(search_no_matches(Query)))
461 )
462 ; Result = true(Hits), 463 local_search(Query, Local),
464 list_hits(Hits, Local, [])
465 ).
466
467list_hits(Hits, Local, Options) :-
468 append(Hits, Local, All),
469 sort(All, Sorted),
470 join_status(Sorted, Packs0),
471 include(filtered(Options), Packs0, Packs),
472 maplist(list_hit(Options), Packs).
473
474filtered(Options, pack(_,Tag,_,_,_)) :-
475 option(outdated(true), Options),
476 !,
477 Tag == 'U'.
478filtered(_, _).
479
480list_hit(_Options, pack(Pack, Tag, Title, Version, _URL)) =>
481 list_tag(Tag),
482 ansi_format(code, '~w', [Pack]),
483 format('@'),
484 list_version(Tag, Version),
485 format('~35|- ', []),
486 ansi_format(comment, '~w~n', [Title]).
487
488list_tag(Tag) :-
489 tag_color(Tag, Color),
490 ansi_format(Color, '~w ', [Tag]).
491
492list_version(Tag, VersionI-VersionS) =>
493 tag_color(Tag, Color),
494 ansi_format(Color, '~w', [VersionI]),
495 ansi_format(bold, '(~w)', [VersionS]).
496list_version(_Tag, Version) =>
497 ansi_format([], '~w', [Version]).
498
499tag_color('U', warning) :- !.
500tag_color('A', comment) :- !.
501tag_color(_, []).
502
509
510join_status([], []).
511join_status([ pack(Pack, i, Title, Version, URL),
512 pack(Pack, p, Title, Version, _)
513 | T0
514 ],
515 [ pack(Pack, Tag, Title, Version, URL)
516 | T
517 ]) :-
518 !,
519 ( pack_status(Pack, automatic(true))
520 -> Tag = a
521 ; Tag = i
522 ),
523 join_status(T0, T).
524join_status([ pack(Pack, i, Title, VersionI, URLI),
525 pack(Pack, p, _, VersionS, URLS)
526 | T0
527 ],
528 [ pack(Pack, Tag, Title, VersionI-VersionS, URLI-URLS)
529 | T
530 ]) :-
531 !,
532 version_sort_key(VersionI, VDI),
533 version_sort_key(VersionS, VDS),
534 ( VDI @< VDS
535 -> Tag = 'U'
536 ; Tag = 'A'
537 ),
538 join_status(T0, T).
539join_status([ pack(Pack, i, Title, VersionI, URL)
540 | T0
541 ],
542 [ pack(Pack, l, Title, VersionI, URL)
543 | T
544 ]) :-
545 !,
546 join_status(T0, T).
547join_status([H|T0], [H|T]) :-
548 join_status(T0, T).
549
553
554local_search(Query, Packs) :-
555 findall(Pack, matching_installed_pack(Query, Pack), Packs).
556
557matching_installed_pack(Query, pack(Pack, i, Title, Version, URL)) :-
558 current_pack(Pack),
559 findall(Term,
560 ( pack_info(Pack, _, Term),
561 search_info(Term)
562 ), Info),
563 ( sub_atom_icasechk(Pack, _, Query)
564 -> true
565 ; memberchk(title(Title), Info),
566 sub_atom_icasechk(Title, _, Query)
567 ),
568 option(title(Title), Info, '<no title>'),
569 option(version(Version), Info, '<no version>'),
570 option(download(URL), Info, '<no download url>').
571
572search_info(title(_)).
573search_info(version(_)).
574search_info(download(_)).
575
576
577 580
678
679pack_install(Spec) :-
680 pack_default_options(Spec, Pack, [], Options),
681 pack_install(Pack, [pack(Pack)|Options]).
682
683pack_install(Specs, Options) :-
684 is_list(Specs),
685 !,
686 maplist(pack_options(Options), Specs, Pairs),
687 pack_install_dir(PackTopDir, Options),
688 pack_install_set(Pairs, PackTopDir, Options).
689pack_install(Spec, Options) :-
690 pack_default_options(Spec, Pack, Options, DefOptions),
691 ( option(already_installed(Installed), DefOptions)
692 -> print_message(informational, pack(already_installed(Installed)))
693 ; merge_options(Options, DefOptions, PackOptions),
694 pack_install_dir(PackTopDir, PackOptions),
695 pack_install_set([Pack-PackOptions], PackTopDir, Options)
696 ).
697
698pack_options(Options, Spec, Pack-PackOptions) :-
699 pack_default_options(Spec, Pack, Options, DefOptions),
700 merge_options(Options, DefOptions, PackOptions).
701
724
725
726pack_default_options(_Spec, Pack, OptsIn, Options) :- 727 option(already_installed(pack(Pack,_Version)), OptsIn),
728 !,
729 Options = OptsIn.
730pack_default_options(_Spec, Pack, OptsIn, Options) :- 731 option(url(URL), OptsIn),
732 !,
733 ( option(git(_), OptsIn)
734 -> Options = OptsIn
735 ; git_url(URL, Pack, OptsIn)
736 -> Options = [git(true)|OptsIn]
737 ; Options = OptsIn
738 ),
739 ( nonvar(Pack)
740 -> true
741 ; option(pack(Pack), Options)
742 -> true
743 ; pack_version_file(Pack, _Version, URL)
744 ).
745pack_default_options(Archive, Pack, OptsIn, Options) :- 746 must_be(atom, Archive),
747 \+ uri_is_global(Archive),
748 expand_file_name(Archive, [File]),
749 exists_file(File),
750 !,
751 ( pack_version_file(Pack, Version, File)
752 -> uri_file_name(FileURL, File),
753 merge_options([url(FileURL), version(Version)], OptsIn, Options)
754 ; domain_error(pack_file_name, Archive)
755 ).
756pack_default_options(URL, Pack, OptsIn, Options) :- 757 git_url(URL, Pack, OptsIn),
758 !,
759 merge_options([git(true), url(URL)], OptsIn, Options).
760pack_default_options(FileURL, Pack, _, Options) :- 761 uri_file_name(FileURL, Dir),
762 exists_directory(Dir),
763 pack_info_term(Dir, name(Pack)),
764 !,
765 ( pack_info_term(Dir, version(Version))
766 -> uri_file_name(DirURL, Dir),
767 Options = [url(DirURL), version(Version)]
768 ; throw(error(existence_error(key, version, Dir),_))
769 ).
770pack_default_options('.', Pack, OptsIn, Options) :- 771 pack_info_term('.', name(Pack)),
772 !,
773 working_directory(Dir, Dir),
774 ( pack_info_term(Dir, version(Version))
775 -> uri_file_name(DirURL, Dir),
776 NewOptions = [url(DirURL), version(Version) | Options1],
777 ( current_prolog_flag(windows, true)
778 -> Options1 = []
779 ; Options1 = [link(true), rebuild(make)]
780 ),
781 merge_options(NewOptions, OptsIn, Options)
782 ; throw(error(existence_error(key, version, Dir),_))
783 ).
784pack_default_options(URL, Pack, OptsIn, Options) :- 785 pack_version_file(Pack, Version, URL),
786 download_url(URL),
787 !,
788 available_download_versions(URL, Available, Options),
789 Available = [URLVersion-LatestURL|_],
790 NewOptions = [url(LatestURL)|VersionOptions],
791 version_options(Version, URLVersion, Available, VersionOptions),
792 merge_options(NewOptions, OptsIn, Options).
793pack_default_options(Pack, Pack, Options, Options) :- 794 \+ uri_is_global(Pack).
795
796version_options(Version, Version, _, [version(Version)]) :- !.
797version_options(Version, _, Available, [versions(Available)]) :-
798 sub_atom(Version, _, _, _, *),
799 !.
800version_options(_, _, _, []).
801
819
820pack_install_dir(PackDir, Options) :-
821 option(pack_directory(PackDir), Options),
822 ensure_directory(PackDir),
823 !.
824pack_install_dir(PackDir, Options) :-
825 base_alias(Alias, Options),
826 absolute_file_name(Alias, PackDir,
827 [ file_type(directory),
828 access(write),
829 file_errors(fail)
830 ]),
831 !.
832pack_install_dir(PackDir, Options) :-
833 pack_create_install_dir(PackDir, Options).
834
835base_alias(Alias, Options) :-
836 option(global(true), Options),
837 !,
838 Alias = common_app_data(pack).
839base_alias(Alias, Options) :-
840 option(global(false), Options),
841 !,
842 Alias = user_app_data(pack).
843base_alias(Alias, _Options) :-
844 Alias = pack('.').
845
846pack_create_install_dir(PackDir, Options) :-
847 base_alias(Alias, Options),
848 findall(Candidate = create_dir(Candidate),
849 ( absolute_file_name(Alias, Candidate, [solutions(all)]),
850 \+ exists_file(Candidate),
851 \+ exists_directory(Candidate),
852 file_directory_name(Candidate, Super),
853 ( exists_directory(Super)
854 -> access_file(Super, write)
855 ; true
856 )
857 ),
858 Candidates0),
859 list_to_set(Candidates0, Candidates), 860 pack_create_install_dir(Candidates, PackDir, Options).
861
862pack_create_install_dir(Candidates, PackDir, Options) :-
863 Candidates = [Default=_|_],
864 !,
865 append(Candidates, [cancel=cancel], Menu),
866 menu(pack(create_pack_dir), Menu, Default, Selected, Options),
867 Selected \== cancel,
868 ( catch(make_directory_path(Selected), E,
869 (print_message(warning, E), fail))
870 -> PackDir = Selected
871 ; delete(Candidates, PackDir=create_dir(PackDir), Remaining),
872 pack_create_install_dir(Remaining, PackDir, Options)
873 ).
874pack_create_install_dir(_, _, _) :-
875 print_message(error, pack(cannot_create_dir(pack(.)))),
876 fail.
877
889
890pack_unpack_from_local(Source0, PackTopDir, Name, PackDir, Options) :-
891 exists_directory(Source0),
892 remove_slash(Source0, Source),
893 !,
894 directory_file_path(PackTopDir, Name, PackDir),
895 ( option(link(true), Options)
896 -> ( same_file(Source, PackDir)
897 -> true
898 ; remove_existing_pack(PackDir, Options),
899 atom_concat(PackTopDir, '/', PackTopDirS),
900 relative_file_name(Source, PackTopDirS, RelPath),
901 link_file(RelPath, PackDir, symbolic),
902 assertion(same_file(Source, PackDir))
903 )
904 ; \+ option(git(false), Options),
905 is_git_directory(Source)
906 -> remove_existing_pack(PackDir, Options),
907 run_process(path(git), [clone, Source, PackDir], [])
908 ; prepare_pack_dir(PackDir, Options),
909 copy_directory(Source, PackDir)
910 ).
911pack_unpack_from_local(Source, PackTopDir, Name, PackDir, Options) :-
912 exists_file(Source),
913 directory_file_path(PackTopDir, Name, PackDir),
914 prepare_pack_dir(PackDir, Options),
915 pack_unpack(Source, PackDir, Name, Options).
916
923
924:- if(exists_source(library(archive))). 925pack_unpack(Source, PackDir, Pack, Options) :-
926 ensure_loaded_archive,
927 pack_archive_info(Source, Pack, _Info, StripOptions),
928 prepare_pack_dir(PackDir, Options),
929 archive_extract(Source, PackDir,
930 [ exclude(['._*']) 931 | StripOptions
932 ]).
933:- else. 934pack_unpack(_,_,_,_) :-
935 existence_error(library, archive).
936:- endif. 937
943
944pack_install_local(M:Gen, Dir, Options) :-
945 findall(Pack-PackOptions, call(M:Gen, Pack, PackOptions), Pairs),
946 pack_install_set(Pairs, Dir, Options).
947
948pack_install_set(Pairs, Dir, Options) :-
949 must_be(list(pair), Pairs),
950 ensure_directory(Dir),
951 partition(known_media, Pairs, Local, Remote),
952 maplist(pack_options_to_versions, Local, LocalVersions),
953 ( Remote == []
954 -> AllVersions = LocalVersions
955 ; pairs_keys(Remote, Packs),
956 prolog_description(Properties),
957 query_pack_server(versions(Packs, Properties), Result, Options),
958 ( Result = true(RemoteVersions)
959 -> append(LocalVersions, RemoteVersions, AllVersions)
960 ; print_message(error, pack(query_failed(Result))),
961 fail
962 )
963 ),
964 local_packs(Dir, Existing),
965 pack_resolve(Pairs, Existing, AllVersions, Plan0, Options),
966 !, 967 maplist(hsts_info(Options), Plan0, Plan),
968 Options1 = [pack_directory(Dir), installed_packs(Existing)|Options],
969 download_plan(Pairs, Plan, PlanB, Options1),
970 register_downloads(PlanB, Options),
971 maplist(update_automatic, PlanB),
972 build_plan(PlanB, Built, Options1),
973 publish_download(PlanB, Options),
974 work_done(Pairs, Plan, PlanB, Built, Options).
975
976hsts_info(Options, Info0, Info) :-
977 hsts(Info0.get(url), URL, Options),
978 !,
979 Info = Info0.put(url, URL).
980hsts_info(_Options, Info, Info).
981
988
989known_media(_-Options) :-
990 option(url(_), Options).
991
1007
1008pack_resolve(Pairs, Existing, Versions, Plan, Options) :-
1009 insert_existing(Existing, Versions, AllVersions, Options),
1010 phrase(select_version(Pairs, AllVersions,
1011 [ plan(PlanA), 1012 dependency_for([]) 1013 | Options
1014 ]),
1015 PlanA),
1016 mark_installed(PlanA, Existing, Plan).
1017
1026
1027:- det(insert_existing/4). 1028insert_existing(Existing, [], Versions, _Options) =>
1029 maplist(existing_to_versions, Existing, Versions).
1030insert_existing(Existing, [Pack-Versions|T0], AllPackVersions, Options),
1031 select(Installed, Existing, Existing2),
1032 Installed.pack == Pack =>
1033 can_upgrade(Installed, Versions, Installed2),
1034 insert_existing_(Installed2, Versions, AllVersions, Options),
1035 AllPackVersions = [Pack-AllVersions|T],
1036 insert_existing(Existing2, T0, T, Options).
1037insert_existing(Existing, [H|T0], AllVersions, Options) =>
1038 AllVersions = [H|T],
1039 insert_existing(Existing, T0, T, Options).
1040
1041existing_to_versions(Installed, Pack-[Version-[Installed]]) :-
1042 Pack = Installed.pack,
1043 Version = Installed.version.
1044
1045insert_existing_(Installed, Versions, AllVersions, Options) :-
1046 option(upgrade(true), Options),
1047 !,
1048 insert_existing_(Installed, Versions, AllVersions).
1049insert_existing_(Installed, Versions, AllVersions, _) :-
1050 AllVersions = [Installed.version-[Installed]|Versions].
1051
1052insert_existing_(Installed, [H|T0], [H|T]) :-
1053 H = V0-_Infos,
1054 cmp_versions(>, V0, Installed.version),
1055 !,
1056 insert_existing_(Installed, T0, T).
1057insert_existing_(Installed, [H0|T], [H|T]) :-
1058 H0 = V0-Infos,
1059 V0 == Installed.version,
1060 !,
1061 H = V0-[Installed|Infos].
1062insert_existing_(Installed, Versions, All) :-
1063 All = [Installed.version-[Installed]|Versions].
1064
1069
1070can_upgrade(Info, [Version-_|_], Info2) :-
1071 cmp_versions(>, Version, Info.version),
1072 !,
1073 Info2 = Info.put(latest_version, Version).
1074can_upgrade(Info, _, Info).
1075
1081
1082mark_installed([], _, []).
1083mark_installed([Info|T], Existing, Plan) :-
1084 ( member(Installed, Existing),
1085 Installed.pack == Info.pack
1086 -> ( ( Installed.git == true
1087 -> Info.git == true,
1088 Installed.hash == Info.hash
1089 ; Version = Info.get(version)
1090 -> Installed.version == Version
1091 )
1092 -> Plan = [Info.put(keep, true)|PlanT] 1093 ; Plan = [Info.put(upgrade, Installed)|PlanT] 1094 )
1095 ; Plan = [Info|PlanT] 1096 ),
1097 mark_installed(T, Existing, PlanT).
1098
1104
1105select_version([], _, _) -->
1106 [].
1107select_version([Pack-PackOptions|More], Versions, Options) -->
1108 { memberchk(Pack-PackVersions, Versions),
1109 member(Version-Infos, PackVersions),
1110 compatible_version(Pack, Version, PackOptions),
1111 member(Info, Infos),
1112 pack_options_compatible_with_info(Info, PackOptions),
1113 pack_satisfies(Pack, Version, Info, Info2, PackOptions),
1114 all_downloads(PackVersions, Downloads)
1115 },
1116 add_to_plan(Info2.put(_{version: Version, all_downloads:Downloads}),
1117 Versions, Options),
1118 select_version(More, Versions, Options).
1119select_version([Pack-_PackOptions|_More], _Versions, _Options) -->
1120 { existence_error(pack, Pack) }. 1121
1122all_downloads(PackVersions, AllDownloads) :-
1123 aggregate_all(sum(Downloads),
1124 ( member(_Version-Infos, PackVersions),
1125 member(Info, Infos),
1126 get_dict(downloads, Info, Downloads)
1127 ),
1128 AllDownloads).
1129
1130add_requirements([], _, _) -->
1131 [].
1132add_requirements([H|T], Versions, Options) -->
1133 { is_prolog_token(H),
1134 !,
1135 prolog_satisfies(H)
1136 },
1137 add_requirements(T, Versions, Options).
1138add_requirements([H|T], Versions, Options) -->
1139 { member(Pack-PackVersions, Versions),
1140 member(Version-Infos, PackVersions),
1141 member(Info, Infos),
1142 ( Provides = @(Pack,Version)
1143 ; member(Provides, Info.get(provides))
1144 ),
1145 satisfies_req(Provides, H),
1146 all_downloads(PackVersions, Downloads)
1147 },
1148 add_to_plan(Info.put(_{version: Version, all_downloads:Downloads}),
1149 Versions, Options),
1150 add_requirements(T, Versions, Options).
1151
1157
1158add_to_plan(Info, _Versions, Options) -->
1159 { option(plan(Plan), Options),
1160 member_nonvar(Planned, Plan),
1161 Planned.pack == Info.pack,
1162 !,
1163 same_version(Planned, Info) 1164 }.
1165add_to_plan(Info, _Versions, _Options) -->
1166 { member(Conflict, Info.get(conflicts)),
1167 is_prolog_token(Conflict),
1168 prolog_satisfies(Conflict),
1169 !,
1170 fail 1171 }.
1172add_to_plan(Info, _Versions, Options) -->
1173 { option(plan(Plan), Options),
1174 member_nonvar(Planned, Plan),
1175 info_conflicts(Info, Planned), 1176 !,
1177 fail
1178 }.
1179add_to_plan(Info, Versions, Options) -->
1180 { select_option(dependency_for(Dep0), Options, Options1),
1181 Options2 = [dependency_for([Info.pack|Dep0])|Options1],
1182 ( Dep0 = [DepFor|_]
1183 -> add_dependency_for(DepFor, Info, Info1)
1184 ; Info1 = Info
1185 )
1186 },
1187 [Info1],
1188 add_requirements(Info.get(requires,[]), Versions, Options2).
1189
1190add_dependency_for(Pack, Info, Info) :-
1191 Old = Info.get(dependency_for),
1192 !,
1193 b_set_dict(dependency_for, Info, [Pack|Old]).
1194add_dependency_for(Pack, Info0, Info) :-
1195 Info = Info0.put(dependency_for, [Pack]).
1196
1197same_version(Info, Info) :-
1198 !.
1199same_version(Planned, Info) :-
1200 Hash = Planned.get(hash),
1201 Hash \== (-),
1202 !,
1203 Hash == Info.get(hash).
1204same_version(Planned, Info) :-
1205 Planned.get(version) == Info.get(version).
1206
1210
1211info_conflicts(Info, Planned) :-
1212 info_conflicts_(Info, Planned),
1213 !.
1214info_conflicts(Info, Planned) :-
1215 info_conflicts_(Planned, Info),
1216 !.
1217
1218info_conflicts_(Info, Planned) :-
1219 member(Conflict, Info.get(conflicts)),
1220 \+ is_prolog_token(Conflict),
1221 info_provides(Planned, Provides),
1222 satisfies_req(Provides, Conflict),
1223 !.
1224
1225info_provides(Info, Provides) :-
1226 ( Provides = Info.pack@Info.version
1227 ; member(Provides, Info.get(provides))
1228 ).
1229
1234
1235pack_satisfies(_Pack, _Version, Info0, Info, Options) :-
1236 option(commit('HEAD'), Options),
1237 !,
1238 Info0.get(git) == true,
1239 Info = Info0.put(commit, 'HEAD').
1240pack_satisfies(_Pack, _Version, Info, Info, Options) :-
1241 option(commit(Commit), Options),
1242 !,
1243 Commit == Info.get(hash).
1244pack_satisfies(Pack, Version, Info, Info, Options) :-
1245 option(version(ReqVersion), Options),
1246 !,
1247 satisfies_version(Pack, Version, ReqVersion).
1248pack_satisfies(_Pack, _Version, Info, Info, _Options).
1249
1251
1252satisfies_version(Pack, Version, ReqVersion) :-
1253 catch(require_version(pack(Pack), Version, ReqVersion),
1254 error(version_error(pack(Pack), Version, ReqVersion),_),
1255 fail).
1256
1260
1261satisfies_req(Token, Token) => true.
1262satisfies_req(@(Token,_), Token) => true.
1263satisfies_req(@(Token,PrvVersion), Req), cmp(Req, Token, Cmp, ReqVersion) =>
1264 cmp_versions(Cmp, PrvVersion, ReqVersion).
1265satisfies_req(_,_) => fail.
1266
1267cmp(Token < Version, Token, <, Version).
1268cmp(Token =< Version, Token, =<, Version).
1269cmp(Token = Version, Token, =, Version).
1270cmp(Token == Version, Token, ==, Version).
1271cmp(Token >= Version, Token, >=, Version).
1272cmp(Token > Version, Token, >, Version).
1273
1284
1285:- det(pack_options_to_versions/2). 1286pack_options_to_versions(Pack-PackOptions, Pack-Versions) :-
1287 option(versions(Available), PackOptions), !,
1288 maplist(version_url_info(Pack, PackOptions), Available, Versions).
1289pack_options_to_versions(Pack-PackOptions, Pack-[Version-[Info]]) :-
1290 option(url(URL), PackOptions),
1291 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1292 dict_create(Info, #,
1293 [ pack-Pack,
1294 url-URL
1295 | Pairs
1296 ]),
1297 Version = Info.get(version, '0.0.0').
1298
1299version_url_info(Pack, PackOptions, Version-URL, Version-[Info]) :-
1300 findall(Prop,
1301 ( option_info_prop(PackOptions, Prop),
1302 Prop \= version-_
1303 ),
1304 Pairs),
1305 dict_create(Info, #,
1306 [ pack-Pack,
1307 url-URL,
1308 version-Version
1309 | Pairs
1310 ]).
1311
1312option_info_prop(PackOptions, Prop-Value) :-
1313 option_info(Prop),
1314 Opt =.. [Prop,Value],
1315 option(Opt, PackOptions).
1316
1317option_info(git).
1318option_info(hash).
1319option_info(version).
1320option_info(branch).
1321option_info(link).
1322
1327
1328compatible_version(Pack, Version, PackOptions) :-
1329 option(version(ReqVersion), PackOptions),
1330 !,
1331 satisfies_version(Pack, Version, ReqVersion).
1332compatible_version(_, _, _).
1333
1338
1339pack_options_compatible_with_info(Info, PackOptions) :-
1340 findall(Prop, option_info_prop(PackOptions, Prop), Pairs),
1341 dict_create(Dict, _, Pairs),
1342 Dict >:< Info.
1343
1351
1352download_plan(_Targets, Plan, Plan, _Options) :-
1353 exclude(installed, Plan, []),
1354 !.
1355download_plan(Targets, Plan0, Plan, Options) :-
1356 confirm(download_plan(Plan0), yes, Options),
1357 maplist(download_from_info(Options), Plan0, Plan1),
1358 plan_unsatisfied_dependencies(Plan1, Deps),
1359 ( Deps == []
1360 -> Plan = Plan1
1361 ; print_message(informational, pack(new_dependencies(Deps))),
1362 prolog_description(Properties),
1363 query_pack_server(versions(Deps, Properties), Result, []),
1364 ( Result = true(Versions)
1365 -> merge_installed(Plan1, Options, Existing),
1366 pack_resolve(Targets, Existing, Versions, Plan2, Options),
1367 !,
1368 download_plan(Targets, Plan2, Plan, Options)
1369 ; print_message(error, pack(query_failed(Result))),
1370 fail
1371 )
1372 ).
1373
1381
1382merge_installed(Plan, Options, Existing) :-
1383 option(installed_packs(Installed), Options, []),
1384 exclude(in_plan(Plan), Installed, InstalledRest),
1385 append(Plan, InstalledRest, Existing).
1386
1387in_plan(Plan, Info) :-
1388 member(P, Plan),
1389 P.pack == Info.pack,
1390 !.
1391
1396
1397plan_unsatisfied_dependencies(Plan, Deps) :-
1398 phrase(plan_unsatisfied_dependencies(Plan, Plan), Deps).
1399
1400plan_unsatisfied_dependencies([], _) -->
1401 [].
1402plan_unsatisfied_dependencies([Info|Infos], Plan) -->
1403 { Deps = Info.get(requires) },
1404 plan_unsatisfied_requirements(Deps, Plan),
1405 plan_unsatisfied_dependencies(Infos, Plan).
1406
1407plan_unsatisfied_requirements([], _) -->
1408 [].
1409plan_unsatisfied_requirements([H|T], Plan) -->
1410 { is_prolog_token(H), 1411 prolog_satisfies(H)
1412 },
1413 !,
1414 plan_unsatisfied_requirements(T, Plan).
1415plan_unsatisfied_requirements([H|T], Plan) -->
1416 { member(Info, Plan),
1417 ( ( Version = Info.get(version)
1418 -> Provides = @(Info.get(pack), Version)
1419 ; Provides = Info.get(pack)
1420 )
1421 ; member(Provides, Info.get(provides))
1422 ),
1423 satisfies_req(Provides, H)
1424 }, !,
1425 plan_unsatisfied_requirements(T, Plan).
1426plan_unsatisfied_requirements([H|T], Plan) -->
1427 [H],
1428 plan_unsatisfied_requirements(T, Plan).
1429
1430
1436
1437build_plan(Plan, Ordered, Options) :-
1438 maplist(decide_autoload_pack(Options), Plan, Plan1),
1439 partition(needs_rebuild_from_info(Options), Plan1, ToBuild, NoBuild),
1440 maplist(attach_from_info(Options), NoBuild),
1441 ( ToBuild == []
1442 -> post_install_autoload(NoBuild),
1443 Ordered = []
1444 ; order_builds(ToBuild, Ordered),
1445 confirm(build_plan(Ordered), yes, Options),
1446 maplist(exec_plan_rebuild_step(Options), Ordered)
1447 ).
1448
1452
1453needs_rebuild_from_info(Options, Info) :-
1454 PackDir = Info.installed,
1455 is_foreign_pack(PackDir, _),
1456 \+ is_built(PackDir, Options).
1457
1464
1465is_built(PackDir, _Options) :-
1466 current_prolog_flag(arch, Arch),
1467 prolog_version_dotted(Version), 1468 pack_status_dir(PackDir, built(Arch, Version, _)).
1469
1474
1475order_builds(ToBuild, Ordered) :-
1476 findall(Pack-Dependent, dep_edge(ToBuild, Pack, Dependent), Edges),
1477 maplist(get_dict(pack), ToBuild, Packs),
1478 vertices_edges_to_ugraph(Packs, Edges, Graph),
1479 ugraph_layers(Graph, Layers),
1480 append(Layers, PackNames),
1481 maplist(pack_info_from_name(ToBuild), PackNames, Ordered).
1482
1488
1489dep_edge(Infos, Pack, Dependent) :-
1490 member(Info, Infos),
1491 Pack = Info.pack,
1492 member(Dependent, Info.get(dependency_for)),
1493 ( member(DepInfo, Infos),
1494 DepInfo.pack == Dependent
1495 -> true
1496 ).
1497
1498:- det(pack_info_from_name/3). 1499pack_info_from_name(Infos, Pack, Info) :-
1500 member(Info, Infos),
1501 Info.pack == Pack,
1502 !.
1503
1507
1508exec_plan_rebuild_step(Options, Info) :-
1509 print_message(informational, pack(build(Info.pack, Info.installed))),
1510 pack_post_install(Info, Options),
1511 attach_from_info(Options, Info).
1512
1516
1517attach_from_info(_Options, Info) :-
1518 Info.get(keep) == true,
1519 !.
1520attach_from_info(Options, Info) :-
1521 ( option(pack_directory(_Parent), Options)
1522 -> pack_attach(Info.installed, [duplicate(replace)])
1523 ; pack_attach(Info.installed, [])
1524 ).
1525
1533
1534download_from_info(Options, Info0, Info), option(dryrun(true), Options) =>
1535 print_term(Info0, [nl(true)]),
1536 Info = Info0.
1537download_from_info(_Options, Info0, Info), installed(Info0) =>
1538 Info = Info0.
1539download_from_info(_Options, Info0, Info),
1540 _{upgrade:OldInfo, git:true} :< Info0,
1541 is_git_directory(OldInfo.installed) =>
1542 PackDir = OldInfo.installed,
1543 git_checkout_version(PackDir, [commit(Info0.hash)]),
1544 reload_info(PackDir, Info0, Info).
1545download_from_info(Options, Info0, Info),
1546 _{upgrade:OldInfo} :< Info0 =>
1547 PackDir = OldInfo.installed,
1548 detach_pack(OldInfo.pack, PackDir),
1549 delete_directory_and_contents(PackDir),
1550 del_dict(upgrade, Info0, _, Info1),
1551 download_from_info(Options, Info1, Info).
1552download_from_info(Options, Info0, Info),
1553 _{url:URL, git:true} :< Info0, \+ have_git =>
1554 git_archive_url(URL, Archive, Options),
1555 download_from_info([git_url(URL)|Options],
1556 Info0.put(_{ url:Archive,
1557 git:false,
1558 git_url:URL
1559 }),
1560 Info1),
1561 1562 ( Info1.get(version) == Info0.get(version),
1563 Hash = Info0.get(hash)
1564 -> Info = Info1.put(hash, Hash)
1565 ; Info = Info1
1566 ).
1567download_from_info(Options, Info0, Info),
1568 _{url:URL} :< Info0 =>
1569 select_option(pack_directory(Dir), Options, Options1),
1570 select_option(version(_), Options1, Options2, _),
1571 download_info_extra(Info0, InstallOptions, Options2),
1572 pack_download_from_url(URL, Dir, Info0.pack,
1573 [ interactive(false),
1574 pack_dir(PackDir)
1575 | InstallOptions
1576 ]),
1577 reload_info(PackDir, Info0, Info).
1578
(Info, [git(true),commit(Hash)|Options], Options) :-
1580 Info.get(git) == true,
1581 !,
1582 Hash = Info.get(commit, 'HEAD').
1583download_info_extra(Info, [link(true)|Options], Options) :-
1584 Info.get(link) == true,
1585 !.
1586download_info_extra(_, Options, Options).
1587
1588installed(Info) :-
1589 _ = Info.get(installed).
1590
1591detach_pack(Pack, PackDir) :-
1592 ( current_pack(Pack, PackDir)
1593 -> '$pack_detach'(Pack, PackDir)
1594 ; true
1595 ).
1596
1603
1604reload_info(_PackDir, Info, Info) :-
1605 _ = Info.get(installed), 1606 !.
1607reload_info(PackDir, Info0, Info) :-
1608 local_pack_info(PackDir, Info1),
1609 Info = Info0.put(installed, PackDir)
1610 .put(downloaded, Info0.url)
1611 .put(Info1).
1612
1617
1618work_done(_, _, _, _, Options),
1619 option(silent(true), Options) =>
1620 true.
1621work_done(Targets, Plan, Plan, [], _Options) =>
1622 convlist(can_upgrade_target(Plan), Targets, CanUpgrade),
1623 ( CanUpgrade == []
1624 -> pairs_keys(Targets, Packs),
1625 print_message(informational, pack(up_to_date(Packs)))
1626 ; print_message(informational, pack(installed_can_upgrade(CanUpgrade)))
1627 ).
1628work_done(_, _, _, _, _) =>
1629 true.
1630
1631can_upgrade_target(Plan, Pack-_, Info) =>
1632 member(Info, Plan),
1633 Info.pack == Pack,
1634 !,
1635 _ = Info.get(latest_version).
1636
1641
1642local_packs(Dir, Packs) :-
1643 findall(Pack, pack_in_subdir(Dir, Pack), Packs).
1644
1645pack_in_subdir(Dir, Info) :-
1646 directory_member(Dir, PackDir,
1647 [ file_type(directory),
1648 hidden(false)
1649 ]),
1650 local_pack_info(PackDir, Info).
1651
1652local_pack_info(PackDir,
1653 #{ pack: Pack,
1654 version: Version,
1655 title: Title,
1656 hash: Hash,
1657 url: URL,
1658 git: IsGit,
1659 requires: Requires,
1660 provides: Provides,
1661 conflicts: Conflicts,
1662 installed: PackDir
1663 }) :-
1664 directory_file_path(PackDir, 'pack.pl', MetaFile),
1665 exists_file(MetaFile),
1666 file_base_name(PackDir, DirName),
1667 findall(Term, pack_dir_info(PackDir, _, Term), Info),
1668 option(pack(Pack), Info, DirName),
1669 option(title(Title), Info, '<no title>'),
1670 option(version(Version), Info, '<no version>'),
1671 option(download(URL), Info, '<no download url>'),
1672 findall(Req, member(requires(Req), Info), Requires),
1673 findall(Prv, member(provides(Prv), Info), Provides),
1674 findall(Cfl, member(conflicts(Cfl), Info), Conflicts),
1675 ( have_git,
1676 is_git_directory(PackDir)
1677 -> git_hash(Hash, [directory(PackDir)]),
1678 IsGit = true
1679 ; Hash = '-',
1680 IsGit = false
1681 ).
1682
1683
1684 1687
1696
1697prolog_description([prolog(swi(Version))]) :-
1698 prolog_version(Version).
1699
1700prolog_version(Version) :-
1701 current_prolog_flag(version_git, Version),
1702 !.
1703prolog_version(Version) :-
1704 prolog_version_dotted(Version).
1705
1706prolog_version_dotted(Version) :-
1707 current_prolog_flag(version_data, swi(Major, Minor, Patch, _)),
1708 VNumbers = [Major, Minor, Patch],
1709 atomic_list_concat(VNumbers, '.', Version).
1710
1715
1716is_prolog_token(Token), cmp(Token, prolog, _Cmp, _Version) => true.
1717is_prolog_token(prolog:Feature), atom(Feature) => true.
1718is_prolog_token(prolog:Feature), flag_value_feature(Feature, _Flag, _Value) =>
1719 true.
1720is_prolog_token(_) => fail.
1721
1734
1735prolog_satisfies(Token), cmp(Token, prolog, Cmp, ReqVersion) =>
1736 prolog_version(CurrentVersion),
1737 cmp_versions(Cmp, CurrentVersion, ReqVersion).
1738prolog_satisfies(prolog:library(Lib)), atom(Lib) =>
1739 exists_source(library(Lib)).
1740prolog_satisfies(prolog:Feature), atom(Feature) =>
1741 current_prolog_flag(Feature, true).
1742prolog_satisfies(prolog:Feature), flag_value_feature(Feature, Flag, Value) =>
1743 current_prolog_flag(Flag, Value).
1744
1745flag_value_feature(Feature, Flag, Value) :-
1746 compound(Feature),
1747 compound_name_arguments(Feature, Flag, [Value]),
1748 atom(Flag).
1749
1750
1751 1754
1766
1767:- if(exists_source(library(archive))). 1768ensure_loaded_archive :-
1769 current_predicate(archive_open/3),
1770 !.
1771ensure_loaded_archive :-
1772 use_module(library(archive)).
1773
1774pack_archive_info(Archive, Pack, [archive_size(Bytes)|Info], Strip) :-
1775 ensure_loaded_archive,
1776 size_file(Archive, Bytes),
1777 setup_call_cleanup(
1778 archive_open(Archive, Handle, []),
1779 ( repeat,
1780 ( archive_next_header(Handle, InfoFile)
1781 -> true
1782 ; !, fail
1783 )
1784 ),
1785 archive_close(Handle)),
1786 file_base_name(InfoFile, 'pack.pl'),
1787 atom_concat(Prefix, 'pack.pl', InfoFile),
1788 strip_option(Prefix, Pack, Strip),
1789 setup_call_cleanup(
1790 archive_open_entry(Handle, Stream),
1791 read_stream_to_terms(Stream, Info),
1792 close(Stream)),
1793 !,
1794 must_be(ground, Info),
1795 maplist(valid_term(pack_info_term), Info).
1796:- else. 1797pack_archive_info(_, _, _, _) :-
1798 existence_error(library, archive).
1799:- endif. 1800pack_archive_info(_, _, _, _) :-
1801 existence_error(pack_file, 'pack.pl').
1802
1803strip_option('', _, []) :- !.
1804strip_option('./', _, []) :- !.
1805strip_option(Prefix, Pack, [remove_prefix(Prefix)]) :-
1806 atom_concat(PrefixDir, /, Prefix),
1807 file_base_name(PrefixDir, Base),
1808 ( Base == Pack
1809 -> true
1810 ; pack_version_file(Pack, _, Base)
1811 -> true
1812 ; \+ sub_atom(PrefixDir, _, _, _, /)
1813 ).
1814
1815read_stream_to_terms(Stream, Terms) :-
1816 read(Stream, Term0),
1817 read_stream_to_terms(Term0, Stream, Terms).
1818
1819read_stream_to_terms(end_of_file, _, []) :- !.
1820read_stream_to_terms(Term0, Stream, [Term0|Terms]) :-
1821 read(Stream, Term1),
1822 read_stream_to_terms(Term1, Stream, Terms).
1823
1824
1829
1830pack_git_info(GitDir, Hash, [git(true), installed_size(Bytes)|Info]) :-
1831 exists_directory(GitDir),
1832 !,
1833 git_ls_tree(Entries, [directory(GitDir)]),
1834 git_hash(Hash, [directory(GitDir)]),
1835 maplist(arg(4), Entries, Sizes),
1836 sum_list(Sizes, Bytes),
1837 dir_metadata(GitDir, Info).
1838
1839dir_metadata(GitDir, Info) :-
1840 directory_file_path(GitDir, 'pack.pl', InfoFile),
1841 read_file_to_terms(InfoFile, Info, [encoding(utf8)]),
1842 maplist(valid_term(pack_info_term), Info).
1843
1847
1848download_file_sanity_check(Archive, Pack, Info) :-
1849 info_field(name(PackName), Info),
1850 info_field(version(PackVersion), Info),
1851 pack_version_file(PackFile, FileVersion, Archive),
1852 must_match([Pack, PackName, PackFile], name),
1853 must_match([PackVersion, FileVersion], version).
1854
1855info_field(Field, Info) :-
1856 memberchk(Field, Info),
1857 ground(Field),
1858 !.
1859info_field(Field, _Info) :-
1860 functor(Field, FieldName, _),
1861 print_message(error, pack(missing(FieldName))),
1862 fail.
1863
1864must_match(Values, _Field) :-
1865 sort(Values, [_]),
1866 !.
1867must_match(Values, Field) :-
1868 print_message(error, pack(conflict(Field, Values))),
1869 fail.
1870
1871
1872 1875
1887
1888prepare_pack_dir(Dir, Options) :-
1889 exists_directory(Dir),
1890 !,
1891 ( empty_directory(Dir)
1892 -> true
1893 ; remove_existing_pack(Dir, Options)
1894 -> make_directory(Dir)
1895 ).
1896prepare_pack_dir(Dir, _) :-
1897 ( read_link(Dir, _, _)
1898 ; access_file(Dir, exist)
1899 ),
1900 !,
1901 delete_file(Dir),
1902 make_directory(Dir).
1903prepare_pack_dir(Dir, _) :-
1904 make_directory(Dir).
1905
1909
1910empty_directory(Dir) :-
1911 \+ ( directory_files(Dir, Entries),
1912 member(Entry, Entries),
1913 \+ special(Entry)
1914 ).
1915
1916special(.).
1917special(..).
1918
1925
1926remove_existing_pack(PackDir, Options) :-
1927 exists_directory(PackDir),
1928 !,
1929 ( ( option(upgrade(true), Options)
1930 ; confirm(remove_existing_pack(PackDir), yes, Options)
1931 )
1932 -> delete_directory_and_contents(PackDir)
1933 ; print_message(error, pack(directory_exists(PackDir))),
1934 fail
1935 ).
1936remove_existing_pack(_, _).
1937
1951
1952pack_download_from_url(URL, PackTopDir, Pack, Options) :-
1953 option(git(true), Options),
1954 !,
1955 directory_file_path(PackTopDir, Pack, PackDir),
1956 prepare_pack_dir(PackDir, Options),
1957 ( option(branch(Branch), Options)
1958 -> Extra = ['--branch', Branch]
1959 ; Extra = []
1960 ),
1961 run_process(path(git), [clone, URL, PackDir|Extra], []),
1962 git_checkout_version(PackDir, [update(false)|Options]),
1963 option(pack_dir(PackDir), Options, _).
1964pack_download_from_url(URL0, PackTopDir, Pack, Options) :-
1965 download_url(URL0),
1966 !,
1967 hsts(URL0, URL, Options),
1968 directory_file_path(PackTopDir, Pack, PackDir),
1969 prepare_pack_dir(PackDir, Options),
1970 pack_download_dir(PackTopDir, DownLoadDir),
1971 download_file(URL, Pack, DownloadBase, Options),
1972 directory_file_path(DownLoadDir, DownloadBase, DownloadFile),
1973 ( option(insecure(true), Options, false)
1974 -> TLSOptions = [cert_verify_hook(ssl_verify)]
1975 ; TLSOptions = []
1976 ),
1977 print_message(informational, pack(download(begin, Pack, URL, DownloadFile))),
1978 setup_call_cleanup(
1979 http_open(URL, In, TLSOptions),
1980 setup_call_cleanup(
1981 open(DownloadFile, write, Out, [type(binary)]),
1982 copy_stream_data(In, Out),
1983 close(Out)),
1984 close(In)),
1985 print_message(informational, pack(download(end, Pack, URL, DownloadFile))),
1986 pack_archive_info(DownloadFile, Pack, Info, _),
1987 ( option(git_url(GitURL), Options)
1988 -> Origin = GitURL 1989 ; download_file_sanity_check(DownloadFile, Pack, Info),
1990 Origin = URL
1991 ),
1992 pack_unpack_from_local(DownloadFile, PackTopDir, Pack, PackDir, Options),
1993 pack_assert(PackDir, archive(DownloadFile, Origin)),
1994 option(pack_dir(PackDir), Options, _).
1995pack_download_from_url(URL, PackTopDir, Pack, Options) :-
1996 local_uri_file_name(URL, File),
1997 !,
1998 pack_unpack_from_local(File, PackTopDir, Pack, PackDir, Options),
1999 pack_assert(PackDir, archive(File, URL)),
2000 option(pack_dir(PackDir), Options, _).
2001pack_download_from_url(URL, _PackTopDir, _Pack, _Options) :-
2002 domain_error(url, URL).
2003
2025
2026git_checkout_version(PackDir, Options) :-
2027 option(commit('HEAD'), Options),
2028 option(branch(Branch), Options),
2029 !,
2030 git_ensure_on_branch(PackDir, Branch),
2031 run_process(path(git), ['-C', PackDir, pull], []).
2032git_checkout_version(PackDir, Options) :-
2033 option(commit('HEAD'), Options),
2034 git_current_branch(_, [directory(PackDir)]),
2035 !,
2036 run_process(path(git), ['-C', PackDir, pull], []).
2037git_checkout_version(PackDir, Options) :-
2038 option(commit('HEAD'), Options),
2039 !,
2040 git_default_branch(Branch, [directory(PackDir)]),
2041 git_ensure_on_branch(PackDir, Branch),
2042 run_process(path(git), ['-C', PackDir, pull], []).
2043git_checkout_version(PackDir, Options) :-
2044 option(commit(Hash), Options),
2045 run_process(path(git), ['-C', PackDir, fetch], []),
2046 git_branches(Branches, [contains(Hash), directory(PackDir)]),
2047 git_process_output(['-C', PackDir, 'rev-parse' | Branches],
2048 read_lines_to_atoms(Commits),
2049 []),
2050 nth1(I, Commits, Hash),
2051 nth1(I, Branches, Branch),
2052 !,
2053 git_ensure_on_branch(PackDir, Branch).
2054git_checkout_version(PackDir, Options) :-
2055 option(commit(Hash), Options),
2056 !,
2057 run_process(path(git), ['-C', PackDir, checkout, '--quiet', Hash], []).
2058git_checkout_version(PackDir, Options) :-
2059 option(version(Version), Options),
2060 !,
2061 git_tags(Tags, [directory(PackDir)]),
2062 ( memberchk(Version, Tags)
2063 -> Tag = Version
2064 ; member(Tag, Tags),
2065 sub_atom(Tag, B, _, 0, Version),
2066 sub_atom(Tag, 0, B, _, Prefix),
2067 version_prefix(Prefix)
2068 -> true
2069 ; existence_error(version_tag, Version)
2070 ),
2071 run_process(path(git), ['-C', PackDir, checkout, Tag], []).
2072git_checkout_version(_PackDir, Options) :-
2073 option(fresh(true), Options),
2074 !.
2075git_checkout_version(PackDir, _Options) :-
2076 git_current_branch(_, [directory(PackDir)]),
2077 !,
2078 run_process(path(git), ['-C', PackDir, pull], []).
2079git_checkout_version(PackDir, _Options) :-
2080 git_default_branch(Branch, [directory(PackDir)]),
2081 git_ensure_on_branch(PackDir, Branch),
2082 run_process(path(git), ['-C', PackDir, pull], []).
2083
2087
2088git_ensure_on_branch(PackDir, Branch) :-
2089 git_current_branch(Branch, [directory(PackDir)]),
2090 !.
2091git_ensure_on_branch(PackDir, Branch) :-
2092 run_process(path(git), ['-C', PackDir, checkout, Branch], []).
2093
2094read_lines_to_atoms(Atoms, In) :-
2095 read_line_to_string(In, Line),
2096 ( Line == end_of_file
2097 -> Atoms = []
2098 ; atom_string(Atom, Line),
2099 Atoms = [Atom|T],
2100 read_lines_to_atoms(T, In)
2101 ).
2102
2103version_prefix(Prefix) :-
2104 atom_codes(Prefix, Codes),
2105 phrase(version_prefix, Codes).
2106
2107version_prefix -->
2108 [C],
2109 { code_type(C, alpha) },
2110 !,
2111 version_prefix.
2112version_prefix -->
2113 "-".
2114version_prefix -->
2115 "_".
2116version_prefix -->
2117 "".
2118
2123
2124download_file(URL, Pack, File, Options) :-
2125 option(version(Version), Options),
2126 !,
2127 file_name_extension(_, Ext, URL),
2128 format(atom(File), '~w-~w.~w', [Pack, Version, Ext]).
2129download_file(URL, Pack, File, _) :-
2130 file_base_name(URL,Basename),
2131 no_int_file_name_extension(Tag,Ext,Basename),
2132 tag_version(Tag,Version),
2133 !,
2134 format(atom(File0), '~w-~w', [Pack, Version]),
2135 file_name_extension(File0, Ext, File).
2136download_file(URL, _, File, _) :-
2137 file_base_name(URL, File).
2138
2144
2145:- public pack_url_file/2. 2146pack_url_file(URL, FileID) :-
2147 github_release_url(URL, Pack, Version),
2148 !,
2149 download_file(URL, Pack, FileID, [version(Version)]).
2150pack_url_file(URL, FileID) :-
2151 file_base_name(URL, FileID).
2152
2157
2158:- public ssl_verify/5. 2159ssl_verify(_SSL,
2160 _ProblemCertificate, _AllCertificates, _FirstCertificate,
2161 _Error).
2162
2163pack_download_dir(PackTopDir, DownLoadDir) :-
2164 directory_file_path(PackTopDir, 'Downloads', DownLoadDir),
2165 ( exists_directory(DownLoadDir)
2166 -> true
2167 ; make_directory(DownLoadDir)
2168 ),
2169 ( access_file(DownLoadDir, write)
2170 -> true
2171 ; permission_error(write, directory, DownLoadDir)
2172 ).
2173
2179
2180download_url(URL) :-
2181 url_scheme(URL, Scheme),
2182 download_scheme(Scheme).
2183
2184url_scheme(URL, Scheme) :-
2185 atom(URL),
2186 uri_components(URL, Components),
2187 uri_data(scheme, Components, Scheme0),
2188 atom(Scheme0),
2189 Scheme = Scheme0.
2190
2191download_scheme(http).
2192download_scheme(https).
2193
2202
2203hsts(URL0, URL, Options) :-
2204 option(insecure(true), Options, false),
2205 !,
2206 URL = URL0.
2207hsts(URL0, URL, _Options) :-
2208 url_scheme(URL0, http),
2209 !,
2210 uri_edit(scheme(https), URL0, URL).
2211hsts(URL, URL, _Options).
2212
2213
2221
2222pack_post_install(Info, Options) :-
2223 Pack = Info.pack,
2224 PackDir = Info.installed,
2225 post_install_foreign(Pack, PackDir, Options),
2226 post_install_autoload(Info),
2227 pack_attach(PackDir, [duplicate(warning)]).
2228
2234
2235pack_rebuild :-
2236 forall(current_pack(Pack),
2237 ( print_message(informational, pack(rebuild(Pack))),
2238 pack_rebuild(Pack)
2239 )).
2240
2241pack_rebuild(Pack) :-
2242 current_pack(Pack, PackDir),
2243 !,
2244 post_install_foreign(Pack, PackDir, [rebuild(true)]),
2245 pack_attach(PackDir, [duplicate(replace)]).
2246pack_rebuild(Pack) :-
2247 unattached_pack(Pack, PackDir),
2248 !,
2249 post_install_foreign(Pack, PackDir, [rebuild(true)]),
2250 pack_attach(PackDir, [duplicate(replace)]).
2251pack_rebuild(Pack) :-
2252 existence_error(pack, Pack).
2253
2254unattached_pack(Pack, BaseDir) :-
2255 directory_file_path(Pack, 'pack.pl', PackFile),
2256 absolute_file_name(pack(PackFile), PackPath,
2257 [ access(read),
2258 file_errors(fail)
2259 ]),
2260 file_directory_name(PackPath, BaseDir).
2261
2262
2263
2275
2276post_install_foreign(Pack, PackDir, Options) :-
2277 is_foreign_pack(PackDir, _),
2278 !,
2279 ( pack_info_term(PackDir, pack_version(Version))
2280 -> true
2281 ; Version = 1
2282 ),
2283 option(rebuild(Rebuild), Options, if_absent),
2284 current_prolog_flag(arch, Arch),
2285 prolog_version_dotted(PrologVersion),
2286 ( Rebuild == if_absent,
2287 foreign_present(PackDir, Arch)
2288 -> print_message(informational, pack(kept_foreign(Pack, Arch))),
2289 ( pack_status_dir(PackDir, built(Arch, _, _))
2290 -> true
2291 ; pack_assert(PackDir, built(Arch, PrologVersion, downloaded))
2292 )
2293 ; BuildSteps0 = [[dependencies], [configure], build, install, [test]],
2294 ( Rebuild == true
2295 -> BuildSteps1 = [distclean|BuildSteps0]
2296 ; BuildSteps1 = BuildSteps0
2297 ),
2298 ( option(test(false), Options)
2299 -> delete(BuildSteps1, [test], BuildSteps2)
2300 ; BuildSteps2 = BuildSteps1
2301 ),
2302 ( option(clean(true), Options)
2303 -> append(BuildSteps2, [[clean]], BuildSteps)
2304 ; BuildSteps = BuildSteps2
2305 ),
2306 build_steps(BuildSteps, PackDir, [pack_version(Version)|Options]),
2307 pack_assert(PackDir, built(Arch, PrologVersion, built))
2308 ).
2309post_install_foreign(_, _, _).
2310
2311
2319
2320foreign_present(PackDir, Arch) :-
2321 atomic_list_concat([PackDir, '/lib'], ForeignBaseDir),
2322 exists_directory(ForeignBaseDir),
2323 !,
2324 atomic_list_concat([PackDir, '/lib/', Arch], ForeignDir),
2325 exists_directory(ForeignDir),
2326 current_prolog_flag(shared_object_extension, Ext),
2327 atomic_list_concat([ForeignDir, '/*.', Ext], Pattern),
2328 expand_file_name(Pattern, Files),
2329 Files \== [].
2330
2335
2336is_foreign_pack(PackDir, Type) :-
2337 foreign_file(File, Type),
2338 directory_file_path(PackDir, File, Path),
2339 exists_file(Path).
2340
2341foreign_file('CMakeLists.txt', cmake).
2342foreign_file('configure', configure).
2343foreign_file('configure.in', autoconf).
2344foreign_file('configure.ac', autoconf).
2345foreign_file('Makefile.am', automake).
2346foreign_file('Makefile', make).
2347foreign_file('makefile', make).
2348foreign_file('conanfile.txt', conan).
2349foreign_file('conanfile.py', conan).
2350
2351
2352 2355
2359
2360post_install_autoload(List), is_list(List) =>
2361 maplist(post_install_autoload, List).
2362post_install_autoload(Info),
2363 _{installed:PackDir, autoload:true} :< Info =>
2364 directory_file_path(PackDir, prolog, PrologLibDir),
2365 make_library_index(PrologLibDir).
2366post_install_autoload(Info) =>
2367 directory_file_path(Info.installed, 'prolog/INDEX.pl', IndexFile),
2368 ( exists_file(IndexFile)
2369 -> E = error(_,_),
2370 print_message(warning, pack(delete_autoload_index(Info.pack, IndexFile))),
2371 catch(delete_file(IndexFile), E,
2372 print_message(warning, E))
2373 ; true
2374 ).
2375
2380
2381decide_autoload_pack(Options, Info0, Info) :-
2382 is_autoload_pack(Info0.pack, Info0.installed, Options),
2383 !,
2384 Info = Info0.put(autoload, true).
2385decide_autoload_pack(_, Info, Info).
2386
2387is_autoload_pack(_Pack, _PackDir, Options) :-
2388 option(autoload(true), Options),
2389 !.
2390is_autoload_pack(Pack, PackDir, Options) :-
2391 pack_info_term(PackDir, autoload(true)),
2392 confirm(autoload(Pack), no, Options).
2393
2394
2395 2398
2402
2403pack_upgrade(Pack) :-
2404 pack_install(Pack, [upgrade(true)]).
2405
2406
2407 2410
2421
2422pack_remove(Pack) :-
2423 pack_remove(Pack, []).
2424
2425pack_remove(Pack, Options) :-
2426 option(dependencies(false), Options),
2427 !,
2428 pack_remove_forced(Pack).
2429pack_remove(Pack, Options) :-
2430 ( dependents(Pack, Deps)
2431 -> ( option(dependencies(true), Options)
2432 -> true
2433 ; confirm_remove(Pack, Deps, Delete, Options)
2434 ),
2435 forall(member(P, Delete), pack_remove_forced(P))
2436 ; pack_remove_forced(Pack)
2437 ).
2438
2439pack_remove_forced(Pack) :-
2440 catch('$pack_detach'(Pack, BaseDir),
2441 error(existence_error(pack, Pack), _),
2442 fail),
2443 !,
2444 ( read_link(BaseDir, _, Target)
2445 -> What = link(Target)
2446 ; What = directory
2447 ),
2448 print_message(informational, pack(remove(What, BaseDir))),
2449 delete_directory_and_contents(BaseDir).
2450pack_remove_forced(Pack) :-
2451 unattached_pack(Pack, BaseDir),
2452 !,
2453 delete_directory_and_contents(BaseDir).
2454pack_remove_forced(Pack) :-
2455 print_message(informational, error(existence_error(pack, Pack),_)).
2456
2457confirm_remove(Pack, Deps, Delete, Options) :-
2458 print_message(warning, pack(depends(Pack, Deps))),
2459 menu(pack(resolve_remove),
2460 [ [Pack] = remove_only(Pack),
2461 [Pack|Deps] = remove_deps(Pack, Deps),
2462 [] = cancel
2463 ], [], Delete, Options),
2464 Delete \== [].
2465
2466
2467 2470
2521
2522pack_publish(Dir, Options) :-
2523 \+ download_url(Dir),
2524 is_git_directory(Dir), !,
2525 pack_git_info(Dir, _Hash, Metadata),
2526 prepare_repository(Dir, Metadata, Options),
2527 ( memberchk(download(URL), Metadata),
2528 git_url(URL, _, Options)
2529 -> true
2530 ; option(remote(Remote), Options, origin),
2531 git_remote_url(Remote, RemoteURL, [directory(Dir)]),
2532 git_to_https_url(RemoteURL, URL)
2533 ),
2534 memberchk(version(Version), Metadata),
2535 memberchk(name(Pack), Metadata),
2536 pack_publish_(URL,
2537 [ version(Version),
2538 pack(Pack)
2539 | Options
2540 ]).
2541pack_publish(Spec, Options) :-
2542 pack_publish_(Spec, Options).
2543
2544pack_publish_(Spec, Options) :-
2545 pack_default_options(Spec, Pack, Options, DefOptions),
2546 option(url(URL), DefOptions),
2547 valid_publish_url(URL, Options),
2548 prepare_build_location(Pack, Dir, Clean, Options),
2549 ( option(register(false), Options)
2550 -> InstallOptions = DefOptions
2551 ; InstallOptions = [publish(Pack)|DefOptions]
2552 ),
2553 call_cleanup(pack_install(Pack,
2554 [ pack(Pack)
2555 | InstallOptions
2556 ]),
2557 cleanup_publish(Clean, Dir)).
2558
2559cleanup_publish(true, Dir) :-
2560 !,
2561 delete_directory_and_contents(Dir).
2562cleanup_publish(_, _).
2563
2564valid_publish_url(URL, Options) :-
2565 option(register(Register), Options, true),
2566 ( Register == false
2567 -> true
2568 ; download_url(URL)
2569 -> true
2570 ; permission_error(publish, pack, URL)
2571 ).
2572
2573prepare_build_location(Pack, Dir, Clean, Options) :-
2574 ( option(pack_directory(Dir), Options)
2575 -> ensure_directory(Dir),
2576 ( option(clean(true), Options, true)
2577 -> delete_directory_contents(Dir)
2578 ; true
2579 )
2580 ; tmp_file(pack, Dir),
2581 make_directory(Dir),
2582 Clean = true
2583 ),
2584 ( option(isolated(false), Options)
2585 -> detach_pack(Pack, _),
2586 attach_packs(Dir, [search(first)])
2587 ; attach_packs(Dir, [replace(true)])
2588 ).
2589
2590
2591
2598
2599prepare_repository(_Dir, _Metadata, Options) :-
2600 option(register(false), Options),
2601 !.
2602prepare_repository(Dir, Metadata, Options) :-
2603 git_dir_must_be_clean(Dir),
2604 git_must_be_on_default_branch(Dir, Options),
2605 tag_git_dir(Dir, Metadata, Action, Options),
2606 confirm(git_push, yes, Options),
2607 run_process(path(git), ['-C', file(Dir), push ], []),
2608 ( Action = push_tag(Tag)
2609 -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], [])
2610 ; true
2611 ).
2612
2613git_dir_must_be_clean(Dir) :-
2614 git_describe(Description, [directory(Dir)]),
2615 ( sub_atom(Description, _, _, 0, '-DIRTY')
2616 -> print_message(error, pack(git_not_clean(Dir))),
2617 fail
2618 ; true
2619 ).
2620
2621git_must_be_on_default_branch(Dir, Options) :-
2622 ( option(branch(Default), Options)
2623 -> true
2624 ; git_default_branch(Default, [directory(Dir)])
2625 ),
2626 git_current_branch(Current, [directory(Dir)]),
2627 ( Default == Current
2628 -> true
2629 ; print_message(error,
2630 pack(git_branch_not_default(Dir, Default, Current))),
2631 fail
2632 ).
2633
2634
2640
2641tag_git_dir(Dir, Metadata, Action, Options) :-
2642 memberchk(version(Version), Metadata),
2643 atom_concat('V', Version, Tag),
2644 git_tags(Tags, [directory(Dir)]),
2645 ( memberchk(Tag, Tags)
2646 -> git_tag_is_consistent(Dir, Tag, Action, Options)
2647 ; format(string(Message), 'Release ~w', [Version]),
2648 findall(Opt, git_tag_option(Opt, Options), Argv,
2649 [ '-m', Message, Tag ]),
2650 confirm(git_tag(Tag), yes, Options),
2651 run_process(path(git), ['-C', file(Dir), tag | Argv ], []),
2652 Action = push_tag(Tag)
2653 ).
2654
2655git_tag_option('-s', Options) :- option(sign(true), Options, true).
2656git_tag_option('-f', Options) :- option(force(true), Options, true).
2657
2658git_tag_is_consistent(Dir, Tag, Action, Options) :-
2659 format(atom(TagRef), 'refs/tags/~w', [Tag]),
2660 format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]),
2661 option(remote(Remote), Options, origin),
2662 git_ls_remote(Dir, LocalTags, [tags(true)]),
2663 memberchk(CommitHash-CommitRef, LocalTags),
2664 ( git_hash(CommitHash, [directory(Dir)])
2665 -> true
2666 ; print_message(error, pack(git_release_tag_not_at_head(Tag))),
2667 fail
2668 ),
2669 memberchk(TagHash-TagRef, LocalTags),
2670 git_ls_remote(Remote, RemoteTags, [tags(true)]),
2671 ( memberchk(RemoteCommitHash-CommitRef, RemoteTags),
2672 memberchk(RemoteTagHash-TagRef, RemoteTags)
2673 -> ( RemoteCommitHash == CommitHash,
2674 RemoteTagHash == TagHash
2675 -> Action = none
2676 ; print_message(error, pack(git_tag_out_of_sync(Tag))),
2677 fail
2678 )
2679 ; Action = push_tag(Tag)
2680 ).
2681
2687
2688git_to_https_url(URL, URL) :-
2689 download_url(URL),
2690 !.
2691git_to_https_url(GitURL, URL) :-
2692 atom_concat('git@github.com:', Repo, GitURL),
2693 !,
2694 atom_concat('https://github.com/', Repo, URL).
2695git_to_https_url(GitURL, _) :-
2696 print_message(error, pack(git_no_https(GitURL))),
2697 fail.
2698
2699
2700 2703
2724
2725pack_property(Pack, Property) :-
2726 findall(Pack-Property, pack_property_(Pack, Property), List),
2727 member(Pack-Property, List). 2728
2729pack_property_(Pack, Property) :-
2730 pack_info(Pack, _, Property).
2731pack_property_(Pack, Property) :-
2732 \+ \+ info_file(Property, _),
2733 '$pack':pack(Pack, BaseDir),
2734 access_file(BaseDir, read),
2735 directory_files(BaseDir, Files),
2736 member(File, Files),
2737 info_file(Property, Pattern),
2738 downcase_atom(File, Pattern),
2739 directory_file_path(BaseDir, File, InfoFile),
2740 arg(1, Property, InfoFile).
2741
2742info_file(readme(_), 'readme.txt').
2743info_file(readme(_), 'readme').
2744info_file(todo(_), 'todo.txt').
2745info_file(todo(_), 'todo').
2746
2747
2748 2751
2758
2759pack_version_file(Pack, Version, GitHubRelease) :-
2760 atomic(GitHubRelease),
2761 github_release_url(GitHubRelease, Pack, Version),
2762 !.
2763pack_version_file(Pack, Version, Path) :-
2764 atomic(Path),
2765 file_base_name(Path, File),
2766 no_int_file_name_extension(Base, _Ext, File),
2767 atom_codes(Base, Codes),
2768 ( phrase(pack_version(Pack, Version), Codes),
2769 safe_pack_name(Pack)
2770 -> true
2771 ).
2772
2773no_int_file_name_extension(Base, Ext, File) :-
2774 file_name_extension(Base0, Ext0, File),
2775 \+ atom_number(Ext0, _),
2776 !,
2777 Base = Base0,
2778 Ext = Ext0.
2779no_int_file_name_extension(File, '', File).
2780
2785
2786safe_pack_name(Name) :-
2787 atom_length(Name, Len),
2788 Len >= 3, 2789 atom_codes(Name, Codes),
2790 maplist(safe_pack_char, Codes),
2791 !.
2792
2793safe_pack_char(C) :- between(0'a, 0'z, C), !.
2794safe_pack_char(C) :- between(0'A, 0'Z, C), !.
2795safe_pack_char(C) :- between(0'0, 0'9, C), !.
2796safe_pack_char(0'_).
2797
2805
2806consistent_pack_name(Pack, Pack) :-
2807 !.
2808consistent_pack_name(Pack, Repo) :-
2809 downcase_atom(Pack, Lwr),
2810 downcase_atom(Repo, Lwr),
2811 !.
2812consistent_pack_name(Pack, Repo) :-
2813 format(string(Msg), "Pack name must match repository name (~w)", [Repo]),
2814 throw(error(permission_error(Pack, name, pack),
2815 context(_, Msg))).
2816
2820
2821pack_version(Pack, Version) -->
2822 string(Codes), "-",
2823 version(Parts),
2824 !,
2825 { atom_codes(Pack, Codes),
2826 atomic_list_concat(Parts, '.', Version)
2827 }.
2828
2829version([H|T]) -->
2830 version_part(H),
2831 ( "."
2832 -> version(T)
2833 ; {T=[]}
2834 ).
2835
2836version_part(*) --> "*", !.
2837version_part(Int) --> integer(Int).
2838
2839
2840 2843
2853
2854have_git :-
2855 process_which(path(git), GIT),
2856 is_sane_git(GIT).
2857
2858:- if(current_prolog_flag(apple, true)). 2859sane_xcode_path -->
2860 "Xcode.app/Contents".
2861sane_xcode_path -->
2862 "CommandLineTools".
2863
2864is_sane_git('/usr/bin/git') :-
2865 !,
2866 process_which(path('xcode-select'), XSpath),
2867 catch(run_process(XSpath,['-p'],[output(Output),error(_)]), error(_,_), fail),
2868 once(phrase((string(_), sane_xcode_path), Output, _)).
2869:- endif. 2870is_sane_git(_).
2871
2875
2876git_url(URL, Pack, Options) :-
2877 uri_components(URL, Components),
2878 uri_data(scheme, Components, Scheme),
2879 nonvar(Scheme), 2880 uri_data(path, Components, Path),
2881 ( Scheme == git
2882 -> true
2883 ; git_download_scheme(Scheme),
2884 file_name_extension(_, git, Path)
2885 ; git_download_scheme(Scheme),
2886 catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail)
2887 -> true
2888 ),
2889 file_base_name(Path, PackExt),
2890 ( file_name_extension(PackFromRepo, git, PackExt)
2891 -> true
2892 ; PackFromRepo = PackExt
2893 ),
2894 option(pack(Pack), Options, PackFromRepo),
2895 ( safe_pack_name(Pack)
2896 -> consistent_pack_name(Pack, PackFromRepo)
2897 ; domain_error(pack_name, Pack)
2898 ).
2899
2900git_download_scheme(http).
2901git_download_scheme(https).
2902
2909
2910github_release_url(URL, Pack, Version) :-
2911 uri_components(URL, Components),
2912 uri_data(authority, Components, 'github.com'),
2913 uri_data(scheme, Components, Scheme),
2914 download_scheme(Scheme),
2915 uri_data(path, Components, Path),
2916 github_archive_path(Archive,Pack,File),
2917 atomic_list_concat(Archive, /, Path),
2918 file_name_extension(Tag, Ext, File),
2919 github_archive_extension(Ext),
2920 tag_version(Tag, Version),
2921 !.
2922
2923github_archive_path(['',_User,Pack,archive,File],Pack,File).
2924github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File).
2925
2926github_archive_extension(tgz).
2927github_archive_extension(zip).
2928
2933
2934tag_version(Tag, Version) :-
2935 version_tag_prefix(Prefix),
2936 atom_concat(Prefix, Version, Tag),
2937 is_version(Version).
2938
2939version_tag_prefix(v).
2940version_tag_prefix('V').
2941version_tag_prefix('').
2942
2943
2949
2950git_archive_url(URL, Archive, Options) :-
2951 uri_components(URL, Components),
2952 uri_data(authority, Components, 'github.com'),
2953 uri_data(path, Components, Path),
2954 atomic_list_concat(['', User, RepoGit], /, Path),
2955 $,
2956 remove_git_ext(RepoGit, Repo),
2957 git_archive_version(Version, Options),
2958 atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath),
2959 uri_edit([ path(ArchivePath),
2960 host('codeload.github.com')
2961 ],
2962 URL, Archive).
2963git_archive_url(URL, _, _) :-
2964 print_message(error, pack(no_git(URL))),
2965 fail.
2966
2967remove_git_ext(RepoGit, Repo) :-
2968 file_name_extension(Repo, git, RepoGit),
2969 !.
2970remove_git_ext(Repo, Repo).
2971
2972git_archive_version(Version, Options) :-
2973 option(commit(Version), Options),
2974 !.
2975git_archive_version(Version, Options) :-
2976 option(branch(Version), Options),
2977 !.
2978git_archive_version(Version, Options) :-
2979 option(version(Version), Options),
2980 !.
2981git_archive_version('HEAD', _).
2982
2983 2986
2999
3000register_downloads(_, Options) :-
3001 option(register(false), Options),
3002 !.
3003register_downloads(_, Options) :-
3004 option(publish(_), Options),
3005 !.
3006register_downloads(Infos, Options) :-
3007 convlist(download_data, Infos, Data),
3008 ( Data == []
3009 -> true
3010 ; query_pack_server(downloaded(Data), Reply, Options),
3011 ( option(do_publish(Pack), Options)
3012 -> ( member(Info, Infos),
3013 Info.pack == Pack
3014 -> true
3015 ),
3016 ( Reply = true(Actions),
3017 memberchk(Pack-Result, Actions)
3018 -> ( registered(Result)
3019 -> print_message(informational, pack(published(Info, Result)))
3020 ; print_message(error, pack(publish_failed(Info, Result))),
3021 fail
3022 )
3023 ; print_message(error, pack(publish_failed(Info, false)))
3024 )
3025 ; true
3026 )
3027 ).
3028
3029registered(git(_URL)).
3030registered(file(_URL)).
3031
3032publish_download(Infos, Options) :-
3033 select_option(publish(Pack), Options, Options1),
3034 !,
3035 register_downloads(Infos, [do_publish(Pack)|Options1]).
3036publish_download(_Infos, _Options).
3037
3048
3049download_data(Info, Data),
3050 Info.get(git) == true => 3051 Data = download(URL, Hash, Metadata),
3052 URL = Info.get(downloaded),
3053 pack_git_info(Info.installed, Hash, Metadata).
3054download_data(Info, Data),
3055 _{git_url:URL,hash:Hash} :< Info, Hash \== (-) =>
3056 Data = download(URL, Hash, Metadata), 3057 dir_metadata(Info.installed, Metadata).
3058download_data(Info, Data) => 3059 Data = download(URL, Hash, Metadata),
3060 URL = Info.get(downloaded),
3061 download_url(URL),
3062 pack_status_dir(Info.installed, archive(Archive, URL)),
3063 file_sha1(Archive, Hash),
3064 pack_archive_info(Archive, _Pack, Metadata, _).
3065
3070
3071query_pack_server(Query, Result, Options) :-
3072 ( option(server(ServerOpt), Options)
3073 -> server_url(ServerOpt, ServerBase)
3074 ; setting(server, ServerBase),
3075 ServerBase \== ''
3076 ),
3077 atom_concat(ServerBase, query, Server),
3078 format(codes(Data), '~q.~n', Query),
3079 info_level(Informational, Options),
3080 print_message(Informational, pack(contacting_server(Server))),
3081 setup_call_cleanup(
3082 http_open(Server, In,
3083 [ post(codes(application/'x-prolog', Data)),
3084 header(content_type, ContentType)
3085 ]),
3086 read_reply(ContentType, In, Result),
3087 close(In)),
3088 message_severity(Result, Level, Informational),
3089 print_message(Level, pack(server_reply(Result))).
3090
3091server_url(URL0, URL) :-
3092 uri_components(URL0, Components),
3093 uri_data(scheme, Components, Scheme),
3094 var(Scheme),
3095 !,
3096 atom_concat('https://', URL0, URL1),
3097 server_url(URL1, URL).
3098server_url(URL0, URL) :-
3099 uri_components(URL0, Components),
3100 uri_data(path, Components, ''),
3101 !,
3102 uri_edit([path('/pack/')], URL0, URL).
3103server_url(URL, URL).
3104
3105read_reply(ContentType, In, Result) :-
3106 sub_atom(ContentType, 0, _, _, 'application/x-prolog'),
3107 !,
3108 set_stream(In, encoding(utf8)),
3109 read(In, Result).
3110read_reply(ContentType, In, _Result) :-
3111 read_string(In, 500, String),
3112 print_message(error, pack(no_prolog_response(ContentType, String))),
3113 fail.
3114
3115info_level(Level, Options) :-
3116 option(silent(true), Options),
3117 !,
3118 Level = silent.
3119info_level(informational, _).
3120
3121message_severity(true(_), Informational, Informational).
3122message_severity(false, warning, _).
3123message_severity(exception(_), error, _).
3124
3125
3126 3129
3136
3137available_download_versions(URL, Versions, _Options) :-
3138 wildcard_pattern(URL),
3139 github_url(URL, User, Repo), 3140 !,
3141 findall(Version-VersionURL,
3142 github_version(User, Repo, Version, VersionURL),
3143 Versions).
3144available_download_versions(URL0, Versions, Options) :-
3145 wildcard_pattern(URL0),
3146 !,
3147 hsts(URL0, URL, Options),
3148 file_directory_name(URL, DirURL0),
3149 ensure_slash(DirURL0, DirURL),
3150 print_message(informational, pack(query_versions(DirURL))),
3151 setup_call_cleanup(
3152 http_open(DirURL, In, []),
3153 load_html(stream(In), DOM,
3154 [ syntax_errors(quiet)
3155 ]),
3156 close(In)),
3157 findall(MatchingURL,
3158 absolute_matching_href(DOM, URL, MatchingURL),
3159 MatchingURLs),
3160 ( MatchingURLs == []
3161 -> print_message(warning, pack(no_matching_urls(URL)))
3162 ; true
3163 ),
3164 versioned_urls(MatchingURLs, VersionedURLs),
3165 sort_version_pairs(VersionedURLs, Versions),
3166 print_message(informational, pack(found_versions(Versions))).
3167available_download_versions(URL, [Version-URL], _Options) :-
3168 ( pack_version_file(_Pack, Version0, URL)
3169 -> Version = Version0
3170 ; Version = '0.0.0'
3171 ).
3172
3176
3177sort_version_pairs(Pairs, Sorted) :-
3178 map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed),
3179 sort(1, @>=, Keyed, SortedKeyed),
3180 pairs_values(SortedKeyed, Sorted).
3181
3182version_pair_sort_key_(Version-_Data, Key) :-
3183 version_sort_key(Version, Key).
3184
3185version_sort_key(Version, Key) :-
3186 split_string(Version, ".", "", Parts),
3187 maplist(number_string, Key, Parts),
3188 !.
3189version_sort_key(Version, _) :-
3190 domain_error(version, Version).
3191
3195
3196github_url(URL, User, Repo) :-
3197 uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3198 atomic_list_concat(['',User,Repo|_], /, Path).
3199
3200
3205
3206github_version(User, Repo, Version, VersionURI) :-
3207 atomic_list_concat(['',repos,User,Repo,tags], /, Path1),
3208 uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)),
3209 setup_call_cleanup(
3210 http_open(ApiUri, In,
3211 [ request_header('Accept'='application/vnd.github.v3+json')
3212 ]),
3213 json_read_dict(In, Dicts),
3214 close(In)),
3215 member(Dict, Dicts),
3216 atom_string(Tag, Dict.name),
3217 tag_version(Tag, Version),
3218 atom_string(VersionURI, Dict.zipball_url).
3219
3220wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *).
3221wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?).
3222
3223ensure_slash(Dir, DirS) :-
3224 ( sub_atom(Dir, _, _, 0, /)
3225 -> DirS = Dir
3226 ; atom_concat(Dir, /, DirS)
3227 ).
3228
3229remove_slash(Dir0, Dir) :-
3230 Dir0 \== '/',
3231 atom_concat(Dir1, /, Dir0),
3232 !,
3233 remove_slash(Dir1, Dir).
3234remove_slash(Dir, Dir).
3235
3236absolute_matching_href(DOM, Pattern, Match) :-
3237 xpath(DOM, //a(@href), HREF),
3238 uri_normalized(HREF, Pattern, Match),
3239 wildcard_match(Pattern, Match).
3240
3241versioned_urls([], []).
3242versioned_urls([H|T0], List) :-
3243 file_base_name(H, File),
3244 ( pack_version_file(_Pack, Version, File)
3245 -> List = [Version-H|T]
3246 ; List = T
3247 ),
3248 versioned_urls(T0, T).
3249
3250
3251 3254
3260
3261pack_provides(Pack, Pack@Version) :-
3262 current_pack(Pack),
3263 once(pack_info(Pack, version, version(Version))).
3264pack_provides(Pack, Provides) :-
3265 findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList),
3266 member(Provides, PrvList).
3267
3268pack_requires(Pack, Requires) :-
3269 current_pack(Pack),
3270 findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList),
3271 member(Requires, ReqList).
3272
3273pack_conflicts(Pack, Conflicts) :-
3274 current_pack(Pack),
3275 findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList),
3276 member(Conflicts, CflList).
3277
3282
3283pack_depends_on(Pack, Dependency) :-
3284 ground(Pack),
3285 !,
3286 pack_requires(Pack, Requires),
3287 \+ is_prolog_token(Requires),
3288 pack_provides(Dependency, Provides),
3289 satisfies_req(Provides, Requires).
3290pack_depends_on(Pack, Dependency) :-
3291 ground(Dependency),
3292 !,
3293 pack_provides(Dependency, Provides),
3294 pack_requires(Pack, Requires),
3295 satisfies_req(Provides, Requires).
3296pack_depends_on(Pack, Dependency) :-
3297 current_pack(Pack),
3298 pack_depends_on(Pack, Dependency).
3299
3304
3305dependents(Pack, Deps) :-
3306 setof(Dep, dependent(Pack, Dep, []), Deps).
3307
3308dependent(Pack, Dep, Seen) :-
3309 pack_depends_on(Dep0, Pack),
3310 \+ memberchk(Dep0, Seen),
3311 ( Dep = Dep0
3312 ; dependent(Dep0, Dep, [Dep0|Seen])
3313 ).
3314
3318
3319validate_dependencies :-
3320 setof(Issue, pack_dependency_issue(_, Issue), Issues),
3321 !,
3322 print_message(warning, pack(dependency_issues(Issues))).
3323validate_dependencies.
3324
3334
3335pack_dependency_issue(Pack, Issue) :-
3336 current_pack(Pack),
3337 pack_dependency_issue_(Pack, Issue).
3338
3339pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :-
3340 pack_requires(Pack, Requires),
3341 ( is_prolog_token(Requires)
3342 -> \+ prolog_satisfies(Requires)
3343 ; \+ ( pack_provides(_, Provides),
3344 satisfies_req(Provides, Requires) )
3345 ).
3346pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :-
3347 pack_conflicts(Pack, Conflicts),
3348 ( is_prolog_token(Conflicts)
3349 -> prolog_satisfies(Conflicts)
3350 ; pack_provides(_, Provides),
3351 satisfies_req(Provides, Conflicts)
3352 ).
3353
3354
3355 3358
3372
3373pack_assert(PackDir, Fact) :-
3374 must_be(ground, Fact),
3375 findall(Term, pack_status_dir(PackDir, Term), Facts0),
3376 update_facts(Facts0, Fact, Facts),
3377 OpenOptions = [encoding(utf8), lock(exclusive)],
3378 status_file(PackDir, StatusFile),
3379 ( Facts == Facts0
3380 -> true
3381 ; Facts0 \== [],
3382 append(Facts0, New, Facts)
3383 -> setup_call_cleanup(
3384 open(StatusFile, append, Out, OpenOptions),
3385 maplist(write_fact(Out), New),
3386 close(Out))
3387 ; setup_call_cleanup(
3388 open(StatusFile, write, Out, OpenOptions),
3389 ( write_facts_header(Out),
3390 maplist(write_fact(Out), Facts)
3391 ),
3392 close(Out))
3393 ).
3394
3395update_facts([], Fact, [Fact]) :-
3396 !.
3397update_facts([H|T], Fact, [Fact|T]) :-
3398 general_pack_fact(Fact, GenFact),
3399 general_pack_fact(H, GenTerm),
3400 GenFact =@= GenTerm,
3401 !.
3402update_facts([H|T0], Fact, [H|T]) :-
3403 update_facts(T0, Fact, T).
3404
3405general_pack_fact(built(Arch, _Version, _How), General) =>
3406 General = built(Arch, _, _).
3407general_pack_fact(Term, General), compound(Term) =>
3408 compound_name_arity(Term, Name, Arity),
3409 compound_name_arity(General, Name, Arity).
3410general_pack_fact(Term, General) =>
3411 General = Term.
3412
(Out) :-
3414 format(Out, '% Fact status file. Managed by package manager.~n', []).
3415
3416write_fact(Out, Term) :-
3417 format(Out, '~q.~n', [Term]).
3418
3424
3425pack_status(Pack, Fact) :-
3426 current_pack(Pack, PackDir),
3427 pack_status_dir(PackDir, Fact).
3428
3429pack_status_dir(PackDir, Fact) :-
3430 det_if(ground(Fact), pack_status_(PackDir, Fact)).
3431
3432pack_status_(PackDir, Fact) :-
3433 status_file(PackDir, StatusFile),
3434 catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact),
3435 error(existence_error(source_sink, StatusFile), _),
3436 fail).
3437
3438pack_status_term(built(atom, version, oneof([built,downloaded]))).
3439pack_status_term(automatic(boolean)).
3440pack_status_term(archive(atom, atom)).
3441
3442
3449
3450update_automatic(Info) :-
3451 _ = Info.get(dependency_for),
3452 \+ pack_status(Info.installed, automatic(_)),
3453 !,
3454 pack_assert(Info.installed, automatic(true)).
3455update_automatic(Info) :-
3456 pack_assert(Info.installed, automatic(false)).
3457
3458status_file(PackDir, StatusFile) :-
3459 directory_file_path(PackDir, 'status.db', StatusFile).
3460
3461 3464
3465:- multifile prolog:message//1. 3466
3468
(_Question, _Alternatives, Default, Selection, Options) :-
3470 option(interactive(false), Options),
3471 !,
3472 Selection = Default.
3473menu(Question, Alternatives, Default, Selection, _) :-
3474 length(Alternatives, N),
3475 between(1, 5, _),
3476 print_message(query, Question),
3477 print_menu(Alternatives, Default, 1),
3478 print_message(query, pack(menu(select))),
3479 read_selection(N, Choice),
3480 !,
3481 ( Choice == default
3482 -> Selection = Default
3483 ; nth1(Choice, Alternatives, Selection=_)
3484 -> true
3485 ).
3486
([], _, _).
3488print_menu([Value=Label|T], Default, I) :-
3489 ( Value == Default
3490 -> print_message(query, pack(menu(default_item(I, Label))))
3491 ; print_message(query, pack(menu(item(I, Label))))
3492 ),
3493 I2 is I + 1,
3494 print_menu(T, Default, I2).
3495
3496read_selection(Max, Choice) :-
3497 get_single_char(Code),
3498 ( answered_default(Code)
3499 -> Choice = default
3500 ; code_type(Code, digit(Choice)),
3501 between(1, Max, Choice)
3502 -> true
3503 ; print_message(warning, pack(menu(reply(1,Max)))),
3504 fail
3505 ).
3506
3512
3513confirm(_Question, Default, Options) :-
3514 Default \== none,
3515 option(interactive(false), Options, true),
3516 !,
3517 Default == yes.
3518confirm(Question, Default, _) :-
3519 between(1, 5, _),
3520 print_message(query, pack(confirm(Question, Default))),
3521 read_yes_no(YesNo, Default),
3522 !,
3523 format(user_error, '~N', []),
3524 YesNo == yes.
3525
3526read_yes_no(YesNo, Default) :-
3527 get_single_char(Code),
3528 code_yes_no(Code, Default, YesNo),
3529 !.
3530
3531code_yes_no(0'y, _, yes).
3532code_yes_no(0'Y, _, yes).
3533code_yes_no(0'n, _, no).
3534code_yes_no(0'N, _, no).
3535code_yes_no(_, none, _) :- !, fail.
3536code_yes_no(C, Default, Default) :-
3537 answered_default(C).
3538
3539answered_default(0'\r).
3540answered_default(0'\n).
3541answered_default(0'\s).
3542
3543
3544 3547
3548:- multifile prolog:message//1. 3549
3550prolog:message(pack(Message)) -->
3551 message(Message).
3552
3553:- discontiguous
3554 message//1,
3555 label//1. 3556
3557message(invalid_term(pack_info_term, Term)) -->
3558 [ 'Invalid package meta data: ~q'-[Term] ].
3559message(invalid_term(pack_status_term, Term)) -->
3560 [ 'Invalid package status data: ~q'-[Term] ].
3561message(directory_exists(Dir)) -->
3562 [ 'Package target directory exists and is not empty:', nl,
3563 '\t~q'-[Dir]
3564 ].
3565message(already_installed(pack(Pack, Version))) -->
3566 [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ].
3567message(already_installed(Pack)) -->
3568 [ 'Pack `~w'' is already installed. Package info:'-[Pack] ].
3569message(kept_foreign(Pack, Arch)) -->
3570 [ 'Found foreign libraries for architecture '-[],
3571 ansi(code, '~q', [Arch]), nl,
3572 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]),
3573 ' to rebuild from sources'-[]
3574 ].
3575message(no_pack_installed(Pack)) -->
3576 [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ].
3577message(dependency_issues(Issues)) -->
3578 [ 'The current set of packs has dependency issues:', nl ],
3579 dep_issues(Issues).
3580message(depends(Pack, Deps)) -->
3581 [ 'The following packs depend on `~w\':'-[Pack], nl ],
3582 pack_list(Deps).
3583message(remove(link(To), PackDir)) -->
3584 [ 'Removing ', url(PackDir), nl, ' as link to ', url(To) ].
3585message(remove(directory, PackDir)) -->
3586 [ 'Removing ~q and contents'-[PackDir] ].
3587message(remove_existing_pack(PackDir)) -->
3588 [ 'Remove old installation in ~q'-[PackDir] ].
3589message(delete_autoload_index(Pack, Index)) -->
3590 [ 'Pack ' ], msg_pack(Pack), [ ': deleting autoload index ', url(Index) ].
3591message(download_plan(Plan)) -->
3592 [ ansi(bold, 'Installation plan:', []), nl ],
3593 install_plan(Plan, Actions),
3594 install_label(Actions).
3595message(build_plan(Plan)) -->
3596 [ ansi(bold, 'The following packs have post install scripts:', []), nl ],
3597 msg_build_plan(Plan),
3598 [ nl, ansi(bold, 'Run scripts?', []) ].
3599message(autoload(Pack)) -->
3600 [ 'Pack ' ], msg_pack(Pack),
3601 [ ' prefers to be added as autoload library',
3602 nl, ansi(bold, 'Allow?', [])
3603 ].
3604message(no_meta_data(BaseDir)) -->
3605 [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ].
3606message(search_no_matches(Name)) -->
3607 [ 'Search for "~w", returned no matching packages'-[Name] ].
3608message(rebuild(Pack)) -->
3609 [ 'Checking pack "~w" for rebuild ...'-[Pack] ].
3610message(up_to_date([Pack])) -->
3611 !,
3612 [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ].
3613message(up_to_date(Packs)) -->
3614 [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ].
3615message(installed_can_upgrade(List)) -->
3616 sequence(msg_can_upgrade_target, [nl], List).
3617message(new_dependencies(Deps)) -->
3618 [ 'Found new dependencies after downloading (~p).'-[Deps], nl ].
3619message(query_versions(URL)) -->
3620 [ 'Querying "~w" to find new versions ...'-[URL] ].
3621message(no_matching_urls(URL)) -->
3622 [ 'Could not find any matching URL: ~q'-[URL] ].
3623message(found_versions([Latest-_URL|More])) -->
3624 { length(More, Len) },
3625 [ ' Latest version: ~w (~D older)'-[Latest, Len] ].
3626message(build(Pack, PackDir)) -->
3627 [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ].
3628message(contacting_server(Server)) -->
3629 [ 'Contacting server at ~w ...'-[Server], flush ].
3630message(server_reply(true(_))) -->
3631 [ at_same_line, ' ok'-[] ].
3632message(server_reply(false)) -->
3633 [ at_same_line, ' done'-[] ].
3634message(server_reply(exception(E))) -->
3635 [ 'Server reported the following error:'-[], nl ],
3636 '$messages':translate_message(E).
3637message(cannot_create_dir(Alias)) -->
3638 { findall(PackDir,
3639 absolute_file_name(Alias, PackDir, [solutions(all)]),
3640 PackDirs0),
3641 sort(PackDirs0, PackDirs)
3642 },
3643 [ 'Cannot find a place to create a package directory.'-[],
3644 'Considered:'-[]
3645 ],
3646 candidate_dirs(PackDirs).
3647message(conflict(version, [PackV, FileV])) -->
3648 ['Version mismatch: pack.pl: '-[]], msg_version(PackV),
3649 [', file claims version '-[]], msg_version(FileV).
3650message(conflict(name, [PackInfo, FileInfo])) -->
3651 ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]],
3652 [', file claims ~w: ~p'-[FileInfo]].
3653message(no_prolog_response(ContentType, String)) -->
3654 [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl,
3655 '~s'-[String]
3656 ].
3657message(download(begin, Pack, _URL, _DownloadFile)) -->
3658 [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ].
3659message(download(end, _, _, File)) -->
3660 { size_file(File, Bytes) },
3661 [ at_same_line, '~D bytes'-[Bytes] ].
3662message(no_git(URL)) -->
3663 [ 'Cannot install from git repository ', url(URL), '.', nl,
3664 'Cannot find git program and do not know how to download the code', nl,
3665 'from this git service. Please install git and retry.'
3666 ].
3667message(git_no_https(GitURL)) -->
3668 [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ].
3669message(git_branch_not_default(Dir, Default, Current)) -->
3670 [ 'GIT current branch on ', url(Dir), ' is not default.', nl,
3671 ' Current branch: ', ansi(code, '~w', [Current]),
3672 ' default: ', ansi(code, '~w', [Default])
3673 ].
3674message(git_not_clean(Dir)) -->
3675 [ 'GIT working directory is dirty: ', url(Dir), nl,
3676 'Your repository must be clean before publishing.'
3677 ].
3678message(git_push) -->
3679 [ 'Push release to GIT origin?' ].
3680message(git_tag(Tag)) -->
3681 [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ].
3682message(git_release_tag_not_at_head(Tag)) -->
3683 [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl,
3684 'If you want to update the tag, please run ',
3685 ansi(code, 'git tag -d ~w', [Tag])
3686 ].
3687message(git_tag_out_of_sync(Tag)) -->
3688 [ 'Release tag ', ansi(code, '~w', [Tag]),
3689 ' differs from this tag at the origin'
3690 ].
3691
3692message(published(Info, At)) -->
3693 [ 'Published pack ' ], msg_pack(Info), msg_info_version(Info),
3694 [' to be installed from '],
3695 msg_published_address(At).
3696message(publish_failed(Info, Reason)) -->
3697 [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3698 msg_publish_failed(Reason).
3699
3700msg_publish_failed(throw(error(permission_error(register,
3701 pack(_),_URL),_))) -->
3702 [ ' is already registered with a different URL'].
3703msg_publish_failed(download) -->
3704 [' was already published?'].
3705msg_publish_failed(Status) -->
3706 [ ' failed for unknown reason (~p)'-[Status] ].
3707
3708msg_published_address(git(URL)) -->
3709 msg_url(URL, _).
3710msg_published_address(file(URL)) -->
3711 msg_url(URL, _).
3712
3713candidate_dirs([]) --> [].
3714candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T).
3715 3716message(resolve_remove) -->
3717 [ nl, 'Please select an action:', nl, nl ].
3718message(create_pack_dir) -->
3719 [ nl, 'Create directory for packages', nl ].
3720message(menu(item(I, Label))) -->
3721 [ '~t(~d)~6| '-[I] ],
3722 label(Label).
3723message(menu(default_item(I, Label))) -->
3724 [ '~t(~d)~6| * '-[I] ],
3725 label(Label).
3726message(menu(select)) -->
3727 [ nl, 'Your choice? ', flush ].
3728message(confirm(Question, Default)) -->
3729 message(Question),
3730 confirm_default(Default),
3731 [ flush ].
3732message(menu(reply(Min,Max))) -->
3733 ( { Max =:= Min+1 }
3734 -> [ 'Please enter ~w or ~w'-[Min,Max] ]
3735 ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ]
3736 ).
3737
3738 3739dep_issues(Issues) -->
3740 sequence(dep_issue, [nl], Issues).
3741
3742dep_issue(unsatisfied(Pack, Requires)) -->
3743 [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]].
3744dep_issue(conflicts(Pack, Conflict)) -->
3745 [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3746
3751
3752install_label([link]) -->
3753 !,
3754 [ ansi(bold, 'Activate pack?', []) ].
3755install_label([unpack]) -->
3756 !,
3757 [ ansi(bold, 'Unpack archive?', []) ].
3758install_label(_) -->
3759 [ ansi(bold, 'Download packs?', []) ].
3760
3761
3762install_plan(Plan, Actions) -->
3763 install_plan(Plan, Actions, Sec),
3764 sec_warning(Sec).
3765
3766install_plan([], [], _) -->
3767 [].
3768install_plan([H|T], [AH|AT], Sec) -->
3769 install_step(H, AH, Sec), [nl],
3770 install_plan(T, AT, Sec).
3771
3772install_step(Info, keep, _Sec) -->
3773 { Info.get(keep) == true },
3774 !,
3775 [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3776 msg_can_upgrade(Info).
3777install_step(Info, Action, Sec) -->
3778 { From = Info.get(upgrade),
3779 VFrom = From.version,
3780 VTo = Info.get(version),
3781 ( cmp_versions(>=, VTo, VFrom)
3782 -> Label = ansi(bold, ' Upgrade ', [])
3783 ; Label = ansi(warning, ' Downgrade ', [])
3784 )
3785 },
3786 [ Label ], msg_pack(Info),
3787 [ ' from version ~w to ~w'- [From.version, Info.get(version)] ],
3788 install_from(Info, Action, Sec).
3789install_step(Info, Action, Sec) -->
3790 { _From = Info.get(upgrade) },
3791 [ ' Upgrade ' ], msg_pack(Info),
3792 install_from(Info, Action, Sec).
3793install_step(Info, Action, Sec) -->
3794 { Dep = Info.get(dependency_for) },
3795 [ ' Install ' ], msg_pack(Info),
3796 [ ' at version ~w as dependency for '-[Info.version],
3797 ansi(code, '~w', [Dep])
3798 ],
3799 install_from(Info, Action, Sec),
3800 msg_downloads(Info).
3801install_step(Info, Action, Sec) -->
3802 { Info.get(commit) == 'HEAD' },
3803 !,
3804 [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ],
3805 install_from(Info, Action, Sec),
3806 msg_downloads(Info).
3807install_step(Info, link, _Sec) -->
3808 { Info.get(link) == true,
3809 uri_file_name(Info.get(url), Dir)
3810 },
3811 !,
3812 [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ].
3813install_step(Info, Action, Sec) -->
3814 [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ],
3815 install_from(Info, Action, Sec),
3816 msg_downloads(Info).
3817install_step(Info, Action, Sec) -->
3818 [ ' Install ' ], msg_pack(Info),
3819 install_from(Info, Action, Sec),
3820 msg_downloads(Info).
3821
3822install_from(Info, download, Sec) -->
3823 { download_url(Info.url) },
3824 !,
3825 [ ' from ' ], msg_url(Info.url, Sec).
3826install_from(Info, unpack, Sec) -->
3827 [ ' from ' ], msg_url(Info.url, Sec).
3828
3829msg_url(URL, unsafe) -->
3830 { atomic(URL),
3831 atom_concat('http://', Rest, URL)
3832 },
3833 [ ansi(error, '~w', ['http://']), '~w'-[Rest] ].
3834msg_url(URL, _) -->
3835 [ url(URL) ].
3836
3837sec_warning(Sec) -->
3838 { var(Sec) },
3839 !.
3840sec_warning(unsafe) -->
3841 [ ansi(warning, ' WARNING: The installation plan includes downloads \c
3842 from insecure HTTP servers.', []), nl
3843 ].
3844
3845msg_downloads(Info) -->
3846 { Downloads = Info.get(all_downloads),
3847 Downloads > 0
3848 },
3849 [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ],
3850 !.
3851msg_downloads(_) -->
3852 [].
3853
3854msg_pack(Pack) -->
3855 { atom(Pack) },
3856 !,
3857 [ ansi(code, '~w', [Pack]) ].
3858msg_pack(Info) -->
3859 msg_pack(Info.pack).
3860
3861msg_info_version(Info) -->
3862 [ ansi(code, '@~w', [Info.get(version)]) ],
3863 !.
3864msg_info_version(_Info) -->
3865 [].
3866
3870
3871msg_build_plan(Plan) -->
3872 sequence(build_step, [nl], Plan).
3873
3874build_step(Info) -->
3875 [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ].
3876
3877msg_can_upgrade_target(Info) -->
3878 [ ' Pack ' ], msg_pack(Info),
3879 [ ' is installed at version ~w'-[Info.version] ],
3880 msg_can_upgrade(Info).
3881
3882pack_list([]) --> [].
3883pack_list([H|T]) -->
3884 [ ' - Pack ' ], msg_pack(H), [nl],
3885 pack_list(T).
3886
3887label(remove_only(Pack)) -->
3888 [ 'Only remove package ~w (break dependencies)'-[Pack] ].
3889label(remove_deps(Pack, Deps)) -->
3890 { length(Deps, Count) },
3891 [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ].
3892label(create_dir(Dir)) -->
3893 [ '~w'-[Dir] ].
3894label(install_from(git(URL))) -->
3895 !,
3896 [ 'GIT repository at ~w'-[URL] ].
3897label(install_from(URL)) -->
3898 [ '~w'-[URL] ].
3899label(cancel) -->
3900 [ 'Cancel' ].
3901
3902confirm_default(yes) -->
3903 [ ' Y/n? ' ].
3904confirm_default(no) -->
3905 [ ' y/N? ' ].
3906confirm_default(none) -->
3907 [ ' y/n? ' ].
3908
3909msg_version(Version) -->
3910 [ '~w'-[Version] ].
3911
3912msg_can_upgrade(Info) -->
3913 { Latest = Info.get(latest_version) },
3914 [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ].
3915msg_can_upgrade(_) -->
3916 [].
3917
3918
3919 3922
3923local_uri_file_name(URL, FileName) :-
3924 uri_file_name(URL, FileName),
3925 !.
3926local_uri_file_name(URL, FileName) :-
3927 uri_components(URL, Components),
3928 uri_data(scheme, Components, File), File == file,
3929 uri_data(authority, Components, FileNameEnc),
3930 uri_data(path, Components, ''),
3931 uri_encoded(path, FileName, FileNameEnc).
3932
3933det_if(Cond, Goal) :-
3934 ( Cond
3935 -> Goal,
3936 !
3937 ; Goal
3938 ).
3939
3940member_nonvar(_, Var) :-
3941 var(Var),
3942 !,
3943 fail.
3944member_nonvar(E, [E|_]).
3945member_nonvar(E, [_|T]) :-
3946 member_nonvar(E, T)