[Rpm-maint] RFC: Collections in packages, take 0.1
Panu Matilainen
pmatilai at laiskiainen.org
Thu Jun 24 12:02:07 UTC 2010
Hi,
Attached are two early rough-cut RFC patches to move collections ownership
into packages and handle them as a special case of dependencies. Lots of
loose ends and unanswered questions remain (I tried to document these in
the patch comments), but it appears to work for my rather trivial
testcase:
This added to Fedora's desktop-file-utils package:
%collection -c desktop-database -p exec.so
/usr/bin/update-desktop-database /usr/share/applications
..and then "Collections: desktop-database" added to package(s) which drop
stuff to /usr/share/applications.
These add Requires and Provides "collection(desktop-database)" as
appropriate with a new special RPMSENSE_COLLECTION flag behind the scenes,
and as they now appear as mostly regular dependencies, they're
discoverable by yum & the like and get ordered by the normal rules, which
should be enough at least for the "normal" cases. Selinux might still want
something special...
Just FYI: it's Midsummer festival weekend here in Finland (going to
countryside to feed the mosquitos...), so its unlikely that I'm able to
further comment/discuss until Monday.
- Panu -
-------------- next part --------------
From 26b5923f7c67cbdbfd30a46477bbc25705d74717 Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai at redhat.com>
Date: Thu, 24 Jun 2010 13:23:13 +0300
Subject: [PATCH 1/2] Add rough-cut %collection parsing + related tags'n stuff
- preliminaries for supporting package-provided collections,
%collection -c <collection_name> [-p <interpreter>] [-n] <pkg (sub)name
# collection script / plugin arguments goes here
- add new tags for storing the collection "interpreter" etc
- add collection(name) provides+requires to packages, marked with
new RPMSENSE_COLLECTION for easy detection
---
build/Makefile.am | 3 +-
build/parseCollection.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++
build/parsePreamble.c | 8 ++-
build/parseSpec.c | 5 ++
build/rpmbuild.h | 10 +++-
lib/rpmds.c | 3 +
lib/rpmds.h | 3 +-
tests/rpmgeneral.at | 3 +
8 files changed, 193 insertions(+), 4 deletions(-)
create mode 100644 build/parseCollection.c
diff --git a/build/Makefile.am b/build/Makefile.am
index f5ed2f6..afec2e6 100644
--- a/build/Makefile.am
+++ b/build/Makefile.am
@@ -11,7 +11,8 @@ usrlib_LTLIBRARIES = librpmbuild.la
librpmbuild_la_SOURCES = \
build.c buildio.h expression.c files.c misc.c names.c pack.c \
parseBuildInstallClean.c parseChangelog.c parseDescription.c \
- parseFiles.c parsePreamble.c parsePrep.c parseReqs.c parseScript.c \
+ parseFiles.c parsePreamble.c parsePrep.c parseReqs.c \
+ parseScript.c parseCollection.c \
parseSpec.c poptBT.c reqprov.c rpmfc.c spec.c fts.h fts.c
librpmbuild_la_LDFLAGS = -version-info 1:0:0
diff --git a/build/parseCollection.c b/build/parseCollection.c
new file mode 100644
index 0000000..8a7d715
--- /dev/null
+++ b/build/parseCollection.c
@@ -0,0 +1,162 @@
+/** \ingroup rpmbuild
+ * \file build/parseCollection.c
+ * Parse %collection section from spec file.
+ */
+
+#include "system.h"
+
+#include <rpm/header.h>
+#include <rpm/rpmbuild.h>
+#include <rpm/rpmlog.h>
+#include "debug.h"
+
+int parseCollection(rpmSpec spec)
+{
+ int nextPart = PART_ERROR; /* assume error */
+ StringBuf sb;
+ int flag = PART_SUBNAME;
+ Package pkg;
+ int rc, argc;
+ int arg;
+ const char **argv = NULL;
+ const char *name = NULL;
+ const char *prog = NULL;
+ const char *coll = NULL;
+ const char *script = NULL;
+ poptContext optCon = NULL;
+ struct poptOption optionsTable[] = {
+ { NULL, 'p', POPT_ARG_STRING, &prog, 'p', NULL, NULL},
+ { NULL, 'n', POPT_ARG_STRING, &name, 'n', NULL, NULL},
+ { NULL, 'c', POPT_ARG_STRING, &coll, 'c', NULL, NULL},
+ { 0, 0, 0, 0, 0, NULL, NULL}
+ };
+
+ if ((rc = poptParseArgvString(spec->line, &argc, &argv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing %%collection: %s\n"),
+ spec->lineNum, poptStrerror(rc));
+ return PART_ERROR;
+ }
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((arg = poptGetNextOpt(optCon)) > 0) {
+ switch (arg) {
+ case 'p':
+ /* assume plugins don't start with '/' */
+ /* XXX we don't support non-plugin collections yet, bail out */
+ if (prog[0] == '/') {
+ rpmlog(RPMLOG_ERR, "collection scripts not supported yet\n");
+ goto exit;
+ }
+ break;
+ case 'n':
+ flag = PART_NAME;
+ break;
+ }
+ }
+
+ if (arg < -1) {
+ rpmlog(RPMLOG_ERR, _("line %d: Bad option %s: %s\n"),
+ spec->lineNum,
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ spec->line);
+ goto exit;
+ }
+
+ if (coll == NULL) {
+ rpmlog(RPMLOG_ERR, _("line %d: collection name missing: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+
+ if (poptPeekArg(optCon)) {
+ if (name == NULL)
+ name = poptGetArg(optCon);
+ if (poptPeekArg(optCon)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Too many names: %s\n"),
+ spec->lineNum,
+ spec->line);
+ goto exit;
+ }
+ }
+
+ if (lookupPackage(spec, name, flag, &pkg)) {
+ rpmlog(RPMLOG_ERR, _("line %d: Package does not exist: %s\n"),
+ spec->lineNum, spec->line);
+ goto exit;
+ }
+
+ sb = newStringBuf();
+
+ if ((rc = readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ } else if (rc < 0) {
+ nextPart = PART_ERROR;
+ goto exit;
+ } else {
+ while (! (nextPart = isPart(spec->line))) {
+ appendLineStringBuf(sb, spec->line);
+ if ((rc =
+ readLine(spec, STRIP_TRAILINGSPACE | STRIP_COMMENTS)) > 0) {
+ nextPart = PART_NONE;
+ break;
+ } else if (rc < 0) {
+ nextPart = PART_ERROR;
+ goto exit;
+ }
+ }
+ }
+
+ stripTrailingBlanksStringBuf(sb);
+
+ if (prog) {
+ const char **progArgv = NULL;
+ int progArgc;
+ if ((rc = poptParseArgvString(prog, &progArgc, &progArgv))) {
+ rpmlog(RPMLOG_ERR, _("line %d: Error parsing collection: %s\n"),
+ spec->lineNum, poptStrerror(rc));
+ goto exit;
+ }
+ headerPutStringArray(pkg->header, RPMTAG_COLLPROG, progArgv, progArgc);
+
+ /* add dependency for collection "interpreter" (plugin or other) */
+ if (*progArgv[0] != '/') {
+#if 0
+ /* XXX what to do about built-in plugins? */
+ /* XXX how to handle eg exec plugin argument depedencies? */
+
+ char *plugin = rpmGetPath("%{__plugindir}", progArgv[0], NULL);
+ addReqProv(spec, pkg->header, RPMTAG_REQUIRENAME,
+ plugin, NULL, RPMSENSE_INTERP, 0);
+ free(plugin);
+
+#endif
+ } else {
+ addReqProv(spec, pkg->header, RPMTAG_REQUIRENAME,
+ progArgv[0], NULL, RPMSENSE_INTERP, 0);
+ }
+ }
+
+ /* XXX plugin args added as the script, does this make sense? */
+ script = getStringBuf(sb);
+ if (!rstreq(script, "")) {
+ headerPutString(pkg->header, RPMTAG_COLLSCRIPT, script);
+ }
+
+ /* XXX TODO: collection flags */
+
+ if (prog || script) {
+ char * dep = NULL;
+ rasprintf(&dep, "collection(%s)", coll);
+ addReqProv(spec, pkg->header, RPMTAG_PROVIDENAME,
+ dep, NULL, RPMSENSE_COLLECTION, 0);
+ free(dep);
+ rpmlibNeedsFeature(pkg->header, "Collections", "4.9.0-1");
+ }
+
+ sb = freeStringBuf(sb);
+
+exit:
+ argv = _free(argv);
+ optCon = poptFreeContext(optCon);
+ return nextPart;
+}
diff --git a/build/parsePreamble.c b/build/parsePreamble.c
index b2a4670..55dc328 100644
--- a/build/parsePreamble.c
+++ b/build/parsePreamble.c
@@ -663,9 +663,15 @@ static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
spec->BANames = _free(spec->BANames);
break;
}
- case RPMTAG_COLLECTIONS:
+ case RPMTAG_COLLECTIONS: {
+ char * dep = rstrscat(NULL, "collection(", field, ")", NULL);
+ addReqProv(spec, pkg->header, RPMTAG_REQUIRENAME,
+ dep, NULL, RPMSENSE_COLLECTION, 0);
+ free(dep);
+ rpmlibNeedsFeature(pkg->header, "Collections", "4.9.0-1");
addOrAppendListEntry(pkg->header, tag, field);
break;
+ }
default:
rpmlog(RPMLOG_ERR, _("Internal error: Bogus tag %d\n"), tag);
return RPMRC_FAIL;
diff --git a/build/parseSpec.c b/build/parseSpec.c
index f45022b..96e2e3d 100644
--- a/build/parseSpec.c
+++ b/build/parseSpec.c
@@ -47,6 +47,7 @@ static const struct PartRec {
{ PART_TRIGGERIN, LEN_AND_STR("%triggerin")},
{ PART_TRIGGERIN, LEN_AND_STR("%trigger")},
{ PART_VERIFYSCRIPT, LEN_AND_STR("%verifyscript")},
+ { PART_COLLECTION, LEN_AND_STR("%collection")},
{0, 0, 0}
};
@@ -474,6 +475,10 @@ int parseSpec(rpmts ts, const char *specFile, const char *rootDir,
parsePart = parseDescription(spec);
break;
+ case PART_COLLECTION:
+ parsePart = parseCollection(spec);
+ break;
+
case PART_PRE:
case PART_POST:
case PART_PREUN:
diff --git a/build/rpmbuild.h b/build/rpmbuild.h
index c7d0da3..540f1b1 100644
--- a/build/rpmbuild.h
+++ b/build/rpmbuild.h
@@ -73,7 +73,8 @@ typedef enum rpmParseState_e {
PART_BUILDARCHITECTURES= 29+PART_BASE,/*!< */
PART_TRIGGERPOSTUN = 30+PART_BASE, /*!< */
PART_TRIGGERPREIN = 31+PART_BASE, /*!< */
- PART_LAST = 32+PART_BASE /*!< */
+ PART_COLLECTION = 32+PART_BASE, /*!< */
+ PART_LAST = 33+PART_BASE /*!< */
} rpmParseState;
@@ -267,6 +268,13 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char * field, rpmTag tagN,
int parseScript(rpmSpec spec, int parsePart);
/** \ingroup rpmbuild
+ * Parse %collection from a spec file.
+ * @param spec spec file control structure
+ * @return >= 0 next rpmParseState, < 0 on error
+ */
+int parseCollection(rpmSpec spec);
+
+/** \ingroup rpmbuild
* Evaluate boolean expression.
* @param spec spec file control structure
* @param expr expression to parse
diff --git a/lib/rpmds.c b/lib/rpmds.c
index 8157fd3..dfc4c94 100644
--- a/lib/rpmds.c
+++ b/lib/rpmds.c
@@ -900,6 +900,9 @@ static const struct rpmlibProvides_s rpmlibProvides[] = {
{ "rpmlib(ScriptletExpansion)", "4.9.0-1",
( RPMSENSE_EQUAL),
N_("package scriptlets can be expanded at install time.") },
+ { "rpmlib(Collections)", "4.9.0-1",
+ ( RPMSENSE_EQUAL),
+ N_("support for collections") },
{ NULL, NULL, 0, NULL }
};
diff --git a/lib/rpmds.h b/lib/rpmds.h
index 7525020..d99add9 100644
--- a/lib/rpmds.h
+++ b/lib/rpmds.h
@@ -47,7 +47,7 @@ typedef enum rpmsenseFlags_e {
RPMSENSE_RPMLIB = (1 << 24), /*!< rpmlib(feature) dependency. */
RPMSENSE_TRIGGERPREIN = (1 << 25), /*!< %triggerprein dependency. */
RPMSENSE_KEYRING = (1 << 26),
- /* bit 27 unused */
+ RPMSENSE_COLLECTION = (1 << 27), /*!< collection(name) dependency. */
RPMSENSE_CONFIG = (1 << 28)
} rpmsenseFlags;
@@ -67,6 +67,7 @@ typedef enum rpmsenseFlags_e {
RPMSENSE_FIND_REQUIRES | \
RPMSENSE_RPMLIB | \
RPMSENSE_KEYRING | \
+ RPMSENSE_COLLECTION | \
RPMSENSE_PRETRANS | \
RPMSENSE_PREREQ)
diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at
index 4c33f29..ad2a848 100644
--- a/tests/rpmgeneral.at
+++ b/tests/rpmgeneral.at
@@ -81,6 +81,9 @@ CHANGELOGTEXT
CHANGELOGTIME
CLASSDICT
COLLECTIONS
+COLLFLAGS
+COLLPROG
+COLLSCRIPT
CONFLICTFLAGS
CONFLICTNAME
CONFLICTS
--
1.7.0.1
-------------- next part --------------
From 7df1115f1e55192da1959c793fd094d2af2ecb5d Mon Sep 17 00:00:00 2001
From: Panu Matilainen <pmatilai at redhat.com>
Date: Thu, 24 Jun 2010 14:33:24 +0300
Subject: [PATCH 2/2] Fetch and execute collections from package headers, take 0.1
- RPMTAG_COLLECTIONS is now only used internally by spec parsing, collections
subscription is detected from requires
- Instead of expanding macros, pull the collection "interpreter" and
its arguments from installed headers. Collection provider has to get
installed before packages belonging to it - otherwise either ordering
failed to do its job or --nodeps/--noorder was used.
- Only plugins supported for now.
- All sorts of loose ends and unanswered questions remain.
---
build/parsePreamble.c | 1 -
lib/formats.c | 2 +
lib/order.c | 7 +++++
lib/rpmtag.h | 5 +++-
lib/rpmte.c | 65 ++++++++++++++++++++++++++++++-------------------
macros.in | 2 -
tests/rpmgeneral.at | 1 -
7 files changed, 53 insertions(+), 30 deletions(-)
diff --git a/build/parsePreamble.c b/build/parsePreamble.c
index 55dc328..8a07f79 100644
--- a/build/parsePreamble.c
+++ b/build/parsePreamble.c
@@ -669,7 +669,6 @@ static int handlePreambleTag(rpmSpec spec, Package pkg, rpmTag tag,
dep, NULL, RPMSENSE_COLLECTION, 0);
free(dep);
rpmlibNeedsFeature(pkg->header, "Collections", "4.9.0-1");
- addOrAppendListEntry(pkg->header, tag, field);
break;
}
default:
diff --git a/lib/formats.c b/lib/formats.c
index 499c5d0..ce07ea1 100644
--- a/lib/formats.c
+++ b/lib/formats.c
@@ -257,6 +257,8 @@ static char * deptypeFormat(rpmtd td, char * formatPrefix)
argvAdd(&sdeps, "prereq");
if (item & RPMSENSE_PRETRANS)
argvAdd(&sdeps, "pretrans");
+ if (item & RPMSENSE_COLLECTION)
+ argvAdd(&sdeps, "collection");
if (sdeps) {
val = argvJoin(sdeps, ",");
diff --git a/lib/order.c b/lib/order.c
index 7e77834..adcd678 100644
--- a/lib/order.c
+++ b/lib/order.c
@@ -245,6 +245,12 @@ static inline int addRelation(rpmts ts,
addSingleRelation(p, q, dsflags);
+ /*
+ * Disabled for now, collection dependencies should take care of this...
+ * or it needs to be controlled by additional flags to avoid creating
+ * gigantic dependency loops in the common cases.
+ */
+#if 0
/* If q is a member of any collections, make sure p requires all packages
* that are also in those collections */
for (qcolls = rpmteCollections(q); qcolls && *qcolls; qcolls++) {
@@ -255,6 +261,7 @@ static inline int addRelation(rpmts ts,
}
_free(tes);
}
+#endif
return 0;
}
diff --git a/lib/rpmtag.h b/lib/rpmtag.h
index 3aaebfa..aefe345 100644
--- a/lib/rpmtag.h
+++ b/lib/rpmtag.h
@@ -296,7 +296,10 @@ typedef enum rpmTag_e {
RPMTAG_VERIFYSCRIPTFLAGS = 5026, /* i */
RPMTAG_TRIGGERSCRIPTFLAGS = 5027, /* i[] */
RPMTAG_FILESTATUS = 5028, /* i[] extension */
- RPMTAG_COLLECTIONS = 5029, /* s[] list of collections */
+ RPMTAG_COLLECTIONS = 5029, /* s[] internal */
+ RPMTAG_COLLPROG = 5030, /* s[] */
+ RPMTAG_COLLSCRIPT = 5031, /* s */
+ RPMTAG_COLLFLAGS = 5032, /* i */
RPMTAG_FIRSTFREE_TAG /*!< internal */
} rpmTag;
diff --git a/lib/rpmte.c b/lib/rpmte.c
index ee86957..2b0a3ec 100644
--- a/lib/rpmte.c
+++ b/lib/rpmte.c
@@ -191,8 +191,6 @@ static void buildRelocs(rpmte p, Header h, rpmRelocation *relocs)
*/
static void addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs)
{
- struct rpmtd_s colls;
-
p->name = headerGetAsString(h, RPMTAG_NAME);
p->version = headerGetAsString(h, RPMTAG_VERSION);
p->release = headerGetAsString(h, RPMTAG_RELEASE);
@@ -240,14 +238,18 @@ static void addTE(rpmte p, Header h, fnpyKey key, rpmRelocation * relocs)
p->lastInCollectionsAdd = NULL;
p->firstInCollectionsRemove = NULL;
p->collections = NULL;
- if (headerGet(h, RPMTAG_COLLECTIONS, &colls, HEADERGET_MINMEM)) {
- const char *collname;
- while ((collname = rpmtdNextString(&colls))) {
- argvAdd(&p->collections, collname);
+
+ /* Find the collections this package belongs to */
+ rpmdsInit(p->requires);
+ while (rpmdsNext(p->requires) >= 0) {
+ if (rpmdsFlags(p->requires) & RPMSENSE_COLLECTION) {
+ rpmlog(RPMLOG_DEBUG, "adding %s to collection %s\n",
+ rpmteNEVRA(p), rpmdsN(p->requires));
+ argvAdd(&p->collections, rpmdsN(p->requires));
}
- argvSort(p->collections, NULL);
- rpmtdFreeData(&colls);
}
+ argvSort(p->collections, NULL);
+
rpmteColorDS(p, RPMTAG_PROVIDENAME);
rpmteColorDS(p, RPMTAG_REQUIRENAME);
@@ -826,34 +828,45 @@ static int rpmteRunCollection(rpmte te, const char *collname,
collHookFunc hookFunc;
const char *hookFuncSym;
rpmCollHook *pluginHooks;
- char *plugin;
- char *options;
+ char *prog = NULL;
+ char *plugin = NULL;
+ char *options = NULL;
char *error;
+ rpmdbMatchIterator mi = NULL;
+ Header h = NULL;
int rc = RPMRC_FAIL;
if (rpmtsFlags(te->ts) & RPMTRANS_FLAG_NOCOLLECTIONS) {
return RPMRC_OK;
}
- plugin = rpmExpand("%{?__collection_", collname, "}", NULL);
- if (!plugin || rstreq(plugin, "")) {
- rpmlog(RPMLOG_ERR, _("Failed to expand %%__collection_%s macro\n"),
+ /*
+ * Find collection provider by its name from the rpmdb. If it's not
+ * there at this point, either ordering made a mess or --nodeps is
+ * being used. Or so the theory goes...
+ * TODO: What to do in presence of multiple providers? This simply
+ * picks whatever comes first.
+ * TODO: make sure the provider has RPMSENSE_COLLECTION set
+ */
+ mi = rpmtsInitIterator(te->ts, RPMTAG_PROVIDENAME, collname, 0);
+ while ((h = rpmdbNextIterator(mi)) != NULL) {
+ prog = headerGetAsString(h, RPMTAG_COLLPROG);
+ options = headerGetAsString(h, RPMTAG_COLLSCRIPT);
+ break;
+ }
+ rpmdbFreeIterator(mi);
+
+ if (h == NULL || prog == NULL) {
+ rpmlog(RPMLOG_ERR, _("Collection provider for %s missing!\n"),
collname);
goto exit;
}
- /* split the options from the plugin string */
-#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
-#define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
- options = plugin;
- SKIPNONSPACE(options);
- if (risspace(*options)) {
- *options = '\0';
- options++;
- SKIPSPACE(options);
- }
- if (*options == '\0') {
- options = NULL;
+ if (prog[0] == '/') {
+ /* XXX no script support yet */
+ goto exit;
+ } else {
+ plugin = rpmGetPath("%{__plugindir}/", prog, NULL);
}
handle = dlopen(plugin, RTLD_LAZY);
@@ -902,6 +915,7 @@ static int rpmteRunCollection(rpmte te, const char *collname,
/* don't perform the action if --test or --justdb are set */
rc = RPMRC_OK;
} else {
+ rpmlog(RPMLOG_DEBUG, "executing %s %s %s\n", collname, plugin, options);
rc = (*hookFunc) (te->ts, collname, options);
}
@@ -909,6 +923,7 @@ static int rpmteRunCollection(rpmte te, const char *collname,
if (handle)
dlclose(handle);
_free(plugin);
+ _free(options);
return rc;
}
diff --git a/macros.in b/macros.in
index bf49d04..07497de 100644
--- a/macros.in
+++ b/macros.in
@@ -1162,8 +1162,6 @@ done \
#------------------------------------------------------------------------------
# Collection specific macros
%__plugindir %{_rpmconfigdir}/plugins
-%__collection_font %{__plugindir}/exec.so /usr/bin/fc-cache
-%__collection_java %{__plugindir}/exec.so /usr/bin/rebuild-gcj-db
# \endverbatim
#*/
diff --git a/tests/rpmgeneral.at b/tests/rpmgeneral.at
index ad2a848..e1e95ad 100644
--- a/tests/rpmgeneral.at
+++ b/tests/rpmgeneral.at
@@ -80,7 +80,6 @@ CHANGELOGNAME
CHANGELOGTEXT
CHANGELOGTIME
CLASSDICT
-COLLECTIONS
COLLFLAGS
COLLPROG
COLLSCRIPT
--
1.7.0.1
More information about the Rpm-maint
mailing list