Setting tag sfa-1.0-35
[build.git] / spec2make.c
1 /*
2  * Parses RPM spec file into Makefile fragment. See
3  *
4  * http://fedora.redhat.com/docs/drafts/rpm-guide-en/ch-programming-c.html
5  *
6  * Mark Huang <mlhuang@cs.princeton.edu>
7  * Copyright (C) 2006 The Trustees of Princeton University
8  *
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <libgen.h>
15 #include <errno.h>
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>
21
22 /* from f10 and up, Spec is renamed rpmSpec */
23 #ifndef _RPMTYPES_H
24 #define rpmSpec Spec
25 #endif
26
27 #define         MAX_WHITELIST_SIZE      16
28
29 #ifndef PATH_MAX
30 #include <linux/limits.h>
31 #endif
32 extern size_t strnlen(const char *s, size_t maxlen);
33
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> ]:",
38     NULL },
39  
40   { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
41     "Common options for all rpm modes and executables:",
42     NULL },
43
44   POPT_AUTOALIAS
45   POPT_AUTOHELP
46   POPT_TABLEEND
47 };
48
49 /* Stolen from rpm/build/spec.c:rpmspecQuery() */
50 rpmSpec
51 rpmspecGet(rpmts ts, const char * arg)
52 {
53   char * buildRoot = NULL;
54   int recursing = 0;
55   char * passPhrase = "";
56   char *cookie = NULL;
57   int anyarch = 1;
58   int force = 1;
59
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);
63     return NULL;
64   }
65
66   return rpmtsSpec(ts);
67 }
68
69 int
70 main(int argc, char *argv[])
71 {
72   poptContext context;
73   rpmts ts = NULL;
74   int ec = 0;
75   rpmSpec spec;
76   struct Source *source;
77   Package pkg;
78   const char *name, *version, *release, *arch, *unused;
79   const char *package_name;
80
81   char **package_whitelist;
82
83   /* BEGIN: support to pull out --target from the args list */
84   int  alen, i;
85   char *target = NULL;
86   int args = 1;
87   int whitelist_size=0;
88   package_whitelist = (char **) malloc(MAX_WHITELIST_SIZE * sizeof(char *));
89
90   if (!package_whitelist) {
91     perror("Could not allocate package whitelist\n");
92     exit(1);
93   }
94
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 */
100
101           int option_offset = 1; 
102           char *whitelist_str = argv[args+1];
103           if (whitelist_str != NULL) {
104               char *saveptr = NULL, *str;
105               option_offset = 2;
106               for (str = whitelist_str; ; str = NULL) {
107                   char *token;
108                   token = strtok_r(str, "," , &saveptr);
109                   if (token == NULL) break;
110                   package_whitelist[whitelist_size++] = token;
111               } 
112           }
113           for (i=args;i<argc-2+option_offset;i++) argv[i]=argv[i+option_offset];
114           argc-=option_offset;
115
116       } else if (strcmp(argv[args],"--target")==0) {
117           char **dash;
118
119           /* get arch component of the --target option */
120           dash = (char**)strchr(argv[args+1],'-');
121           if (dash != NULL) *dash=NULL;
122
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);
128           target[alen]='\0';
129
130           /* change argc, argv to take out the "--target xxx" */
131           for (i=args;i<argc;i++) argv[i]=argv[i+2];
132           argc-=2;
133
134           break;
135       }
136       else
137           args++;
138   }
139   argv[1]=argv[argc-2];
140   argv[2]=argv[argc-1];
141   argv[3]=0;
142   argc=3;
143   /* END: support to pull out --target from the args list */
144
145   /* Parse common options for all rpm modes and executables */
146   context = rpmcliInit(argc, argv, optionsTable);
147
148   /* Create transaction state */
149   ts = rpmtsCreate();
150
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];
156   if (!spec) {
157     ec = 1;
158     goto done;
159   }
160
161   /* Print sources */
162   for (source = spec->sources; source; source = source->next) {
163     char fullSource[PATH_MAX];
164
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 */
168     { 
169       char *suffixes[] = {".tar.gz",".tgz",".tar.bz2", NULL};
170       char **suffix;
171       char *suffix_index;
172                   
173       for (suffix=suffixes ; *suffix ; suffix++) {
174         /*printf("# trying %s\n",*suffix);*/
175         suffix_index=strstr(fullSource,*suffix);
176         if (suffix_index) {
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));
182           break;
183         }
184       }
185     }
186                     
187   }
188
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);
196
197   /* Print non-empty packages */
198   for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
199     int force = 0;
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) {
205           arch=target;
206         }
207       }
208       /* skip empty packages 
209        *
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.
215        */
216
217       for (i=0;i<whitelist_size;i++) if (strncmp(package_whitelist[i], name, strlen(name)) == 0) force = 1;
218       
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);
223         /* convenience */
224         printf("%s.rpmnames += %s\n",
225                package_name, name);
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",
231                name,package_name);
232       }
233     }
234   }
235
236   /* export some macros to make */
237   /* note : this relies on pl-specific conventions and might be wrong */
238   { 
239     char *macros[] = { "release" , "name" , "version" , "taglevel" , NULL } ;
240     char **nav;
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);
246     }
247   }
248   
249   /* export arch */
250   printf ("%s.rpm-arch := %s\n",package_name,target);
251
252   spec = freeSpec(spec);
253
254  done:
255   ts = rpmtsFree(ts);
256   context = rpmcliFini(context);
257   return ec;
258