35
36:- module(cpack,
37 [ cpack_install/1, 38 cpack_upgrade/0,
39 cpack_upgrade/1, 40 cpack_remove/1, 41 cpack_remove/2, 42 43 cpack_create/3, 44 cpack_configure/1, 45 46 cpack_add_dir/2, 47 cpack_register/3, 48 current_cpack/1, 49 cpack_property/2 50 ]). 51:- use_module(library(semweb/rdf_db)). 52:- use_module(library(semweb/rdfs)). 53:- use_module(library(semweb/rdf_library)). 54:- use_module(library(http/http_open)). 55:- use_module(library(uri)). 56:- use_module(library(lists)). 57:- use_module(library(git)). 58:- use_module(library(setup)). 59:- use_module(library(conf_d)). 60:- use_module(library(filesex)). 61:- use_module(library(settings)). 62:- use_module(library(error)). 63:- use_module(library(apply)). 64:- use_module(library(option)). 65
69
70:- setting(cpack:package_directory, atom, cpack,
71 'Directory where packages are downloaded'). 72:- setting(cpack:server, atom, 'http://cliopatria.swi-prolog.org/',
73 'Address of the fallback server'). 74
75:- rdf_register_ns(cpack, 'http://cliopatria.swi-prolog.org/schema/cpack#'). 76:- rdf_register_ns(foaf, 'http://xmlns.com/foaf/0.1/'). 77
108
109cpack_install(URL) :-
110 \+ is_list(URL),
111 uri_is_global(URL),
112 !,
113 cpack_package_data(URL, Terms),
114 cpack_install_terms(Terms).
115cpack_install(Name) :-
116 pack_data_url(Name, URL),
117 print_message(informational, cpack(probe(URL))),
118 catch(cpack_package_data(URL, Terms), E, true),
119 ( var(E)
120 -> !, cpack_install_terms(Terms)
121 ; print_message(error, E),
122 fail
123 ).
124
129
130pack_data_url(Name, URL) :-
131 cpack_load_profile,
132 ( rdf_has(_, cpack:servers, List),
133 rdfs_member(Server, List)
134 ; setting(cpack:server, Server)
135 ),
136 ensure_slash(Server, ServerDir),
137 pack_data_url(ServerDir, Name, URL).
138
139pack_data_url(ServerDir, Names, URL) :-
140 is_list(Names),
141 !,
142 maplist(pack_param, Names, Params),
143 uri_query_components(Query, Params),
144 atomic_list_concat([ServerDir, cpack, /?, Query], URL).
145pack_data_url(ServerDir, Name, URL) :-
146 atomic_list_concat([ServerDir, cpack, /, Name], URL).
147
148pack_param(Name, p(Name)).
149
150
151ensure_slash(Server, ServerDir) :-
152 ( sub_atom(Server, _, _, 0, /)
153 -> ServerDir = Server
154 ; atom_concat(Server, /, ServerDir)
155 ).
156
157cpack_package_data(URL, Terms) :-
158 setup_call_cleanup(http_open(URL, In, []),
159 read_stream_to_terms(In, Terms),
160 close(In)).
161
162read_stream_to_terms(In, Terms) :-
163 read_term(In, Term0, []),
164 read_stream_to_terms(Term0, In, Terms).
165
166read_stream_to_terms(end_of_file, _, []) :- !.
167read_stream_to_terms(Term, In, [Term|T]) :-
168 read_term(In, Term1, []),
169 read_stream_to_terms(Term1, In, T).
170
171
175
176cpack_install_terms(Terms) :-
177 ( Terms = [cpack(Name, Packages)]
178 -> print_message(informational, cpack(requires(Name, Packages))),
179 maplist(package_status, Packages, Status),
180 maplist(download_package, Status),
181 maplist(configure_package, Packages)
182 ; Terms = [no_cpack(Name)]
183 -> existence_error(cpack, Name)
184 ; Terms = [error(Error)]
185 -> throw(Error)
186 ; domain_error(cpack_reply, Terms)
187 ).
188
193
194package_status(cpack(Package, Options),
195 cpack(Package, Options, Status)) :-
196 cpack_package_dir(Package, Dir, false),
197 directory_file_path(Dir, '.git', GitRepo),
198 ( access_file(GitRepo, read)
199 -> option(branch(Branch), Options, master),
200 atom_concat('origin/', Branch, Commit),
201 git_describe(OldVersion, [directory(Dir)]),
202 git([fetch, origin], [ directory(Dir) ]),
203 git_describe(NewVersion, [directory(Dir),commit(Commit)]),
204 ( OldVersion == NewVersion
205 -> Status = no_change(OldVersion)
206 ; Status = upgrade(OldVersion, NewVersion)
207 )
208 ; Status = new
209 ).
210
211download_package(cpack(Package, _, no_change(OldVersion))) :-
212 !,
213 print_message(informational, cpack(no_change(Package, OldVersion))).
214download_package(cpack(Package, Options, upgrade(Old, New))) :-
215 !,
216 print_message(informational, cpack(upgrade(Package, Old, New))),
217 option(branch(Branch), Options, master),
218 cpack_package_dir(Package, Dir, false),
219 atom_concat('origin/', Branch, Commit),
220 git([merge, Commit],
221 [ directory(Dir)
222 ]).
223download_package(cpack(Package, Options, new)) :-
224 option(pack_repository(Repository), Options),
225 print_message(informational, cpack(download(Package, Repository))),
226 cpack_package_dir(Package, Dir, false),
227 cpack_download(Repository, Dir).
228
229configure_package(cpack(Package, Options)) :-
230 cpack_module_options(Options, ModuleOptions),
231 cpack_configure(Package, ModuleOptions).
232
233cpack_module_options([], []).
234cpack_module_options([H0|T0], [H|T]) :-
235 cpack_module_option(H0, H),
236 !,
237 cpack_module_options(T0, T).
238cpack_module_options([_|T0], T) :-
239 cpack_module_options(T0, T).
240
241cpack_module_option(url(URL), home_url(URL)).
242cpack_module_option(requires(Packages), requires(Packages)).
243
244
250
251cpack_download(_Package, Dir) :-
252 directory_file_path(Dir, '.git', GitRepo),
253 exists_directory(GitRepo),
254 !,
255 git([pull],
256 [ directory(Dir)
257 ]). 258cpack_download(git(GitURL, Options), Dir) :-
259 findall(O, git_clone_option(O, Options), LOL),
260 append([ [clone, GitURL, Dir]
261 | LOL
262 ], GitOptions),
263 git(GitOptions, []),
264 setup_push_for_download(Dir).
265
266git_clone_option(['-b', Branch], Options) :-
267 option(branch(Branch), Options).
268
277
278setup_push_for_download(Dir) :-
279 file_base_name(Dir, Name),
280 default_binding(default, Name, pushrepository(PushURL)),
281 !,
282 print_message(informational, cpack(probe_remote(PushURL))),
283 catch(git(['ls-remote', '--heads', PushURL],
284 [ output(_),
285 error(_)
286 ]),
287 E, true),
288 ( var(E)
289 -> print_message(informational, cpack(add_remote(upload, PushURL))),
290 git([ remote, add, upload, PushURL],
291 [ directory(Dir)
292 ])
293 ; E = error(process_error(git(_), exit(_)), _)
294 -> true
295 ; print_message(error, E)
296 ).
297setup_push_for_download(_).
298
299
303
304cpack_upgrade :-
305 findall(Name, current_cpack(Name), Names),
306 cpack_install(Names).
307
311
312cpack_upgrade(Name) :-
313 cpack_install(Name).
314
318
319cpack_configure(Name) :-
320 cpack_configure(Name, []).
321
322cpack_configure(Name, Options) :-
323 cpack_package_dir(Name, Dir, false),
324 !,
325 exists_directory(Dir),
326 ( conf_d_enabled(ConfigEnabled)
327 -> cpack_add_dir(ConfigEnabled, Dir, Options)
328 ; existence_error(directory, 'config-enabled')
329 ).
330cpack_configure(Name, _) :-
331 existence_error(cpack, Name).
332
333
339
340cpack_add_dir(ConfigEnable, Dir) :-
341 cpack_add_dir(ConfigEnable, Dir, []).
342
343cpack_add_dir(ConfigEnable, Dir, Options) :-
344 directory_file_path(ConfigEnable, '010-packs.pl', PacksFile),
345 directory_file_path(Dir, 'config-available', ConfigAvailable),
346 file_base_name(Dir, Pack),
347 add_pack_to_search_path(PacksFile, Pack, Dir, Modified, Options),
348 setup_default_config(ConfigEnable, ConfigAvailable, []),
349 ( Modified == true 350 -> load_files(PacksFile, [if(true)])
351 ; true
352 ),
353 conf_d_reload.
354
355
366
367add_pack_to_search_path(PackFile, Pack, Dir, Modified, Options) :-
368 exists_file(PackFile),
369 !,
370 read_file_to_terms(PackFile, Terms, []),
371 New = (:- cpack_register(Pack, Dir, Options)),
372 ( memberchk(New, Terms)
373 -> Modified = false
374 ; Old = (:- cpack_register(Pack, _, _)),
375 memberchk(Old, Terms)
376 -> selectchk(Old, Terms, New, Terms2),
377 write_pack_register(PackFile, Terms2)
378 ; setup_call_cleanup(open(PackFile, append, Out),
379 extend_search_path(Out, Pack, Dir, Options),
380 close(Out)),
381 Modified = true
382 ).
383add_pack_to_search_path(PackFile, Pack, Dir, true, Options) :-
384 open(PackFile, write, Out),
385 write_search_path_header(Out),
386 extend_search_path(Out, Pack, Dir, Options),
387 close(Out).
388
389write_pack_register(PackFile, Terms) :-
390 setup_call_cleanup(open(PackFile, write, Out),
391 ( write_search_path_header(Out),
392 Templ = cpack_register(_, _, _),
393 forall(member((:-Templ), Terms),
394 format(Out, ':- ~q.~n', [Templ]))
395 ),
396 close(Out)).
397
398
(Out) :-
400 format(Out, '/* Generated file~n', []),
401 format(Out, ' This file defines the search-path for added packs~n', []),
402 format(Out, '*/~n~n', []),
403 format(Out, ':- module(conf_packs, []).~n~n', []),
404 format(Out, ':- multifile user:file_search_path/2.~n', []),
405 format(Out, ':- dynamic user:file_search_path/2.~n', []),
406 format(Out, ':- multifile cpack:registered_cpack/2.~n~n', []).
407
408extend_search_path(Out, Pack, Dir, Options) :-
409 format(Out, ':- ~q.~n', [cpack_register(Pack, Dir, Options)]).
410
411
412 415
429
430cpack_remove(Name) :-
431 cpack_remove(Name, []).
432
433cpack_remove(Name, Options) :-
434 \+ option(force(true), Options),
435 required_by(Name, Dependents),
436 !,
437 throw(error(cpack_error(cannot_remove(Name, Dependents)), _)).
438cpack_remove(Name, Options) :-
439 registered_cpack(Name, Dir, _Options),
440 absolute_file_name(Dir, DirPath,
441 [ file_type(directory),
442 access(read)
443 ]),
444 cpack_unregister(Name, Options),
445 remove_config(DirPath, Options),
446 remove_dir(DirPath, Options).
447
448required_by(Name, Dependents) :-
449 setof(Dep, required_pack(Name, Dep), Dependents).
450
451required_pack(Name, Pack) :-
452 registered_cpack(Pack, _, Options),
453 ( member(requires(Packs), Options),
454 member(Name, Packs)
455 -> true
456 ).
457
458
463
464cpack_unregister(Pack, Options) :-
465 conf_d_enabled(ConfigEnabled),
466 directory_file_path(ConfigEnabled, '010-packs.pl', PacksFile),
467 exists_file(PacksFile),
468 read_file_to_terms(PacksFile, Terms, []),
469 selectchk((:- cpack_register(Pack,_,_)), Terms, RestTerms),
470 !,
471 ( option(fake(true), Options)
472 -> print_message(informational, cpack(action(update(PacksFile))))
473 ; write_pack_register(PacksFile, RestTerms),
474 load_files(PacksFile, [if(true)])
475 ).
476cpack_unregister(_, _).
477
478
487
488remove_config(Dir, Options) :-
489 conf_d_enabled(ConfigEnabled),
490 entry_paths(ConfigEnabled, Paths),
491 maplist(remove_config(Dir, Options), Paths).
492
493remove_config(PackDir, Options, File) :-
494 read_link(File, _, Target),
495 absolute_file_name(Target, CanonicalTarget),
496 sub_atom(CanonicalTarget, 0, _, _, PackDir),
497 !,
498 action(delete_file(File), Options).
499remove_config(PackDir, Options, PlFile) :-
500 file_name_extension(_, pl, PlFile),
501 setup_call_cleanup(open(PlFile, read, In),
502 read(In, Term0),
503 close(In)),
504 Term0 = (:- consult(Rel)),
505 absolute_file_name(Rel, Target,
506 [ relative_to(PlFile) ]),
507 sub_atom(Target, 0, _, _, PackDir),
508 !,
509 action(delete_file(PlFile), Options).
510remove_config(_, _, _).
511
512
516
517remove_dir(Link, Options) :-
518 read_link(Link, _, _),
519 !,
520 action(delete_file(Link), Options).
521remove_dir(Dir, Options) :-
522 exists_directory(Dir),
523 !,
524 entry_paths(Dir, Paths),
525 forall(member(P, Paths),
526 remove_dir(P, Options)),
527 action(delete_directory(Dir), Options).
528remove_dir(File, Options) :-
529 action(delete_file(File), Options).
530
531entry_paths(Dir, Paths) :-
532 directory_files(Dir, Entries),
533 entry_paths(Entries, Dir, Paths).
534
535entry_paths([], _, []).
536entry_paths([H|T0], Dir, T) :-
537 hidden(H),
538 !,
539 entry_paths(T0, Dir, T).
540entry_paths([H|T0], Dir, [P|T]) :-
541 directory_file_path(Dir, H, P),
542 entry_paths(T0, Dir, T).
543
544hidden(.).
545hidden(..).
546
547:- meta_predicate
548 action(0, +). 549
550action(G, Options) :-
551 option(fake(true), Options),
552 !,
553 print_message(informational, cpack(action(G))).
554action(G, _) :-
555 G.
556
557 560
564
565cpack_register(PackName, Dir, Options) :-
566 throw(error(context_error(nodirective,
567 cpack_register(PackName, Dir, Options)), _)).
568
569
570user:term_expansion((:-cpack_register(PackName, Dir0, Options)), Clauses) :-
571 full_dir(Dir0, Dir),
572 Term =.. [PackName,'.'],
573 Clauses = [ user:file_search_path(PackName, Dir),
574 user:file_search_path(cpacks, Term),
575 cpack:registered_cpack(PackName, Dir, Options)
576 ].
577
578full_dir(Dir, Dir) :-
579 compound(Dir),
580 !.
581full_dir(Dir, Dir) :-
582 is_absolute_file_name(Dir),
583 !.
584full_dir(Dir, AbsDir) :-
585 prolog_load_context(directory, ConfigEnabled),
586 file_directory_name(ConfigEnabled, RelTo),
587 absolute_file_name(Dir, AbsDir,
588 [ relative_to(RelTo),
589 file_type(directory),
590 access(exist)
591 ]).
592
593
594
595:- multifile
596 registered_cpack/3. 597
601
602current_cpack(Name) :-
603 registered_cpack(Name, _, _).
604
611
612cpack_property(Name, Property) :-
613 property_cpack(Property, Name).
614
615property_cpack(directory(Dir), Name) :-
616 registered_cpack(Name, LocalDir, _),
617 absolute_file_name(LocalDir, Dir).
618property_cpack(Option, Name) :-
619 registered_cpack(Name, _, Options),
620 member(Option, Options).
621
627
628:- multifile
629 prolog_version:git_module_hook/3. 630
631prolog_version:git_module_hook(Name, Directory, Options) :-
632 registered_cpack(Name, LocalDir, Options),
633 absolute_file_name(LocalDir, Directory).
634
635
636 639
657
658cpack_create(Name, Title, Options) :-
659 cpack_load_schema,
660 cpack_load_profile,
661 option(type(Type), Options, package),
662 option(description(Descr), Options,
663 'Package description goes here. You can use markdown.'),
664 package_class_id(Type, PkgClass),
665 default_bindings(default, Name, DefaultBindings),
666 merge_options(Options,
667 [ name(Name),
668 title(Title),
669 pkgclass(PkgClass),
670 description(Descr)
671 | DefaultBindings
672 ], Vars),
673 cpack_package_dir(Name, Dir, true),
674 forall(cpack_dir(SubDir, Type),
675 make_cpack_dir(Dir, SubDir)),
676 forall(cpack_template(In, Out),
677 install_template_file(In, Out, Vars)),
678 git([init], [directory(Dir)]),
679 git([add, '.'], [directory(Dir)]),
680 git([commit, '-m', 'Installed template'], [directory(Dir)]),
681 git([tag, epoch], [directory(Dir)]),
682 git_setup_push(Dir, Vars).
683
684package_class_id(Label, TurtleID) :-
685 package_class(Label, Class),
686 rdf_global_id(Prefix:Name, Class),
687 atomic_list_concat([Prefix, :, Name], TurtleID).
688
689package_class(Label, Class) :-
690 rdf_has(Class, rdfs:label, literal(Label)),
691 rdfs_subclass_of(Class, cpack:'Package'),
692 !.
693package_class(Label, _) :-
694 domain_error(package_class, Label).
695
696default_bindings(Profile, Name, Bindings) :-
697 findall(B, default_binding(Profile, Name, B), Bindings).
698
699default_binding(ProfileName, Name, B) :-
700 rdf_has(Profile, cpack:name, literal(ProfileName)),
701 ( rdf_has(Profile, cpack:defaultAuthor, Author),
702 ( rdf_has(Author, foaf:name, literal(AuthorName)),
703 B = foafname(AuthorName)
704 ; rdf_has(Author, foaf:mbox, MBOX),
705 B = foafmbox(MBOX)
706 )
707 ; rdf_has(Profile, cpack:fetchRepositoryTemplate, literal(GitTempl)),
708 substitute(GitTempl, '@CPACK@', Name, GitRepo),
709 B = fetchrepository(GitRepo)
710 ; rdf_has(Profile, cpack:pushRepositoryTemplate, literal(GitTempl)),
711 substitute(GitTempl, '@CPACK@', Name, GitRepo),
712 B = pushrepository(GitRepo)
713 ).
714
720
721git_setup_push(Dir, Vars) :-
722 option(pushrepository(PushURL), Vars),
723 !,
724 option(title(Title), Vars, 'ClioPatria CPACK'),
725 git([remote, add, origin, PushURL], [directory(Dir)]),
726 directory_file_path(Dir, '.git/config', Config),
727 setup_call_cleanup(open(Config, append, Out),
728 format(Out, '[branch "master"]\n\c
729 \tremote = origin\n\c
730 \tmerge = refs/heads/master\n', []),
731 close(Out)),
732 catch(git_create_origin(Dir, PushURL, Title), E, true),
733 ( var(E)
734 -> true
735 ; subsumes_term(error(existence_error(source_sink, path(Exe)), _), E)
736 -> print_message(error, cpack(missing_program(Exe)))
737 ; print_message(error, E)
738 ).
739git_setup_push(_,_).
740
753
754git_create_origin(Dir, PushURL, Title) :-
755 ( atom_concat('git@github.com:', UserPath, PushURL)
756 -> true
757 ; atom_concat('https://github.com/', UserPath, PushURL)
758 ),
759 atomic_list_concat([_User, RepoGit], /, UserPath),
760 file_name_extension(Repo, git, RepoGit),
761 !,
762 process_create(path(hub), [create, Repo, '-d', Title],
763 [ cwd(Dir)
764 ]).
765git_create_origin(_Dir, PushURL, Title) :-
766 uri_components(PushURL, Components),
767 uri_data(scheme, Components, Scheme),
768 ( Scheme == ssh
769 -> uri_data(authority, Components, Authority)
770 ; Authority = Scheme
771 ),
772 uri_data(path, Components, Path),
773 file_directory_name(Path, Parent),
774 file_base_name(Path, Repo),
775 format(atom(Command),
776 'cd "~w" && mkdir "~w" && cd "~w" && \c
777 git init --bare && echo "~w" > description && \c
778 touch git-daemon-export-ok',
779 [Parent, Repo, Repo, Title]),
780 process_create(path(ssh), [ Authority, Command ], []).
781
782
786
787make_cpack_dir(Dir, SubDir) :-
788 directory_file_path(Dir, SubDir, New),
789 ( exists_directory(New)
790 -> true
791 ; make_directory_path(New),
792 print_message(informational, cpack(create_directory(New)))
793 ).
794
795install_template_file(In, Out, Vars) :-
796 option(name(Name), Vars),
797 absolute_file_name(In, InFile, [access(read)]),
798 substitute(Out, '@NAME@', Name, OutFile),
799 cpack_package_dir(Name, Dir, true),
800 directory_file_path(Dir, OutFile, OutPath),
801 copy_file_with_vars(InFile, OutPath, Vars),
802 print_message(informational, cpack(installed_template(OutFile))).
803
804substitute(In, From, To, Out) :-
805 sub_atom(In, B, _, A, From),
806 !,
807 sub_atom(In, 0, B, _, Start),
808 sub_atom(In, _, A, 0, End),
809 atomic_list_concat([Start, To, End], Out).
810substitute(In, _, _, In).
811
812cpack_dir('rdf', _).
813cpack_dir('rdf/cpack', _).
814cpack_dir('config-available', _).
815cpack_dir('entailment', _).
816cpack_dir('applications', _).
817cpack_dir('api', _).
818cpack_dir('components', _).
819cpack_dir('skin', _).
820cpack_dir('lib', _).
821cpack_dir('web', _).
822cpack_dir('web/js', _).
823cpack_dir('web/css', _).
824cpack_dir('web/html', _).
825
826cpack_template(library('cpack/config-available.pl.in'),
827 'config-available/@NAME@.pl').
828cpack_template(library('cpack/DEFAULTS.in'),
829 'config-available/DEFAULTS').
830cpack_template(library('cpack/pack.ttl.in'),
831 'rdf/cpack/@NAME@.ttl').
832cpack_template(library('cpack/README.md.in'),
833 'README.md').
834
835
836 839
845
846cpack_load_profile :-
847 absolute_file_name(user_profile('.cpack.ttl'), Path,
848 [ access(read),
849 file_errors(fail)
850 ]),
851 !,
852 rdf_load(Path).
853cpack_load_profile.
854
855
859
860cpack_load_schema :-
861 rdf_attach_library(rdf(cpack)),
862 rdf_load_library(cpack).
863
864
865
866 869
873
874cpack_package_dir(Name, Dir, Create) :-
875 setting(cpack:package_directory, PackageDir),
876 directory_file_path(PackageDir, Name, Dir),
877 ( ( Create == false
878 ; exists_directory(Dir)
879 )
880 -> true
881 ; make_directory_path(Dir)
882 ).
883
884:- multifile
885 prolog:message//1,
886 prolog:error_message//1. 887
888prolog:message(cpack(Message)) -->
889 message(Message).
890
891message(create_directory(New)) -->
892 [ 'Created directory ~w'-[New] ].
893message(installed_template(File)) -->
894 [ 'Installed template ~w'-[File] ].
895message(requires(Name, Packages)) -->
896 ( { is_list(Name) }
897 -> [ 'Packages ~w require the following packages:'-[Name] ]
898 ; [ 'Package ~w requires the following packages:'-[Name] ]
899 ),
900 sub_packages(Packages),
901 [ nl, 'Querying package status ...'-[] ].
902message(no_change(Name, Version)) -->
903 [ ' ~w: ~t~30|no change (~w)'-[Name, Version] ].
904message(upgrade(Name, Old, New)) -->
905 [ ' ~w: ~t~30|upgrading (~w..~w) ...'-[Name, Old, New] ].
906message(download(Name, git(Url, _))) -->
907 [ ' ~w: ~t~30|downloading from ~w ...'-[Name, Url] ].
908message(probe(URL)) -->
909 [ 'Trying CPACK server at ~w ...'-[URL] ].
910message(probe_remote(URL)) -->
911 [ 'Checking availability of GIT repository ~w ...'-[URL] ].
912message(add_remote(Name, URL)) -->
913 [ 'Running "git remote add ~w ~w ..."'-[Name, URL] ].
914message(action(G)) -->
915 [ '~q'-[G] ].
916message(missing_program(hub)) -->
917 !,
918 [ 'Cannot find the GitHub command line utility "hub".'-[], nl,
919 'See https://hub.github.com/ for installation instructions'-[]
920 ].
921message(missing_program(Prog)) -->
922 [ 'Cannot find helper program "~w".'-[Prog] ].
923sub_packages([]) --> [].
924sub_packages([H|T]) --> sub_package(H), sub_packages(T).
925
926sub_package(cpack(Name, Options)) -->
927 { option(title(Title), Options) },
928 !,
929 [ nl, ' ~w: ~t~30|~w'-[Name, Title] ].
930sub_package(cpack(Name, _)) -->
931 [ nl, ' ~w: ~t~30|~w'-[Name] ].
932
933prolog:error_message(cpack_error(Error)) -->
934 cpack_error(Error).
935
936cpack_error(not_satisfied(Pack, Reasons)) -->
937 [ 'Package not satisfied: ~p'-[Pack] ],
938 not_satisfied_list(Reasons).
939cpack_error(cannot_remove(Pack, Dependents)) -->
940 [ 'Cannot remove "~p" because the following packs depend on it'-[Pack] ],
941 pack_list(Dependents).
942
943not_satisfied_list([]) --> [].
944not_satisfied_list([H|T]) --> not_satisfied(H), not_satisfied_list(T).
945
946not_satisfied(no_token(Token)) -->
947 [ nl, ' Explicit requirement not found: ~w'-[Token] ].
948not_satisfied(file(File, Problems)) -->
949 [ nl, ' File ~p'-[File] ],
950 file_problems(Problems).
951
952file_problems([]) --> [].
953file_problems([H|T]) --> file_problem(H), file_problems(T).
954
955file_problem(predicate_not_found(PI)) -->
956 [ nl, ' Predicate not resolved: ~w'-[PI] ].
957
958pack_list([]) --> [].
959pack_list([H|T]) -->
960 [ nl, ' ~p'-[H] ],
961 pack_list(T)