/* Part of ClioPatria SeRQL and SPARQL server Author: Jan Wielemaker E-mail: J.Wielemaker@vu.nl WWW: http://www.swi-prolog.org Copyright (c) 2010-2018, University of Amsterdam, VU University Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ :- module(cpack, [ cpack_install/1, % +NameOrURL cpack_upgrade/0, cpack_upgrade/1, % +Name cpack_remove/1, % +Name cpack_remove/2, % +Name, +Options % For creators cpack_create/3, % +Name, +Title, +Options cpack_configure/1, % +Name % Further API cpack_add_dir/2, % +ConfigEnabled, +Directory cpack_register/3, % +Name, +Dir, +Options current_cpack/1, % ?Name cpack_property/2 % ?Name, ?Property ]). :- use_module(library(semweb/rdf_db)). :- use_module(library(semweb/rdfs)). :- use_module(library(semweb/rdf_library)). :- use_module(library(http/http_open)). :- use_module(library(uri)). :- use_module(library(lists)). :- use_module(library(git)). :- use_module(library(setup)). :- use_module(library(conf_d)). :- use_module(library(filesex)). :- use_module(library(settings)). :- use_module(library(error)). :- use_module(library(apply)). :- use_module(library(option)). /** The ClioPatria package manager */ :- setting(cpack:package_directory, atom, cpack, 'Directory where packages are downloaded'). :- setting(cpack:server, atom, 'http://cliopatria.swi-prolog.org/', 'Address of the fallback server'). :- rdf_register_ns(cpack, 'http://cliopatria.swi-prolog.org/schema/cpack#'). :- rdf_register_ns(foaf, 'http://xmlns.com/foaf/0.1/'). %! cpack_install(+Install) is semidet. % % Install package by name or URL. The URL of a CPACK can be found % on the web-page of the package. If a *name* is given, % cpack_install/1 queries the configured servers for the package. % For example: % % == % ?- cpack_install('EDM'). % % Trying CPACK server at http://cliopatria.swi-prolog.org/cpack/EDM ... % % Installing package EDM: % % EDM -- View Europeana Data Model % % Initialized empty Git repository in /home/jan/tmp/test/cpack/EDM/.git/ % % Installing EDM.pl ... % % /home/jan/tmp/test/config-enabled/010-packs.pl compiled into conf_packs 0.00 sec, 1,480 bytes % % Added the following config files: % % /home/jan/tmp/test/config-enabled/010-packs.pl % % /home/jan/tmp/test/config-enabled/EDM.pl % % library(count) compiled into count 0.02 sec, 13,280 bytes % % skin(EDM) compiled into edm 0.02 sec, 52,984 bytes % % /home/jan/tmp/test/config-enabled/EDM.pl compiled into conf_EDM 0.02 sec, 56,112 bytes % true. % == % % @see http://cliopatria.swi-prolog.org is the central package % repository. % @param Install is either a URL on the server that returns the % installation parameter (this is shown in the info box % of the package), or the name of a package or a list of % package names. cpack_install(URL) :- \+ is_list(URL), uri_is_global(URL), !, cpack_package_data(URL, Terms), cpack_install_terms(Terms). cpack_install(Name) :- pack_data_url(Name, URL), print_message(informational, cpack(probe(URL))), catch(cpack_package_data(URL, Terms), E, true), ( var(E) -> !, cpack_install_terms(Terms) ; print_message(error, E), fail ). %! pack_data_url(+NameOrNames, -URL) is nondet. % % URL can be tried to obtain information about the requested % packages. pack_data_url(Name, URL) :- cpack_load_profile, ( rdf_has(_, cpack:servers, List), rdfs_member(Server, List) ; setting(cpack:server, Server) ), ensure_slash(Server, ServerDir), pack_data_url(ServerDir, Name, URL). pack_data_url(ServerDir, Names, URL) :- is_list(Names), !, maplist(pack_param, Names, Params), uri_query_components(Query, Params), atomic_list_concat([ServerDir, cpack, /?, Query], URL). pack_data_url(ServerDir, Name, URL) :- atomic_list_concat([ServerDir, cpack, /, Name], URL). pack_param(Name, p(Name)). ensure_slash(Server, ServerDir) :- ( sub_atom(Server, _, _, 0, /) -> ServerDir = Server ; atom_concat(Server, /, ServerDir) ). cpack_package_data(URL, Terms) :- setup_call_cleanup(http_open(URL, In, []), read_stream_to_terms(In, Terms), close(In)). read_stream_to_terms(In, Terms) :- read_term(In, Term0, []), read_stream_to_terms(Term0, In, Terms). read_stream_to_terms(end_of_file, _, []) :- !. read_stream_to_terms(Term, In, [Term|T]) :- read_term(In, Term1, []), read_stream_to_terms(Term1, In, T). %! cpack_install_terms(+Terms) is det. % % Install from the server reply. cpack_install_terms(Terms) :- ( Terms = [cpack(Name, Packages)] -> print_message(informational, cpack(requires(Name, Packages))), maplist(package_status, Packages, Status), maplist(download_package, Status), maplist(configure_package, Packages) ; Terms = [no_cpack(Name)] -> existence_error(cpack, Name) ; Terms = [error(Error)] -> throw(Error) ; domain_error(cpack_reply, Terms) ). %! package_status(+CpackTerm, -Status) % % @param Status is a term cpack(Package, State), where State is % one of =no_change=, upgrade(Old, New) or =new=. package_status(cpack(Package, Options), cpack(Package, Options, Status)) :- cpack_package_dir(Package, Dir, false), directory_file_path(Dir, '.git', GitRepo), ( access_file(GitRepo, read) -> option(branch(Branch), Options, master), atom_concat('origin/', Branch, Commit), git_describe(OldVersion, [directory(Dir)]), git([fetch, origin], [ directory(Dir) ]), git_describe(NewVersion, [directory(Dir),commit(Commit)]), ( OldVersion == NewVersion -> Status = no_change(OldVersion) ; Status = upgrade(OldVersion, NewVersion) ) ; Status = new ). download_package(cpack(Package, _, no_change(OldVersion))) :- !, print_message(informational, cpack(no_change(Package, OldVersion))). download_package(cpack(Package, Options, upgrade(Old, New))) :- !, print_message(informational, cpack(upgrade(Package, Old, New))), option(branch(Branch), Options, master), cpack_package_dir(Package, Dir, false), atom_concat('origin/', Branch, Commit), git([merge, Commit], [ directory(Dir) ]). download_package(cpack(Package, Options, new)) :- option(pack_repository(Repository), Options), print_message(informational, cpack(download(Package, Repository))), cpack_package_dir(Package, Dir, false), cpack_download(Repository, Dir). configure_package(cpack(Package, Options)) :- cpack_module_options(Options, ModuleOptions), cpack_configure(Package, ModuleOptions). cpack_module_options([], []). cpack_module_options([H0|T0], [H|T]) :- cpack_module_option(H0, H), !, cpack_module_options(T0, T). cpack_module_options([_|T0], T) :- cpack_module_options(T0, T). cpack_module_option(url(URL), home_url(URL)). cpack_module_option(requires(Packages), requires(Packages)). %! cpack_download(+Repository, +TargetDir) % % Download Repository to Dir. % % @tbd Branches, trust cpack_download(_Package, Dir) :- directory_file_path(Dir, '.git', GitRepo), exists_directory(GitRepo), !, git([pull], [ directory(Dir) ]). % Too simplistic cpack_download(git(GitURL, Options), Dir) :- findall(O, git_clone_option(O, Options), LOL), append([ [clone, GitURL, Dir] | LOL ], GitOptions), git(GitOptions, []), setup_push_for_download(Dir). git_clone_option(['-b', Branch], Options) :- option(branch(Branch), Options). %! setup_push_for_download(+Dir) is det. % % If the downloaded repository can be related to a push-location % based on the current profile, we setup a remote for pushing % changes. This remote has tehe symbolic name =upload=. % % @tbd We can (and should) also verify whether the =upload= and % downloaded origin are at the same version. setup_push_for_download(Dir) :- file_base_name(Dir, Name), default_binding(default, Name, pushrepository(PushURL)), !, print_message(informational, cpack(probe_remote(PushURL))), catch(git(['ls-remote', '--heads', PushURL], [ output(_), error(_) ]), E, true), ( var(E) -> print_message(informational, cpack(add_remote(upload, PushURL))), git([ remote, add, upload, PushURL], [ directory(Dir) ]) ; E = error(process_error(git(_), exit(_)), _) -> true ; print_message(error, E) ). setup_push_for_download(_). %! cpack_upgrade % % Upgrade all packages to the server versions. cpack_upgrade :- findall(Name, current_cpack(Name), Names), cpack_install(Names). %! cpack_upgrade(Package) % % Upgrade Package. This is the same as cpack_install(Package). cpack_upgrade(Name) :- cpack_install(Name). %! cpack_configure(+Name) is det. % % Just configure a package. cpack_configure(Name) :- cpack_configure(Name, []). cpack_configure(Name, Options) :- cpack_package_dir(Name, Dir, false), !, exists_directory(Dir), ( conf_d_enabled(ConfigEnabled) -> cpack_add_dir(ConfigEnabled, Dir, Options) ; existence_error(directory, 'config-enabled') ). cpack_configure(Name, _) :- existence_error(cpack, Name). %! cpack_add_dir(+ConfigEnable, +PackageDir) % % Install package located in directory PackageDir. % % @tbd Register version-tracking with register_git_module/3. cpack_add_dir(ConfigEnable, Dir) :- cpack_add_dir(ConfigEnable, Dir, []). cpack_add_dir(ConfigEnable, Dir, Options) :- directory_file_path(ConfigEnable, '010-packs.pl', PacksFile), directory_file_path(Dir, 'config-available', ConfigAvailable), file_base_name(Dir, Pack), add_pack_to_search_path(PacksFile, Pack, Dir, Modified, Options), setup_default_config(ConfigEnable, ConfigAvailable, []), ( Modified == true % Update paths first! -> load_files(PacksFile, [if(true)]) ; true ), conf_d_reload. %! add_pack_to_search_path(+PackFile, +Pack, +Dir, -Modified, %! +Options) is det. % % Add a directive as below to PackFile. If PackFile already % contains a declaration for Pack with different attributes, the % file is rewritten using the new attributes. % % == % :- cpack_register(Pack, Dir, Options). % == add_pack_to_search_path(PackFile, Pack, Dir, Modified, Options) :- exists_file(PackFile), !, read_file_to_terms(PackFile, Terms, []), New = (:- cpack_register(Pack, Dir, Options)), ( memberchk(New, Terms) -> Modified = false ; Old = (:- cpack_register(Pack, _, _)), memberchk(Old, Terms) -> selectchk(Old, Terms, New, Terms2), write_pack_register(PackFile, Terms2) ; setup_call_cleanup(open(PackFile, append, Out), extend_search_path(Out, Pack, Dir, Options), close(Out)), Modified = true ). add_pack_to_search_path(PackFile, Pack, Dir, true, Options) :- open(PackFile, write, Out), write_search_path_header(Out), extend_search_path(Out, Pack, Dir, Options), close(Out). write_pack_register(PackFile, Terms) :- setup_call_cleanup(open(PackFile, write, Out), ( write_search_path_header(Out), Templ = cpack_register(_, _, _), forall(member((:-Templ), Terms), format(Out, ':- ~q.~n', [Templ])) ), close(Out)). write_search_path_header(Out) :- format(Out, '/* Generated file~n', []), format(Out, ' This file defines the search-path for added packs~n', []), format(Out, '*/~n~n', []), format(Out, ':- module(conf_packs, []).~n~n', []), format(Out, ':- multifile user:file_search_path/2.~n', []), format(Out, ':- dynamic user:file_search_path/2.~n', []), format(Out, ':- multifile cpack:registered_cpack/2.~n~n', []). extend_search_path(Out, Pack, Dir, Options) :- format(Out, ':- ~q.~n', [cpack_register(Pack, Dir, Options)]). /******************************* * REMOVAL * *******************************/ %! cpack_remove(+Pack) is det. %! cpack_remove(+Pack, +Options) is det. % % Remove CPACK Pack. Processed options: % % * force(Boolean) % If =true=, omit checking whether removing the package will % break dependencies. % * fake(true) % Print messages indicating what actions will be preformed, but % do not modify anything. % % @tbd Should we also try to unload all loaded files? cpack_remove(Name) :- cpack_remove(Name, []). cpack_remove(Name, Options) :- \+ option(force(true), Options), required_by(Name, Dependents), !, throw(error(cpack_error(cannot_remove(Name, Dependents)), _)). cpack_remove(Name, Options) :- registered_cpack(Name, Dir, _Options), absolute_file_name(Dir, DirPath, [ file_type(directory), access(read) ]), cpack_unregister(Name, Options), remove_config(DirPath, Options), remove_dir(DirPath, Options). required_by(Name, Dependents) :- setof(Dep, required_pack(Name, Dep), Dependents). required_pack(Name, Pack) :- registered_cpack(Pack, _, Options), ( member(requires(Packs), Options), member(Name, Packs) -> true ). %! cpack_unregister(+Pack, +Options) is det. % % Remove registration of the given CPACK. This is achieved by % updating 010-packs.pl and reloading this file. cpack_unregister(Pack, Options) :- conf_d_enabled(ConfigEnabled), directory_file_path(ConfigEnabled, '010-packs.pl', PacksFile), exists_file(PacksFile), read_file_to_terms(PacksFile, Terms, []), selectchk((:- cpack_register(Pack,_,_)), Terms, RestTerms), !, ( option(fake(true), Options) -> print_message(informational, cpack(action(update(PacksFile)))) ; write_pack_register(PacksFile, RestTerms), load_files(PacksFile, [if(true)]) ). cpack_unregister(_, _). %! remove_config(+Dir, +Options) % % Remove configuration that we loaded from Dir. Currently deletes % links and Prolog `link files'. % % @tbd Deal with copied config files. We can base this on % config.done and maybe on the module name. % @tbd Update config.done. remove_config(Dir, Options) :- conf_d_enabled(ConfigEnabled), entry_paths(ConfigEnabled, Paths), maplist(remove_config(Dir, Options), Paths). remove_config(PackDir, Options, File) :- read_link(File, _, Target), absolute_file_name(Target, CanonicalTarget), sub_atom(CanonicalTarget, 0, _, _, PackDir), !, action(delete_file(File), Options). remove_config(PackDir, Options, PlFile) :- file_name_extension(_, pl, PlFile), setup_call_cleanup(open(PlFile, read, In), read(In, Term0), close(In)), Term0 = (:- consult(Rel)), absolute_file_name(Rel, Target, [ relative_to(PlFile) ]), sub_atom(Target, 0, _, _, PackDir), !, action(delete_file(PlFile), Options). remove_config(_, _, _). %! remove_dir(+Dir, Options) % % Removes a directory recursively. remove_dir(Link, Options) :- read_link(Link, _, _), !, action(delete_file(Link), Options). remove_dir(Dir, Options) :- exists_directory(Dir), !, entry_paths(Dir, Paths), forall(member(P, Paths), remove_dir(P, Options)), action(delete_directory(Dir), Options). remove_dir(File, Options) :- action(delete_file(File), Options). entry_paths(Dir, Paths) :- directory_files(Dir, Entries), entry_paths(Entries, Dir, Paths). entry_paths([], _, []). entry_paths([H|T0], Dir, T) :- hidden(H), !, entry_paths(T0, Dir, T). entry_paths([H|T0], Dir, [P|T]) :- directory_file_path(Dir, H, P), entry_paths(T0, Dir, T). hidden(.). hidden(..). :- meta_predicate action(0, +). action(G, Options) :- option(fake(true), Options), !, print_message(informational, cpack(action(G))). action(G, _) :- G. /******************************* * REGISTRATION * *******************************/ %! cpack_register(+PackName, +Dir, +Options) % % Attach a CPACK to the search paths cpack_register(PackName, Dir, Options) :- throw(error(context_error(nodirective, cpack_register(PackName, Dir, Options)), _)). user:term_expansion((:-cpack_register(PackName, Dir0, Options)), Clauses) :- full_dir(Dir0, Dir), Term =.. [PackName,'.'], Clauses = [ user:file_search_path(PackName, Dir), user:file_search_path(cpacks, Term), cpack:registered_cpack(PackName, Dir, Options) ]. full_dir(Dir, Dir) :- compound(Dir), !. full_dir(Dir, Dir) :- is_absolute_file_name(Dir), !. full_dir(Dir, AbsDir) :- prolog_load_context(directory, ConfigEnabled), file_directory_name(ConfigEnabled, RelTo), absolute_file_name(Dir, AbsDir, [ relative_to(RelTo), file_type(directory), access(exist) ]). :- multifile registered_cpack/3. %! current_cpack(-Name) is nondet. % % True when Name is the name of a registered package. current_cpack(Name) :- registered_cpack(Name, _, _). %! cpack_property(Name, Property) is nondet. % % True when Property is a property of the CPACK Name. Defined % properties are: % % * directory(Dir) cpack_property(Name, Property) :- property_cpack(Property, Name). property_cpack(directory(Dir), Name) :- registered_cpack(Name, LocalDir, _), absolute_file_name(LocalDir, Dir). property_cpack(Option, Name) :- registered_cpack(Name, _, Options), member(Option, Options). %! prolog_version:git_module_hook(?Name, ?Directory, ?Options) is %! nondet. % % Make packages available for the version management implemented % by library(version). :- multifile prolog_version:git_module_hook/3. prolog_version:git_module_hook(Name, Directory, Options) :- registered_cpack(Name, LocalDir, Options), absolute_file_name(LocalDir, Directory). /******************************* * CREATE NEW PACKAGES * *******************************/ %! cpack_create(+Name, +Title, +Options) is det. % % Create a new package. Options include % % * type(Type) % Label of a subclass of cpack:Package. Default is =package= % * title(Title) % Title for the package. Should be a short line. % * foafname(FoafName) % foaf:name to put into the default template % * foafmbox(Email) % foaf:mbox to put into the default template % % Default options are extracted from the cpack:Profile named % =default= % % @tbd Allow selection profile, auto-loading of profile, etc. cpack_create(Name, Title, Options) :- cpack_load_schema, cpack_load_profile, option(type(Type), Options, package), option(description(Descr), Options, 'Package description goes here. You can use markdown.'), package_class_id(Type, PkgClass), default_bindings(default, Name, DefaultBindings), merge_options(Options, [ name(Name), title(Title), pkgclass(PkgClass), description(Descr) | DefaultBindings ], Vars), cpack_package_dir(Name, Dir, true), forall(cpack_dir(SubDir, Type), make_cpack_dir(Dir, SubDir)), forall(cpack_template(In, Out), install_template_file(In, Out, Vars)), git([init], [directory(Dir)]), git([add, '.'], [directory(Dir)]), git([commit, '-m', 'Installed template'], [directory(Dir)]), git([tag, epoch], [directory(Dir)]), git_setup_push(Dir, Vars). package_class_id(Label, TurtleID) :- package_class(Label, Class), rdf_global_id(Prefix:Name, Class), atomic_list_concat([Prefix, :, Name], TurtleID). package_class(Label, Class) :- rdf_has(Class, rdfs:label, literal(Label)), rdfs_subclass_of(Class, cpack:'Package'), !. package_class(Label, _) :- domain_error(package_class, Label). default_bindings(Profile, Name, Bindings) :- findall(B, default_binding(Profile, Name, B), Bindings). default_binding(ProfileName, Name, B) :- rdf_has(Profile, cpack:name, literal(ProfileName)), ( rdf_has(Profile, cpack:defaultAuthor, Author), ( rdf_has(Author, foaf:name, literal(AuthorName)), B = foafname(AuthorName) ; rdf_has(Author, foaf:mbox, MBOX), B = foafmbox(MBOX) ) ; rdf_has(Profile, cpack:fetchRepositoryTemplate, literal(GitTempl)), substitute(GitTempl, '@CPACK@', Name, GitRepo), B = fetchrepository(GitRepo) ; rdf_has(Profile, cpack:pushRepositoryTemplate, literal(GitTempl)), substitute(GitTempl, '@CPACK@', Name, GitRepo), B = pushrepository(GitRepo) ). %! git_setup_push(+Dir, +Vars) is det. % % Set an origin for the newly created repository. This also tries % to setup a bare repository at the remote machine using % git_create_origin/2. git_setup_push(Dir, Vars) :- option(pushrepository(PushURL), Vars), !, option(title(Title), Vars, 'ClioPatria CPACK'), git([remote, add, origin, PushURL], [directory(Dir)]), directory_file_path(Dir, '.git/config', Config), setup_call_cleanup(open(Config, append, Out), format(Out, '[branch "master"]\n\c \tremote = origin\n\c \tmerge = refs/heads/master\n', []), close(Out)), catch(git_create_origin(Dir, PushURL, Title), E, true), ( var(E) -> true ; subsumes_term(error(existence_error(source_sink, path(Exe)), _), E) -> print_message(error, cpack(missing_program(Exe))) ; print_message(error, E) ). git_setup_push(_,_). %! git_create_origin(+Dir, +PushURL, +Title) is det. % % Try to create the repository origin. As the user has setup push, % we hope he setup SSH appropriately. Note that this only works if % the remote user has a real shell and not a git-shell. % % When using GitHub, PushURL is % % == % git@github.com:/@CPACK@.git % https://github.com//@CPACK@.git % == git_create_origin(Dir, PushURL, Title) :- ( atom_concat('git@github.com:', UserPath, PushURL) -> true ; atom_concat('https://github.com/', UserPath, PushURL) ), atomic_list_concat([_User, RepoGit], /, UserPath), file_name_extension(Repo, git, RepoGit), !, process_create(path(hub), [create, Repo, '-d', Title], [ cwd(Dir) ]). git_create_origin(_Dir, PushURL, Title) :- uri_components(PushURL, Components), uri_data(scheme, Components, Scheme), ( Scheme == ssh -> uri_data(authority, Components, Authority) ; Authority = Scheme ), uri_data(path, Components, Path), file_directory_name(Path, Parent), file_base_name(Path, Repo), format(atom(Command), 'cd "~w" && mkdir "~w" && cd "~w" && \c git init --bare && echo "~w" > description && \c touch git-daemon-export-ok', [Parent, Repo, Repo, Title]), process_create(path(ssh), [ Authority, Command ], []). %! make_cpack_dir(+BaseDir, +CPACKDir) is det. % % Setup th directory structure for a new package. make_cpack_dir(Dir, SubDir) :- directory_file_path(Dir, SubDir, New), ( exists_directory(New) -> true ; make_directory_path(New), print_message(informational, cpack(create_directory(New))) ). install_template_file(In, Out, Vars) :- option(name(Name), Vars), absolute_file_name(In, InFile, [access(read)]), substitute(Out, '@NAME@', Name, OutFile), cpack_package_dir(Name, Dir, true), directory_file_path(Dir, OutFile, OutPath), copy_file_with_vars(InFile, OutPath, Vars), print_message(informational, cpack(installed_template(OutFile))). substitute(In, From, To, Out) :- sub_atom(In, B, _, A, From), !, sub_atom(In, 0, B, _, Start), sub_atom(In, _, A, 0, End), atomic_list_concat([Start, To, End], Out). substitute(In, _, _, In). cpack_dir('rdf', _). cpack_dir('rdf/cpack', _). cpack_dir('config-available', _). cpack_dir('entailment', _). cpack_dir('applications', _). cpack_dir('api', _). cpack_dir('components', _). cpack_dir('skin', _). cpack_dir('lib', _). cpack_dir('web', _). cpack_dir('web/js', _). cpack_dir('web/css', _). cpack_dir('web/html', _). cpack_template(library('cpack/config-available.pl.in'), 'config-available/@NAME@.pl'). cpack_template(library('cpack/DEFAULTS.in'), 'config-available/DEFAULTS'). cpack_template(library('cpack/pack.ttl.in'), 'rdf/cpack/@NAME@.ttl'). cpack_template(library('cpack/README.md.in'), 'README.md'). /******************************* * PROFILE * *******************************/ %! cpack_load_profile is det. % % Try to load the profile from user_profile('.cpack.ttl'). % % @tbd Prompt for a default profile (notably fill in the servers). cpack_load_profile :- absolute_file_name(user_profile('.cpack.ttl'), Path, [ access(read), file_errors(fail) ]), !, rdf_load(Path). cpack_load_profile. %! cpack_load_schema % % Ensure the CPACK schema data is loaded. cpack_load_schema :- rdf_attach_library(rdf(cpack)), rdf_load_library(cpack). /******************************* * UTIL * *******************************/ %! cpack_package_dir(+PackageName, -Dir, +Create) % % Installation directory for Package cpack_package_dir(Name, Dir, Create) :- setting(cpack:package_directory, PackageDir), directory_file_path(PackageDir, Name, Dir), ( ( Create == false ; exists_directory(Dir) ) -> true ; make_directory_path(Dir) ). :- multifile prolog:message//1, prolog:error_message//1. prolog:message(cpack(Message)) --> message(Message). message(create_directory(New)) --> [ 'Created directory ~w'-[New] ]. message(installed_template(File)) --> [ 'Installed template ~w'-[File] ]. message(requires(Name, Packages)) --> ( { is_list(Name) } -> [ 'Packages ~w require the following packages:'-[Name] ] ; [ 'Package ~w requires the following packages:'-[Name] ] ), sub_packages(Packages), [ nl, 'Querying package status ...'-[] ]. message(no_change(Name, Version)) --> [ ' ~w: ~t~30|no change (~w)'-[Name, Version] ]. message(upgrade(Name, Old, New)) --> [ ' ~w: ~t~30|upgrading (~w..~w) ...'-[Name, Old, New] ]. message(download(Name, git(Url, _))) --> [ ' ~w: ~t~30|downloading from ~w ...'-[Name, Url] ]. message(probe(URL)) --> [ 'Trying CPACK server at ~w ...'-[URL] ]. message(probe_remote(URL)) --> [ 'Checking availability of GIT repository ~w ...'-[URL] ]. message(add_remote(Name, URL)) --> [ 'Running "git remote add ~w ~w ..."'-[Name, URL] ]. message(action(G)) --> [ '~q'-[G] ]. message(missing_program(hub)) --> !, [ 'Cannot find the GitHub command line utility "hub".'-[], nl, 'See https://hub.github.com/ for installation instructions'-[] ]. message(missing_program(Prog)) --> [ 'Cannot find helper program "~w".'-[Prog] ]. sub_packages([]) --> []. sub_packages([H|T]) --> sub_package(H), sub_packages(T). sub_package(cpack(Name, Options)) --> { option(title(Title), Options) }, !, [ nl, ' ~w: ~t~30|~w'-[Name, Title] ]. sub_package(cpack(Name, _)) --> [ nl, ' ~w: ~t~30|~w'-[Name] ]. prolog:error_message(cpack_error(Error)) --> cpack_error(Error). cpack_error(not_satisfied(Pack, Reasons)) --> [ 'Package not satisfied: ~p'-[Pack] ], not_satisfied_list(Reasons). cpack_error(cannot_remove(Pack, Dependents)) --> [ 'Cannot remove "~p" because the following packs depend on it'-[Pack] ], pack_list(Dependents). not_satisfied_list([]) --> []. not_satisfied_list([H|T]) --> not_satisfied(H), not_satisfied_list(T). not_satisfied(no_token(Token)) --> [ nl, ' Explicit requirement not found: ~w'-[Token] ]. not_satisfied(file(File, Problems)) --> [ nl, ' File ~p'-[File] ], file_problems(Problems). file_problems([]) --> []. file_problems([H|T]) --> file_problem(H), file_problems(T). file_problem(predicate_not_found(PI)) --> [ nl, ' Predicate not resolved: ~w'-[PI] ]. pack_list([]) --> []. pack_list([H|T]) --> [ nl, ' ~p'-[H] ], pack_list(T).