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)
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),
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, _)
2529 -> true
2530 ; option(remote(Remote), Options, origin),
2531 git_remote_url(Remote, RemoteURL, [directory(Dir)]),
2532 git_to_https_url(RemoteURL, URL)
2533 ),
2534 memberchk(version(Version), Metadata),
2535 pack_publish_(URL,
2536 [ version(Version)
2537 | Options
2538 ]).
2539pack_publish(Spec, Options) :-
2540 pack_publish_(Spec, Options).
2541
2542pack_publish_(Spec, Options) :-
2543 pack_default_options(Spec, Pack, Options, DefOptions),
2544 option(url(URL), DefOptions),
2545 valid_publish_url(URL, Options),
2546 prepare_build_location(Pack, Dir, Clean, Options),
2547 ( option(register(false), Options)
2548 -> InstallOptions = DefOptions
2549 ; InstallOptions = [publish(Pack)|DefOptions]
2550 ),
2551 call_cleanup(pack_install(Pack,
2552 [ pack(Pack)
2553 | InstallOptions
2554 ]),
2555 cleanup_publish(Clean, Dir)).
2556
2557cleanup_publish(true, Dir) :-
2558 !,
2559 delete_directory_and_contents(Dir).
2560cleanup_publish(_, _).
2561
2562valid_publish_url(URL, Options) :-
2563 option(register(Register), Options, true),
2564 ( Register == false
2565 -> true
2566 ; download_url(URL)
2567 -> true
2568 ; permission_error(publish, pack, URL)
2569 ).
2570
2571prepare_build_location(Pack, Dir, Clean, Options) :-
2572 ( option(pack_directory(Dir), Options)
2573 -> ensure_directory(Dir),
2574 ( option(clean(true), Options, true)
2575 -> delete_directory_contents(Dir)
2576 ; true
2577 )
2578 ; tmp_file(pack, Dir),
2579 make_directory(Dir),
2580 Clean = true
2581 ),
2582 ( option(isolated(false), Options)
2583 -> detach_pack(Pack, _),
2584 attach_packs(Dir, [search(first)])
2585 ; attach_packs(Dir, [replace(true)])
2586 ).
2587
2588
2589
2596
2597prepare_repository(_Dir, _Metadata, Options) :-
2598 option(register(false), Options),
2599 !.
2600prepare_repository(Dir, Metadata, Options) :-
2601 git_dir_must_be_clean(Dir),
2602 git_must_be_on_default_branch(Dir, Options),
2603 tag_git_dir(Dir, Metadata, Action, Options),
2604 confirm(git_push, yes, Options),
2605 run_process(path(git), ['-C', file(Dir), push ], []),
2606 ( Action = push_tag(Tag)
2607 -> run_process(path(git), ['-C', file(Dir), push, origin, Tag ], [])
2608 ; true
2609 ).
2610
2611git_dir_must_be_clean(Dir) :-
2612 git_describe(Description, [directory(Dir)]),
2613 ( sub_atom(Description, _, _, 0, '-DIRTY')
2614 -> print_message(error, pack(git_not_clean(Dir))),
2615 fail
2616 ; true
2617 ).
2618
2619git_must_be_on_default_branch(Dir, Options) :-
2620 ( option(branch(Default), Options)
2621 -> true
2622 ; git_default_branch(Default, [directory(Dir)])
2623 ),
2624 git_current_branch(Current, [directory(Dir)]),
2625 ( Default == Current
2626 -> true
2627 ; print_message(error,
2628 pack(git_branch_not_default(Dir, Default, Current))),
2629 fail
2630 ).
2631
2632
2638
2639tag_git_dir(Dir, Metadata, Action, Options) :-
2640 memberchk(version(Version), Metadata),
2641 atom_concat('V', Version, Tag),
2642 git_tags(Tags, [directory(Dir)]),
2643 ( memberchk(Tag, Tags)
2644 -> git_tag_is_consistent(Dir, Tag, Action, Options)
2645 ; format(string(Message), 'Release ~w', [Version]),
2646 findall(Opt, git_tag_option(Opt, Options), Argv,
2647 [ '-m', Message, Tag ]),
2648 confirm(git_tag(Tag), yes, Options),
2649 run_process(path(git), ['-C', file(Dir), tag | Argv ], []),
2650 Action = push_tag(Tag)
2651 ).
2652
2653git_tag_option('-s', Options) :- option(sign(true), Options, true).
2654git_tag_option('-f', Options) :- option(force(true), Options, true).
2655
2656git_tag_is_consistent(Dir, Tag, Action, Options) :-
2657 format(atom(TagRef), 'refs/tags/~w', [Tag]),
2658 format(atom(CommitRef), 'refs/tags/~w^{}', [Tag]),
2659 option(remote(Remote), Options, origin),
2660 git_ls_remote(Dir, LocalTags, [tags(true)]),
2661 memberchk(CommitHash-CommitRef, LocalTags),
2662 ( git_hash(CommitHash, [directory(Dir)])
2663 -> true
2664 ; print_message(error, pack(git_release_tag_not_at_head(Tag))),
2665 fail
2666 ),
2667 memberchk(TagHash-TagRef, LocalTags),
2668 git_ls_remote(Remote, RemoteTags, [tags(true)]),
2669 ( memberchk(RemoteCommitHash-CommitRef, RemoteTags),
2670 memberchk(RemoteTagHash-TagRef, RemoteTags)
2671 -> ( RemoteCommitHash == CommitHash,
2672 RemoteTagHash == TagHash
2673 -> Action = none
2674 ; print_message(error, pack(git_tag_out_of_sync(Tag))),
2675 fail
2676 )
2677 ; Action = push_tag(Tag)
2678 ).
2679
2685
2686git_to_https_url(URL, URL) :-
2687 download_url(URL),
2688 !.
2689git_to_https_url(GitURL, URL) :-
2690 atom_concat('git@github.com:', Repo, GitURL),
2691 !,
2692 atom_concat('https://github.com/', Repo, URL).
2693git_to_https_url(GitURL, _) :-
2694 print_message(error, pack(git_no_https(GitURL))),
2695 fail.
2696
2697
2698 2701
2722
2723pack_property(Pack, Property) :-
2724 findall(Pack-Property, pack_property_(Pack, Property), List),
2725 member(Pack-Property, List). 2726
2727pack_property_(Pack, Property) :-
2728 pack_info(Pack, _, Property).
2729pack_property_(Pack, Property) :-
2730 \+ \+ info_file(Property, _),
2731 '$pack':pack(Pack, BaseDir),
2732 access_file(BaseDir, read),
2733 directory_files(BaseDir, Files),
2734 member(File, Files),
2735 info_file(Property, Pattern),
2736 downcase_atom(File, Pattern),
2737 directory_file_path(BaseDir, File, InfoFile),
2738 arg(1, Property, InfoFile).
2739
2740info_file(readme(_), 'readme.txt').
2741info_file(readme(_), 'readme').
2742info_file(todo(_), 'todo.txt').
2743info_file(todo(_), 'todo').
2744
2745
2746 2749
2756
2757pack_version_file(Pack, Version, GitHubRelease) :-
2758 atomic(GitHubRelease),
2759 github_release_url(GitHubRelease, Pack, Version),
2760 !.
2761pack_version_file(Pack, Version, Path) :-
2762 atomic(Path),
2763 file_base_name(Path, File),
2764 no_int_file_name_extension(Base, _Ext, File),
2765 atom_codes(Base, Codes),
2766 ( phrase(pack_version(Pack, Version), Codes),
2767 safe_pack_name(Pack)
2768 -> true
2769 ).
2770
2771no_int_file_name_extension(Base, Ext, File) :-
2772 file_name_extension(Base0, Ext0, File),
2773 \+ atom_number(Ext0, _),
2774 !,
2775 Base = Base0,
2776 Ext = Ext0.
2777no_int_file_name_extension(File, '', File).
2778
2783
2784safe_pack_name(Name) :-
2785 atom_length(Name, Len),
2786 Len >= 3, 2787 atom_codes(Name, Codes),
2788 maplist(safe_pack_char, Codes),
2789 !.
2790
2791safe_pack_char(C) :- between(0'a, 0'z, C), !.
2792safe_pack_char(C) :- between(0'A, 0'Z, C), !.
2793safe_pack_char(C) :- between(0'0, 0'9, C), !.
2794safe_pack_char(0'_).
2795
2799
2800pack_version(Pack, Version) -->
2801 string(Codes), "-",
2802 version(Parts),
2803 !,
2804 { atom_codes(Pack, Codes),
2805 atomic_list_concat(Parts, '.', Version)
2806 }.
2807
2808version([H|T]) -->
2809 version_part(H),
2810 ( "."
2811 -> version(T)
2812 ; {T=[]}
2813 ).
2814
2815version_part(*) --> "*", !.
2816version_part(Int) --> integer(Int).
2817
2818
2819 2822
2832
2833have_git :-
2834 process_which(path(git), GIT),
2835 is_sane_git(GIT).
2836
2837:- if(current_prolog_flag(apple, true)). 2838sane_xcode_path -->
2839 "Xcode.app/Contents".
2840sane_xcode_path -->
2841 "CommandLineTools".
2842
2843is_sane_git('/usr/bin/git') :-
2844 !,
2845 process_which(path('xcode-select'), XSpath),
2846 catch(run_process(XSpath,['-p'],[output(Output),error(_)]), error(_,_), fail),
2847 once(phrase((string(_), sane_xcode_path), Output, _)).
2848:- endif. 2849is_sane_git(_).
2850
2854
2855git_url(URL, Pack) :-
2856 uri_components(URL, Components),
2857 uri_data(scheme, Components, Scheme),
2858 nonvar(Scheme), 2859 uri_data(path, Components, Path),
2860 ( Scheme == git
2861 -> true
2862 ; git_download_scheme(Scheme),
2863 file_name_extension(_, git, Path)
2864 ; git_download_scheme(Scheme),
2865 catch(git_ls_remote(URL, _, [refs(['HEAD']), error(_)]), _, fail)
2866 -> true
2867 ),
2868 file_base_name(Path, PackExt),
2869 ( file_name_extension(Pack, git, PackExt)
2870 -> true
2871 ; Pack = PackExt
2872 ),
2873 ( safe_pack_name(Pack)
2874 -> true
2875 ; domain_error(pack_name, Pack)
2876 ).
2877
2878git_download_scheme(http).
2879git_download_scheme(https).
2880
2887
2888github_release_url(URL, Pack, Version) :-
2889 uri_components(URL, Components),
2890 uri_data(authority, Components, 'github.com'),
2891 uri_data(scheme, Components, Scheme),
2892 download_scheme(Scheme),
2893 uri_data(path, Components, Path),
2894 github_archive_path(Archive,Pack,File),
2895 atomic_list_concat(Archive, /, Path),
2896 file_name_extension(Tag, Ext, File),
2897 github_archive_extension(Ext),
2898 tag_version(Tag, Version),
2899 !.
2900
2901github_archive_path(['',_User,Pack,archive,File],Pack,File).
2902github_archive_path(['',_User,Pack,archive,refs,tags,File],Pack,File).
2903
2904github_archive_extension(tgz).
2905github_archive_extension(zip).
2906
2911
2912tag_version(Tag, Version) :-
2913 version_tag_prefix(Prefix),
2914 atom_concat(Prefix, Version, Tag),
2915 is_version(Version).
2916
2917version_tag_prefix(v).
2918version_tag_prefix('V').
2919version_tag_prefix('').
2920
2921
2927
2928git_archive_url(URL, Archive, Options) :-
2929 uri_components(URL, Components),
2930 uri_data(authority, Components, 'github.com'),
2931 uri_data(path, Components, Path),
2932 atomic_list_concat(['', User, RepoGit], /, Path),
2933 $,
2934 remove_git_ext(RepoGit, Repo),
2935 git_archive_version(Version, Options),
2936 atomic_list_concat(['', User, Repo, zip, Version], /, ArchivePath),
2937 uri_edit([ path(ArchivePath),
2938 host('codeload.github.com')
2939 ],
2940 URL, Archive).
2941git_archive_url(URL, _, _) :-
2942 print_message(error, pack(no_git(URL))),
2943 fail.
2944
2945remove_git_ext(RepoGit, Repo) :-
2946 file_name_extension(Repo, git, RepoGit),
2947 !.
2948remove_git_ext(Repo, Repo).
2949
2950git_archive_version(Version, Options) :-
2951 option(commit(Version), Options),
2952 !.
2953git_archive_version(Version, Options) :-
2954 option(branch(Version), Options),
2955 !.
2956git_archive_version(Version, Options) :-
2957 option(version(Version), Options),
2958 !.
2959git_archive_version('HEAD', _).
2960
2961 2964
2977
2978register_downloads(_, Options) :-
2979 option(register(false), Options),
2980 !.
2981register_downloads(_, Options) :-
2982 option(publish(_), Options),
2983 !.
2984register_downloads(Infos, Options) :-
2985 convlist(download_data, Infos, Data),
2986 ( Data == []
2987 -> true
2988 ; query_pack_server(downloaded(Data), Reply, Options),
2989 ( option(do_publish(Pack), Options)
2990 -> ( member(Info, Infos),
2991 Info.pack == Pack
2992 -> true
2993 ),
2994 ( Reply = true(Actions),
2995 memberchk(Pack-Result, Actions)
2996 -> ( registered(Result)
2997 -> print_message(informational, pack(published(Info, Result)))
2998 ; print_message(error, pack(publish_failed(Info, Result))),
2999 fail
3000 )
3001 ; print_message(error, pack(publish_failed(Info, false)))
3002 )
3003 ; true
3004 )
3005 ).
3006
3007registered(git(_URL)).
3008registered(file(_URL)).
3009
3010publish_download(Infos, Options) :-
3011 select_option(publish(Pack), Options, Options1),
3012 !,
3013 register_downloads(Infos, [do_publish(Pack)|Options1]).
3014publish_download(_Infos, _Options).
3015
3026
3027download_data(Info, Data),
3028 Info.get(git) == true => 3029 Data = download(URL, Hash, Metadata),
3030 URL = Info.get(downloaded),
3031 pack_git_info(Info.installed, Hash, Metadata).
3032download_data(Info, Data),
3033 _{git_url:URL,hash:Hash} :< Info, Hash \== (-) =>
3034 Data = download(URL, Hash, Metadata), 3035 dir_metadata(Info.installed, Metadata).
3036download_data(Info, Data) => 3037 Data = download(URL, Hash, Metadata),
3038 URL = Info.get(downloaded),
3039 download_url(URL),
3040 pack_status_dir(Info.installed, archive(Archive, URL)),
3041 file_sha1(Archive, Hash),
3042 pack_archive_info(Archive, _Pack, Metadata, _).
3043
3048
3049query_pack_server(Query, Result, Options) :-
3050 ( option(server(ServerOpt), Options)
3051 -> server_url(ServerOpt, ServerBase)
3052 ; setting(server, ServerBase),
3053 ServerBase \== ''
3054 ),
3055 atom_concat(ServerBase, query, Server),
3056 format(codes(Data), '~q.~n', Query),
3057 info_level(Informational, Options),
3058 print_message(Informational, pack(contacting_server(Server))),
3059 setup_call_cleanup(
3060 http_open(Server, In,
3061 [ post(codes(application/'x-prolog', Data)),
3062 header(content_type, ContentType)
3063 ]),
3064 read_reply(ContentType, In, Result),
3065 close(In)),
3066 message_severity(Result, Level, Informational),
3067 print_message(Level, pack(server_reply(Result))).
3068
3069server_url(URL0, URL) :-
3070 uri_components(URL0, Components),
3071 uri_data(scheme, Components, Scheme),
3072 var(Scheme),
3073 !,
3074 atom_concat('https://', URL0, URL1),
3075 server_url(URL1, URL).
3076server_url(URL0, URL) :-
3077 uri_components(URL0, Components),
3078 uri_data(path, Components, ''),
3079 !,
3080 uri_edit([path('/pack/')], URL0, URL).
3081server_url(URL, URL).
3082
3083read_reply(ContentType, In, Result) :-
3084 sub_atom(ContentType, 0, _, _, 'application/x-prolog'),
3085 !,
3086 set_stream(In, encoding(utf8)),
3087 read(In, Result).
3088read_reply(ContentType, In, _Result) :-
3089 read_string(In, 500, String),
3090 print_message(error, pack(no_prolog_response(ContentType, String))),
3091 fail.
3092
3093info_level(Level, Options) :-
3094 option(silent(true), Options),
3095 !,
3096 Level = silent.
3097info_level(informational, _).
3098
3099message_severity(true(_), Informational, Informational).
3100message_severity(false, warning, _).
3101message_severity(exception(_), error, _).
3102
3103
3104 3107
3114
3115available_download_versions(URL, Versions, _Options) :-
3116 wildcard_pattern(URL),
3117 github_url(URL, User, Repo), 3118 !,
3119 findall(Version-VersionURL,
3120 github_version(User, Repo, Version, VersionURL),
3121 Versions).
3122available_download_versions(URL0, Versions, Options) :-
3123 wildcard_pattern(URL0),
3124 !,
3125 hsts(URL0, URL, Options),
3126 file_directory_name(URL, DirURL0),
3127 ensure_slash(DirURL0, DirURL),
3128 print_message(informational, pack(query_versions(DirURL))),
3129 setup_call_cleanup(
3130 http_open(DirURL, In, []),
3131 load_html(stream(In), DOM,
3132 [ syntax_errors(quiet)
3133 ]),
3134 close(In)),
3135 findall(MatchingURL,
3136 absolute_matching_href(DOM, URL, MatchingURL),
3137 MatchingURLs),
3138 ( MatchingURLs == []
3139 -> print_message(warning, pack(no_matching_urls(URL)))
3140 ; true
3141 ),
3142 versioned_urls(MatchingURLs, VersionedURLs),
3143 sort_version_pairs(VersionedURLs, Versions),
3144 print_message(informational, pack(found_versions(Versions))).
3145available_download_versions(URL, [Version-URL], _Options) :-
3146 ( pack_version_file(_Pack, Version0, URL)
3147 -> Version = Version0
3148 ; Version = '0.0.0'
3149 ).
3150
3154
3155sort_version_pairs(Pairs, Sorted) :-
3156 map_list_to_pairs(version_pair_sort_key_, Pairs, Keyed),
3157 sort(1, @>=, Keyed, SortedKeyed),
3158 pairs_values(SortedKeyed, Sorted).
3159
3160version_pair_sort_key_(Version-_Data, Key) :-
3161 version_sort_key(Version, Key).
3162
3163version_sort_key(Version, Key) :-
3164 split_string(Version, ".", "", Parts),
3165 maplist(number_string, Key, Parts),
3166 !.
3167version_sort_key(Version, _) :-
3168 domain_error(version, Version).
3169
3173
3174github_url(URL, User, Repo) :-
3175 uri_components(URL, uri_components(https,'github.com',Path,_,_)),
3176 atomic_list_concat(['',User,Repo|_], /, Path).
3177
3178
3183
3184github_version(User, Repo, Version, VersionURI) :-
3185 atomic_list_concat(['',repos,User,Repo,tags], /, Path1),
3186 uri_components(ApiUri, uri_components(https,'api.github.com',Path1,_,_)),
3187 setup_call_cleanup(
3188 http_open(ApiUri, In,
3189 [ request_header('Accept'='application/vnd.github.v3+json')
3190 ]),
3191 json_read_dict(In, Dicts),
3192 close(In)),
3193 member(Dict, Dicts),
3194 atom_string(Tag, Dict.name),
3195 tag_version(Tag, Version),
3196 atom_string(VersionURI, Dict.zipball_url).
3197
3198wildcard_pattern(URL) :- sub_atom(URL, _, _, _, *).
3199wildcard_pattern(URL) :- sub_atom(URL, _, _, _, ?).
3200
3201ensure_slash(Dir, DirS) :-
3202 ( sub_atom(Dir, _, _, 0, /)
3203 -> DirS = Dir
3204 ; atom_concat(Dir, /, DirS)
3205 ).
3206
3207remove_slash(Dir0, Dir) :-
3208 Dir0 \== '/',
3209 atom_concat(Dir1, /, Dir0),
3210 !,
3211 remove_slash(Dir1, Dir).
3212remove_slash(Dir, Dir).
3213
3214absolute_matching_href(DOM, Pattern, Match) :-
3215 xpath(DOM, //a(@href), HREF),
3216 uri_normalized(HREF, Pattern, Match),
3217 wildcard_match(Pattern, Match).
3218
3219versioned_urls([], []).
3220versioned_urls([H|T0], List) :-
3221 file_base_name(H, File),
3222 ( pack_version_file(_Pack, Version, File)
3223 -> List = [Version-H|T]
3224 ; List = T
3225 ),
3226 versioned_urls(T0, T).
3227
3228
3229 3232
3238
3239pack_provides(Pack, Pack@Version) :-
3240 current_pack(Pack),
3241 once(pack_info(Pack, version, version(Version))).
3242pack_provides(Pack, Provides) :-
3243 findall(Prv, pack_info(Pack, dependency, provides(Prv)), PrvList),
3244 member(Provides, PrvList).
3245
3246pack_requires(Pack, Requires) :-
3247 current_pack(Pack),
3248 findall(Req, pack_info(Pack, dependency, requires(Req)), ReqList),
3249 member(Requires, ReqList).
3250
3251pack_conflicts(Pack, Conflicts) :-
3252 current_pack(Pack),
3253 findall(Cfl, pack_info(Pack, dependency, conflicts(Cfl)), CflList),
3254 member(Conflicts, CflList).
3255
3260
3261pack_depends_on(Pack, Dependency) :-
3262 ground(Pack),
3263 !,
3264 pack_requires(Pack, Requires),
3265 \+ is_prolog_token(Requires),
3266 pack_provides(Dependency, Provides),
3267 satisfies_req(Provides, Requires).
3268pack_depends_on(Pack, Dependency) :-
3269 ground(Dependency),
3270 !,
3271 pack_provides(Dependency, Provides),
3272 pack_requires(Pack, Requires),
3273 satisfies_req(Provides, Requires).
3274pack_depends_on(Pack, Dependency) :-
3275 current_pack(Pack),
3276 pack_depends_on(Pack, Dependency).
3277
3282
3283dependents(Pack, Deps) :-
3284 setof(Dep, dependent(Pack, Dep, []), Deps).
3285
3286dependent(Pack, Dep, Seen) :-
3287 pack_depends_on(Dep0, Pack),
3288 \+ memberchk(Dep0, Seen),
3289 ( Dep = Dep0
3290 ; dependent(Dep0, Dep, [Dep0|Seen])
3291 ).
3292
3296
3297validate_dependencies :-
3298 setof(Issue, pack_dependency_issue(_, Issue), Issues),
3299 !,
3300 print_message(warning, pack(dependency_issues(Issues))).
3301validate_dependencies.
3302
3312
3313pack_dependency_issue(Pack, Issue) :-
3314 current_pack(Pack),
3315 pack_dependency_issue_(Pack, Issue).
3316
3317pack_dependency_issue_(Pack, unsatisfied(Pack, Requires)) :-
3318 pack_requires(Pack, Requires),
3319 ( is_prolog_token(Requires)
3320 -> \+ prolog_satisfies(Requires)
3321 ; \+ ( pack_provides(_, Provides),
3322 satisfies_req(Provides, Requires) )
3323 ).
3324pack_dependency_issue_(Pack, conflicts(Pack, Conflicts)) :-
3325 pack_conflicts(Pack, Conflicts),
3326 ( is_prolog_token(Conflicts)
3327 -> prolog_satisfies(Conflicts)
3328 ; pack_provides(_, Provides),
3329 satisfies_req(Provides, Conflicts)
3330 ).
3331
3332
3333 3336
3350
3351pack_assert(PackDir, Fact) :-
3352 must_be(ground, Fact),
3353 findall(Term, pack_status_dir(PackDir, Term), Facts0),
3354 update_facts(Facts0, Fact, Facts),
3355 OpenOptions = [encoding(utf8), lock(exclusive)],
3356 status_file(PackDir, StatusFile),
3357 ( Facts == Facts0
3358 -> true
3359 ; Facts0 \== [],
3360 append(Facts0, New, Facts)
3361 -> setup_call_cleanup(
3362 open(StatusFile, append, Out, OpenOptions),
3363 maplist(write_fact(Out), New),
3364 close(Out))
3365 ; setup_call_cleanup(
3366 open(StatusFile, write, Out, OpenOptions),
3367 ( write_facts_header(Out),
3368 maplist(write_fact(Out), Facts)
3369 ),
3370 close(Out))
3371 ).
3372
3373update_facts([], Fact, [Fact]) :-
3374 !.
3375update_facts([H|T], Fact, [Fact|T]) :-
3376 general_pack_fact(Fact, GenFact),
3377 general_pack_fact(H, GenTerm),
3378 GenFact =@= GenTerm,
3379 !.
3380update_facts([H|T0], Fact, [H|T]) :-
3381 update_facts(T0, Fact, T).
3382
3383general_pack_fact(built(Arch, _Version, _How), General) =>
3384 General = built(Arch, _, _).
3385general_pack_fact(Term, General), compound(Term) =>
3386 compound_name_arity(Term, Name, Arity),
3387 compound_name_arity(General, Name, Arity).
3388general_pack_fact(Term, General) =>
3389 General = Term.
3390
(Out) :-
3392 format(Out, '% Fact status file. Managed by package manager.~n', []).
3393
3394write_fact(Out, Term) :-
3395 format(Out, '~q.~n', [Term]).
3396
3402
3403pack_status(Pack, Fact) :-
3404 current_pack(Pack, PackDir),
3405 pack_status_dir(PackDir, Fact).
3406
3407pack_status_dir(PackDir, Fact) :-
3408 det_if(ground(Fact), pack_status_(PackDir, Fact)).
3409
3410pack_status_(PackDir, Fact) :-
3411 status_file(PackDir, StatusFile),
3412 catch(term_in_file(valid_term(pack_status_term), StatusFile, Fact),
3413 error(existence_error(source_sink, StatusFile), _),
3414 fail).
3415
3416pack_status_term(built(atom, version, oneof([built,downloaded]))).
3417pack_status_term(automatic(boolean)).
3418pack_status_term(archive(atom, atom)).
3419
3420
3427
3428update_automatic(Info) :-
3429 _ = Info.get(dependency_for),
3430 \+ pack_status(Info.installed, automatic(_)),
3431 !,
3432 pack_assert(Info.installed, automatic(true)).
3433update_automatic(Info) :-
3434 pack_assert(Info.installed, automatic(false)).
3435
3436status_file(PackDir, StatusFile) :-
3437 directory_file_path(PackDir, 'status.db', StatusFile).
3438
3439 3442
3443:- multifile prolog:message//1. 3444
3446
(_Question, _Alternatives, Default, Selection, Options) :-
3448 option(interactive(false), Options),
3449 !,
3450 Selection = Default.
3451menu(Question, Alternatives, Default, Selection, _) :-
3452 length(Alternatives, N),
3453 between(1, 5, _),
3454 print_message(query, Question),
3455 print_menu(Alternatives, Default, 1),
3456 print_message(query, pack(menu(select))),
3457 read_selection(N, Choice),
3458 !,
3459 ( Choice == default
3460 -> Selection = Default
3461 ; nth1(Choice, Alternatives, Selection=_)
3462 -> true
3463 ).
3464
([], _, _).
3466print_menu([Value=Label|T], Default, I) :-
3467 ( Value == Default
3468 -> print_message(query, pack(menu(default_item(I, Label))))
3469 ; print_message(query, pack(menu(item(I, Label))))
3470 ),
3471 I2 is I + 1,
3472 print_menu(T, Default, I2).
3473
3474read_selection(Max, Choice) :-
3475 get_single_char(Code),
3476 ( answered_default(Code)
3477 -> Choice = default
3478 ; code_type(Code, digit(Choice)),
3479 between(1, Max, Choice)
3480 -> true
3481 ; print_message(warning, pack(menu(reply(1,Max)))),
3482 fail
3483 ).
3484
3490
3491confirm(_Question, Default, Options) :-
3492 Default \== none,
3493 option(interactive(false), Options, true),
3494 !,
3495 Default == yes.
3496confirm(Question, Default, _) :-
3497 between(1, 5, _),
3498 print_message(query, pack(confirm(Question, Default))),
3499 read_yes_no(YesNo, Default),
3500 !,
3501 format(user_error, '~N', []),
3502 YesNo == yes.
3503
3504read_yes_no(YesNo, Default) :-
3505 get_single_char(Code),
3506 code_yes_no(Code, Default, YesNo),
3507 !.
3508
3509code_yes_no(0'y, _, yes).
3510code_yes_no(0'Y, _, yes).
3511code_yes_no(0'n, _, no).
3512code_yes_no(0'N, _, no).
3513code_yes_no(_, none, _) :- !, fail.
3514code_yes_no(C, Default, Default) :-
3515 answered_default(C).
3516
3517answered_default(0'\r).
3518answered_default(0'\n).
3519answered_default(0'\s).
3520
3521
3522 3525
3526:- multifile prolog:message//1. 3527
3528prolog:message(pack(Message)) -->
3529 message(Message).
3530
3531:- discontiguous
3532 message//1,
3533 label//1. 3534
3535message(invalid_term(pack_info_term, Term)) -->
3536 [ 'Invalid package meta data: ~q'-[Term] ].
3537message(invalid_term(pack_status_term, Term)) -->
3538 [ 'Invalid package status data: ~q'-[Term] ].
3539message(directory_exists(Dir)) -->
3540 [ 'Package target directory exists and is not empty:', nl,
3541 '\t~q'-[Dir]
3542 ].
3543message(already_installed(pack(Pack, Version))) -->
3544 [ 'Pack `~w'' is already installed @~w'-[Pack, Version] ].
3545message(already_installed(Pack)) -->
3546 [ 'Pack `~w'' is already installed. Package info:'-[Pack] ].
3547message(kept_foreign(Pack, Arch)) -->
3548 [ 'Found foreign libraries for architecture '-[],
3549 ansi(code, '~q', [Arch]), nl,
3550 'Use ', ansi(code, '?- pack_rebuild(~q).', [Pack]),
3551 ' to rebuild from sources'-[]
3552 ].
3553message(no_pack_installed(Pack)) -->
3554 [ 'No pack ~q installed. Use ?- pack_list(Pattern) to search'-[Pack] ].
3555message(dependency_issues(Issues)) -->
3556 [ 'The current set of packs has dependency issues:', nl ],
3557 dep_issues(Issues).
3558message(depends(Pack, Deps)) -->
3559 [ 'The following packs depend on `~w\':'-[Pack], nl ],
3560 pack_list(Deps).
3561message(remove(link(To), PackDir)) -->
3562 [ 'Removing ', url(PackDir), nl, ' as link to ', url(To) ].
3563message(remove(directory, PackDir)) -->
3564 [ 'Removing ~q and contents'-[PackDir] ].
3565message(remove_existing_pack(PackDir)) -->
3566 [ 'Remove old installation in ~q'-[PackDir] ].
3567message(delete_autoload_index(Pack, Index)) -->
3568 [ 'Pack ' ], msg_pack(Pack), [ ': deleting autoload index ', url(Index) ].
3569message(download_plan(Plan)) -->
3570 [ ansi(bold, 'Installation plan:', []), nl ],
3571 install_plan(Plan, Actions),
3572 install_label(Actions).
3573message(build_plan(Plan)) -->
3574 [ ansi(bold, 'The following packs have post install scripts:', []), nl ],
3575 msg_build_plan(Plan),
3576 [ nl, ansi(bold, 'Run scripts?', []) ].
3577message(autoload(Pack)) -->
3578 [ 'Pack ' ], msg_pack(Pack),
3579 [ ' prefers to be added as autoload library',
3580 nl, ansi(bold, 'Allow?', [])
3581 ].
3582message(no_meta_data(BaseDir)) -->
3583 [ 'Cannot find pack.pl inside directory ~q. Not a package?'-[BaseDir] ].
3584message(search_no_matches(Name)) -->
3585 [ 'Search for "~w", returned no matching packages'-[Name] ].
3586message(rebuild(Pack)) -->
3587 [ 'Checking pack "~w" for rebuild ...'-[Pack] ].
3588message(up_to_date([Pack])) -->
3589 !,
3590 [ 'Pack ' ], msg_pack(Pack), [' is up-to-date' ].
3591message(up_to_date(Packs)) -->
3592 [ 'Packs ' ], sequence(msg_pack, [', '], Packs), [' are up-to-date' ].
3593message(installed_can_upgrade(List)) -->
3594 sequence(msg_can_upgrade_target, [nl], List).
3595message(new_dependencies(Deps)) -->
3596 [ 'Found new dependencies after downloading (~p).'-[Deps], nl ].
3597message(query_versions(URL)) -->
3598 [ 'Querying "~w" to find new versions ...'-[URL] ].
3599message(no_matching_urls(URL)) -->
3600 [ 'Could not find any matching URL: ~q'-[URL] ].
3601message(found_versions([Latest-_URL|More])) -->
3602 { length(More, Len) },
3603 [ ' Latest version: ~w (~D older)'-[Latest, Len] ].
3604message(build(Pack, PackDir)) -->
3605 [ ansi(bold, 'Building pack ~w in directory ~w', [Pack, PackDir]) ].
3606message(contacting_server(Server)) -->
3607 [ 'Contacting server at ~w ...'-[Server], flush ].
3608message(server_reply(true(_))) -->
3609 [ at_same_line, ' ok'-[] ].
3610message(server_reply(false)) -->
3611 [ at_same_line, ' done'-[] ].
3612message(server_reply(exception(E))) -->
3613 [ 'Server reported the following error:'-[], nl ],
3614 '$messages':translate_message(E).
3615message(cannot_create_dir(Alias)) -->
3616 { findall(PackDir,
3617 absolute_file_name(Alias, PackDir, [solutions(all)]),
3618 PackDirs0),
3619 sort(PackDirs0, PackDirs)
3620 },
3621 [ 'Cannot find a place to create a package directory.'-[],
3622 'Considered:'-[]
3623 ],
3624 candidate_dirs(PackDirs).
3625message(conflict(version, [PackV, FileV])) -->
3626 ['Version mismatch: pack.pl: '-[]], msg_version(PackV),
3627 [', file claims version '-[]], msg_version(FileV).
3628message(conflict(name, [PackInfo, FileInfo])) -->
3629 ['Pack ~w mismatch: pack.pl: ~p'-[PackInfo]],
3630 [', file claims ~w: ~p'-[FileInfo]].
3631message(no_prolog_response(ContentType, String)) -->
3632 [ 'Expected Prolog response. Got content of type ~p'-[ContentType], nl,
3633 '~s'-[String]
3634 ].
3635message(download(begin, Pack, _URL, _DownloadFile)) -->
3636 [ 'Downloading ' ], msg_pack(Pack), [ ' ... ', flush ].
3637message(download(end, _, _, File)) -->
3638 { size_file(File, Bytes) },
3639 [ at_same_line, '~D bytes'-[Bytes] ].
3640message(no_git(URL)) -->
3641 [ 'Cannot install from git repository ', url(URL), '.', nl,
3642 'Cannot find git program and do not know how to download the code', nl,
3643 'from this git service. Please install git and retry.'
3644 ].
3645message(git_no_https(GitURL)) -->
3646 [ 'Do not know how to get an HTTP(s) URL for ', url(GitURL) ].
3647message(git_branch_not_default(Dir, Default, Current)) -->
3648 [ 'GIT current branch on ', url(Dir), ' is not default.', nl,
3649 ' Current branch: ', ansi(code, '~w', [Current]),
3650 ' default: ', ansi(code, '~w', [Default])
3651 ].
3652message(git_not_clean(Dir)) -->
3653 [ 'GIT working directory is dirty: ', url(Dir), nl,
3654 'Your repository must be clean before publishing.'
3655 ].
3656message(git_push) -->
3657 [ 'Push release to GIT origin?' ].
3658message(git_tag(Tag)) -->
3659 [ 'Tag repository with release tag ', ansi(code, '~w', [Tag]) ].
3660message(git_release_tag_not_at_head(Tag)) -->
3661 [ 'Release tag ', ansi(code, '~w', [Tag]), ' is not at HEAD.', nl,
3662 'If you want to update the tag, please run ',
3663 ansi(code, 'git tag -d ~w', [Tag])
3664 ].
3665message(git_tag_out_of_sync(Tag)) -->
3666 [ 'Release tag ', ansi(code, '~w', [Tag]),
3667 ' differs from this tag at the origin'
3668 ].
3669
3670message(published(Info, At)) -->
3671 [ 'Published pack ' ], msg_pack(Info), msg_info_version(Info),
3672 [' to be installed from '],
3673 msg_published_address(At).
3674message(publish_failed(Info, Reason)) -->
3675 [ 'Pack ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3676 msg_publish_failed(Reason).
3677
3678msg_publish_failed(throw(error(permission_error(register,
3679 pack(_),_URL),_))) -->
3680 [ ' is already registered with a different URL'].
3681msg_publish_failed(download) -->
3682 [' was already published?'].
3683msg_publish_failed(Status) -->
3684 [ ' failed for unknown reason (~p)'-[Status] ].
3685
3686msg_published_address(git(URL)) -->
3687 msg_url(URL, _).
3688msg_published_address(file(URL)) -->
3689 msg_url(URL, _).
3690
3691candidate_dirs([]) --> [].
3692candidate_dirs([H|T]) --> [ nl, ' ~w'-[H] ], candidate_dirs(T).
3693 3694message(resolve_remove) -->
3695 [ nl, 'Please select an action:', nl, nl ].
3696message(create_pack_dir) -->
3697 [ nl, 'Create directory for packages', nl ].
3698message(menu(item(I, Label))) -->
3699 [ '~t(~d)~6| '-[I] ],
3700 label(Label).
3701message(menu(default_item(I, Label))) -->
3702 [ '~t(~d)~6| * '-[I] ],
3703 label(Label).
3704message(menu(select)) -->
3705 [ nl, 'Your choice? ', flush ].
3706message(confirm(Question, Default)) -->
3707 message(Question),
3708 confirm_default(Default),
3709 [ flush ].
3710message(menu(reply(Min,Max))) -->
3711 ( { Max =:= Min+1 }
3712 -> [ 'Please enter ~w or ~w'-[Min,Max] ]
3713 ; [ 'Please enter a number between ~w and ~w'-[Min,Max] ]
3714 ).
3715
3716 3717dep_issues(Issues) -->
3718 sequence(dep_issue, [nl], Issues).
3719
3720dep_issue(unsatisfied(Pack, Requires)) -->
3721 [ ' - Pack ' ], msg_pack(Pack), [' requires ~p'-[Requires]].
3722dep_issue(conflicts(Pack, Conflict)) -->
3723 [ ' - Pack ' ], msg_pack(Pack), [' conflicts with ~p'-[Conflict]].
3724
3729
3730install_label([link]) -->
3731 !,
3732 [ ansi(bold, 'Activate pack?', []) ].
3733install_label([unpack]) -->
3734 !,
3735 [ ansi(bold, 'Unpack archive?', []) ].
3736install_label(_) -->
3737 [ ansi(bold, 'Download packs?', []) ].
3738
3739
3740install_plan(Plan, Actions) -->
3741 install_plan(Plan, Actions, Sec),
3742 sec_warning(Sec).
3743
3744install_plan([], [], _) -->
3745 [].
3746install_plan([H|T], [AH|AT], Sec) -->
3747 install_step(H, AH, Sec), [nl],
3748 install_plan(T, AT, Sec).
3749
3750install_step(Info, keep, _Sec) -->
3751 { Info.get(keep) == true },
3752 !,
3753 [ ' Keep ' ], msg_pack(Info), [ ' at version ~w'-[Info.version] ],
3754 msg_can_upgrade(Info).
3755install_step(Info, Action, Sec) -->
3756 { From = Info.get(upgrade),
3757 VFrom = From.version,
3758 VTo = Info.get(version),
3759 ( cmp_versions(>=, VTo, VFrom)
3760 -> Label = ansi(bold, ' Upgrade ', [])
3761 ; Label = ansi(warning, ' Downgrade ', [])
3762 )
3763 },
3764 [ Label ], msg_pack(Info),
3765 [ ' from version ~w to ~w'- [From.version, Info.get(version)] ],
3766 install_from(Info, Action, Sec).
3767install_step(Info, Action, Sec) -->
3768 { _From = Info.get(upgrade) },
3769 [ ' Upgrade ' ], msg_pack(Info),
3770 install_from(Info, Action, Sec).
3771install_step(Info, Action, Sec) -->
3772 { Dep = Info.get(dependency_for) },
3773 [ ' Install ' ], msg_pack(Info),
3774 [ ' at version ~w as dependency for '-[Info.version],
3775 ansi(code, '~w', [Dep])
3776 ],
3777 install_from(Info, Action, Sec),
3778 msg_downloads(Info).
3779install_step(Info, Action, Sec) -->
3780 { Info.get(commit) == 'HEAD' },
3781 !,
3782 [ ' Install ' ], msg_pack(Info), [ ' at current GIT HEAD'-[] ],
3783 install_from(Info, Action, Sec),
3784 msg_downloads(Info).
3785install_step(Info, link, _Sec) -->
3786 { Info.get(link) == true,
3787 uri_file_name(Info.get(url), Dir)
3788 },
3789 !,
3790 [ ' Install ' ], msg_pack(Info), [ ' as symlink to ', url(Dir) ].
3791install_step(Info, Action, Sec) -->
3792 [ ' Install ' ], msg_pack(Info), [ ' at version ~w'-[Info.get(version)] ],
3793 install_from(Info, Action, Sec),
3794 msg_downloads(Info).
3795install_step(Info, Action, Sec) -->
3796 [ ' Install ' ], msg_pack(Info),
3797 install_from(Info, Action, Sec),
3798 msg_downloads(Info).
3799
3800install_from(Info, download, Sec) -->
3801 { download_url(Info.url) },
3802 !,
3803 [ ' from ' ], msg_url(Info.url, Sec).
3804install_from(Info, unpack, Sec) -->
3805 [ ' from ' ], msg_url(Info.url, Sec).
3806
3807msg_url(URL, unsafe) -->
3808 { atomic(URL),
3809 atom_concat('http://', Rest, URL)
3810 },
3811 [ ansi(error, '~w', ['http://']), '~w'-[Rest] ].
3812msg_url(URL, _) -->
3813 [ url(URL) ].
3814
3815sec_warning(Sec) -->
3816 { var(Sec) },
3817 !.
3818sec_warning(unsafe) -->
3819 [ ansi(warning, ' WARNING: The installation plan includes downloads \c
3820 from insecure HTTP servers.', []), nl
3821 ].
3822
3823msg_downloads(Info) -->
3824 { Downloads = Info.get(all_downloads),
3825 Downloads > 0
3826 },
3827 [ ansi(comment, ' (downloaded ~D times)', [Downloads]) ],
3828 !.
3829msg_downloads(_) -->
3830 [].
3831
3832msg_pack(Pack) -->
3833 { atom(Pack) },
3834 !,
3835 [ ansi(code, '~w', [Pack]) ].
3836msg_pack(Info) -->
3837 msg_pack(Info.pack).
3838
3839msg_info_version(Info) -->
3840 [ ansi(code, '@~w', [Info.get(version)]) ],
3841 !.
3842msg_info_version(_Info) -->
3843 [].
3844
3848
3849msg_build_plan(Plan) -->
3850 sequence(build_step, [nl], Plan).
3851
3852build_step(Info) -->
3853 [ ' Build ' ], msg_pack(Info), [' in directory ', url(Info.installed) ].
3854
3855msg_can_upgrade_target(Info) -->
3856 [ ' Pack ' ], msg_pack(Info),
3857 [ ' is installed at version ~w'-[Info.version] ],
3858 msg_can_upgrade(Info).
3859
3860pack_list([]) --> [].
3861pack_list([H|T]) -->
3862 [ ' - Pack ' ], msg_pack(H), [nl],
3863 pack_list(T).
3864
3865label(remove_only(Pack)) -->
3866 [ 'Only remove package ~w (break dependencies)'-[Pack] ].
3867label(remove_deps(Pack, Deps)) -->
3868 { length(Deps, Count) },
3869 [ 'Remove package ~w and ~D dependencies'-[Pack, Count] ].
3870label(create_dir(Dir)) -->
3871 [ '~w'-[Dir] ].
3872label(install_from(git(URL))) -->
3873 !,
3874 [ 'GIT repository at ~w'-[URL] ].
3875label(install_from(URL)) -->
3876 [ '~w'-[URL] ].
3877label(cancel) -->
3878 [ 'Cancel' ].
3879
3880confirm_default(yes) -->
3881 [ ' Y/n? ' ].
3882confirm_default(no) -->
3883 [ ' y/N? ' ].
3884confirm_default(none) -->
3885 [ ' y/n? ' ].
3886
3887msg_version(Version) -->
3888 [ '~w'-[Version] ].
3889
3890msg_can_upgrade(Info) -->
3891 { Latest = Info.get(latest_version) },
3892 [ ansi(warning, ' (can be upgraded to ~w)', [Latest]) ].
3893msg_can_upgrade(_) -->
3894 [].
3895
3896
3897 3900
3901local_uri_file_name(URL, FileName) :-
3902 uri_file_name(URL, FileName),
3903 !.
3904local_uri_file_name(URL, FileName) :-
3905 uri_components(URL, Components),
3906 uri_data(scheme, Components, File), File == file,
3907 uri_data(authority, Components, FileNameEnc),
3908 uri_data(path, Components, ''),
3909 uri_encoded(path, FileName, FileNameEnc).
3910
3911det_if(Cond, Goal) :-
3912 ( Cond
3913 -> Goal,
3914 !
3915 ; Goal
3916 ).
3917
3918member_nonvar(_, Var) :-
3919 var(Var),
3920 !,
3921 fail.
3922member_nonvar(E, [E|_]).
3923member_nonvar(E, [_|T]) :-
3924 member_nonvar(E, T)