previous hack in spec2make was too intrusive
[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 #ifndef PATH_MAX
28 #include <linux/limits.h>
29 #endif
30 extern size_t strnlen(const char *s, size_t maxlen);
31
32 /* the structure describing the options we take and the defaults */
33 static struct poptOption optionsTable[] = {
34   { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmBuildPoptTable, 0,
35     "Build options with [ <specfile> | <tarball> | <source package> ]:",
36     NULL },
37  
38   { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
39     "Common options for all rpm modes and executables:",
40     NULL },
41
42   POPT_AUTOALIAS
43   POPT_AUTOHELP
44   POPT_TABLEEND
45 };
46
47 /* Stolen from rpm/build/spec.c:rpmspecQuery() */
48 rpmSpec
49 rpmspecGet(rpmts ts, const char * arg)
50 {
51   char * buildRoot = NULL;
52   int recursing = 0;
53   char * passPhrase = "";
54   char *cookie = NULL;
55   int anyarch = 1;
56   int force = 1;
57
58   if (parseSpec(ts, arg, "/", buildRoot, recursing, passPhrase,
59                 cookie, anyarch, force)) {
60     fprintf(stderr, "query of specfile %s failed, can't parse\n", arg);
61     return NULL;
62   }
63
64   return rpmtsSpec(ts);
65 }
66
67 int
68 main(int argc, char *argv[])
69 {
70   poptContext context;
71   rpmts ts = NULL;
72   int ec = 0;
73   rpmSpec spec;
74   struct Source *source;
75   Package pkg;
76   const char *name, *version, *release, *arch, *unused;
77   const char *package_name;
78
79   /* BEGIN: support to pull out --target from the args list */
80   int  alen, i;
81   char *target = NULL;
82   int args = 1;
83   int hack=0;
84
85   /* walk argv list looking for --target */
86   while ((args+1)<argc) {
87     if (strcmp(argv[args],"--hack")==0) {
88       hack=1;
89     } else if (strcmp(argv[args],"--target")==0) {
90       char **dash;
91
92       /* get arch component of the --target option */
93       dash = (char**)strchr(argv[args+1],'-');
94       if (dash != NULL) *dash=NULL;
95
96       /* copy arch component of --target option to target */
97       alen = strnlen(argv[args+1],32);
98       target = (char*)malloc(alen+1);
99       if (target == NULL) return errno;
100       strncpy(target,argv[args+1],alen);
101       target[alen]='\0';
102
103       /* change argc, argv to take out the "--target xxx" */
104       for (i=args;i<argc;i++) argv[i]=argv[i+2];
105       argc-=2;
106
107       break;
108     }
109     args++;
110   }
111   argv[1]=argv[argc-2];
112   argv[2]=argv[argc-1];
113   argv[3]=0;
114   argc=3;
115   /* END: support to pull out --target from the args list */
116
117   /* Parse common options for all rpm modes and executables */
118   context = rpmcliInit(argc, argv, optionsTable);
119
120   /* Create transaction state */
121   ts = rpmtsCreate();
122
123   /* Parse spec file. The rpmcli functions don't allow you to
124    * access the Spec structure directly, so we call our own
125    * version of rpmSpecQuery() directly. */
126   spec = rpmspecGet(ts, argv[1]);
127   package_name = argv[2];
128   if (!spec) {
129     ec = 1;
130     goto done;
131   }
132
133   /* Print sources */
134   for (source = spec->sources; source; source = source->next) {
135     char fullSource[PATH_MAX];
136
137     strncpy(fullSource, source->fullSource, sizeof(fullSource));
138     printf("%s.tarballs += SOURCES/%s\n", package_name, basename(fullSource));
139     /* computes the SOURCEDIR variable by removing .tar.gz or .tar.bz2 */
140     { 
141       char *suffixes[] = {".tar.gz",".tgz",".tar.bz2", NULL};
142       char **suffix;
143       char *suffix_index;
144                   
145       for (suffix=suffixes ; *suffix ; suffix++) {
146         printf("# trying %s\n",*suffix);
147         suffix_index=strstr(fullSource,*suffix);
148         if (suffix_index) {
149           char sourcename[PATH_MAX];
150           size_t len = (size_t)(suffix_index-fullSource);
151           strncpy(sourcename,fullSource,len);
152           sourcename[len]='\0';
153           printf ("%s.source := SOURCES/%s\n",package_name,basename(sourcename));
154           break;
155         }
156       }
157     }
158                     
159   }
160
161   /* Get SRPM name from name of first package */ 
162   pkg = spec->packages;
163   name = version = release = NULL;
164   (void) headerNVR(pkg->header, &name, &version, &release);
165   if (name && version && release)
166     printf("%s.srpm := SRPMS/%s-%s-%s.src.rpm\n",
167            package_name, name, version, release);
168
169   /* Print non-empty packages */
170   for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
171     name = version = release = arch = NULL;
172     (void) headerNEVRA(pkg->header, &name, &unused, &version, &release, &arch);
173     if (name && version && release && arch) {
174       if (target != NULL) {
175         if (strcmp(arch,target)!=0) {
176           arch=target;
177         }
178       }
179       /* skip empty packages 
180        *
181        * Unfortunately, f8 + the RHEL kernel break this bit of cleverness. The following
182        * line returns false for the kernel-devel package even though it is not empty thereby breaking the build.
183        * Rather than unfolding the kernel package macros in the current specfile, 
184        * this hack should work till f8 dies its natural death. 
185        * Thierry : trigerring this based on the package's NEEDSPEC2MAKEHACK instead of hard-wiring it for kernel here
186        */
187
188       if (pkg->fileList || hack) {
189         /* attach (add) rpm path to package */
190         printf("%s.rpms += RPMS/%s/%s-%s-%s.%s.rpm\n",
191                package_name, arch, name, version, release, arch);
192         /* convenience */
193         printf("%s.rpmnames += %s\n",
194                package_name, name);
195         /* attach path to rpm name */
196         printf("%s.rpm-path := RPMS/%s/%s-%s-%s.%s.rpm\n",
197                name,arch, name, version, release, arch);
198         /* attach package to rpm name for backward resolution - should be unique */
199         printf("%s.package := %s\n",
200                name,package_name);
201       }
202     }
203   }
204
205   /* export some macros to make */
206   /* note : this relies on pl-specific conventions and might be wrong */
207   { 
208     char *macros[] = { "release" , "name" , "version" , "taglevel" , NULL } ;
209     char **nav;
210     char *macro=malloc(32);
211     for (nav=macros; *nav; nav++) {
212       sprintf(macro,"%%{%s}",*nav);
213       char *value = rpmExpand(macro,NULL);
214       printf ("%s.rpm-%s := %s\n",package_name,*nav,value);
215     }
216   }
217   
218   /* export arch */
219   printf ("%s.rpm-arch := %s\n",package_name,target);
220
221   spec = freeSpec(spec);
222
223  done:
224   ts = rpmtsFree(ts);
225   context = rpmcliFini(context);
226   return ec;
227