2 * Parses RPM spec file into Makefile fragment. See
4 * http://fedora.redhat.com/docs/drafts/rpm-guide-en/ch-programming-c.html
6 * Mark Huang <mlhuang@cs.princeton.edu>
7 * Copyright (C) 2006 The Trustees of Princeton University
16 #include <rpm/rpmlib.h>
17 #include <rpm/rpmts.h>
18 #include <rpm/rpmcli.h>
19 #include <rpm/rpmbuild.h>
20 #include <rpm/rpmspec.h>
22 /* from f10 and up, Spec is renamed rpmSpec */
27 #define MAX_WHITELIST_SIZE 16
30 #include <linux/limits.h>
32 extern size_t strnlen(const char *s, size_t maxlen);
34 /* the structure describing the options we take and the defaults */
35 static struct poptOption optionsTable[] = {
36 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmBuildPoptTable, 0,
37 "Build options with [ <specfile> | <tarball> | <source package> ]:",
40 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
41 "Common options for all rpm modes and executables:",
49 /* Stolen from rpm/build/spec.c:rpmspecQuery() */
51 rpmspecGet(rpmts ts, const char * arg)
53 char * buildRoot = NULL;
55 char * passPhrase = "";
60 if (parseSpec(ts, arg, "/", buildRoot, recursing, passPhrase,
61 cookie, anyarch, force)) {
62 fprintf(stderr, "query of specfile %s failed, can't parse\n", arg);
70 main(int argc, char *argv[])
76 struct Source *source;
78 const char *name, *version, *release, *arch, *unused;
79 const char *package_name;
81 char **package_whitelist;
83 /* BEGIN: support to pull out --target from the args list */
88 package_whitelist = (char **) malloc(MAX_WHITELIST_SIZE * sizeof(char *));
90 if (!package_whitelist) {
91 perror("Could not allocate package whitelist\n");
95 /* walk argv list looking for options */
96 while ((args+1)<argc) {
97 /* whitelist-rpms are packages that need to be considered even if the parsing logic of spec2make (second half of this file) concludes otherwise */
98 if (strcmp(argv[args],"--whitelist-rpms")==0) {
99 /* Split "whitelist-rpms" which is a comma-separated list, remove --whitelist-rpms <option> from argv */
101 int option_offset = 1;
102 char *whitelist_str = argv[args+1];
103 if (whitelist_str != NULL) {
104 char *saveptr = NULL, *str;
106 for (str = whitelist_str; ; str = NULL) {
108 token = strtok_r(str, "," , &saveptr);
109 if (token == NULL) break;
110 package_whitelist[whitelist_size++] = token;
113 for (i=args;i<argc-2+option_offset;i++) argv[i]=argv[i+option_offset];
116 } else if (strcmp(argv[args],"--target")==0) {
119 /* get arch component of the --target option */
120 dash = (char**)strchr(argv[args+1],'-');
121 if (dash != NULL) *dash=NULL;
123 /* copy arch component of --target option to target */
124 alen = strnlen(argv[args+1],32);
125 target = (char*)malloc(alen+1);
126 if (target == NULL) return errno;
127 strncpy(target,argv[args+1],alen);
130 /* change argc, argv to take out the "--target xxx" */
131 for (i=args;i<argc;i++) argv[i]=argv[i+2];
139 argv[1]=argv[argc-2];
140 argv[2]=argv[argc-1];
143 /* END: support to pull out --target from the args list */
145 /* Parse common options for all rpm modes and executables */
146 context = rpmcliInit(argc, argv, optionsTable);
148 /* Create transaction state */
151 /* Parse spec file. The rpmcli functions don't allow you to
152 * access the Spec structure directly, so we call our own
153 * version of rpmSpecQuery() directly. */
154 spec = rpmspecGet(ts, argv[1]);
155 package_name = argv[2];
162 for (source = spec->sources; source; source = source->next) {
163 char fullSource[PATH_MAX];
165 strncpy(fullSource, source->fullSource, sizeof(fullSource));
166 printf("%s.tarballs += SOURCES/%s\n", package_name, basename(fullSource));
167 /* computes the SOURCEDIR variable by removing .tar.gz or .tar.bz2 */
169 char *suffixes[] = {".tar.gz",".tgz",".tar.bz2", NULL};
173 for (suffix=suffixes ; *suffix ; suffix++) {
174 /*printf("# trying %s\n",*suffix);*/
175 suffix_index=strstr(fullSource,*suffix);
177 char sourcename[PATH_MAX];
178 size_t len = (size_t)(suffix_index-fullSource);
179 strncpy(sourcename,fullSource,len);
180 sourcename[len]='\0';
181 printf ("%s.source := SOURCES/%s\n",package_name,basename(sourcename));
189 /* Get SRPM name from name of first package */
190 pkg = spec->packages;
191 name = version = release = NULL;
192 (void) headerNVR(pkg->header, &name, &version, &release);
193 if (name && version && release)
194 printf("%s.srpm := SRPMS/%s-%s-%s.src.rpm\n",
195 package_name, name, version, release);
197 /* Print non-empty packages */
198 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
200 name = version = release = arch = NULL;
201 (void) headerNEVRA(pkg->header, &name, &unused, &version, &release, &arch);
202 if (name && version && release && arch) {
203 if (target != NULL) {
204 if (strcmp(arch,target)!=0) {
208 /* skip empty packages
210 * Unfortunately, f8 + the RHEL kernel break this bit of cleverness. The following
211 * line returns false for the kernel-devel package even though it is not empty thereby breaking the build.
212 * Rather than unfolding the kernel package macros in the current specfile,
213 * this hack should work till f8 dies its natural death.
214 * To add rpms that are exempted in this way, add a "<package>-WHITELIST-RPMS" tag in the tags file.
217 for (i=0;i<whitelist_size;i++) if (strncmp(package_whitelist[i], name, strlen(name)) == 0) force = 1;
219 if (pkg->fileList || force) {
220 /* attach (add) rpm path to package */
221 printf("%s.rpms += RPMS/%s/%s-%s-%s.%s.rpm\n",
222 package_name, arch, name, version, release, arch);
224 printf("%s.rpmnames += %s\n",
226 /* attach path to rpm name */
227 printf("%s.rpm-path := RPMS/%s/%s-%s-%s.%s.rpm\n",
228 name,arch, name, version, release, arch);
229 /* attach package to rpm name for backward resolution - should be unique */
230 printf("%s.package := %s\n",
236 /* export some macros to make */
237 /* note : this relies on pl-specific conventions and might be wrong */
239 char *macros[] = { "release" , "name" , "version" , "taglevel" , NULL } ;
241 char *macro=malloc(32);
242 for (nav=macros; *nav; nav++) {
243 sprintf(macro,"%%{%s}",*nav);
244 char *value = rpmExpand(macro,NULL);
245 printf ("%s.rpm-%s := %s\n",package_name,*nav,value);
250 printf ("%s.rpm-arch := %s\n",package_name,target);
252 spec = freeSpec(spec);
256 context = rpmcliFini(context);