]> git.pld-linux.org Git - packages/rpm-specdump.git/blob - rpm-specdump.c
inline commonly used macros
[packages/rpm-specdump.git] / rpm-specdump.c
1 /*
2  * Prints out following information in same format as %dump for builder:
3  * $ rpmbuild --nodigest --nosignature --nobuild -bp --define 'prep %{echo:dummy: PACKAGE_NAME %{name} }%dump' qemu.spec 2>&1 | awk '$2 ~ /^SOURCEURL/ {print} $2 ~ /^PATCHURL/  {print} $2 ~ /^nosource/ {print} $2 ~ /^PACKAGE_/ {print}'
4  * dummy: PACKAGE_NAME qemu ========================
5  * -2: PACKAGE_RELEASE    60@2.6.16.59_2
6  * -1: PACKAGE_VERSION    1.3.0pre11
7  * -3: PATCHURL0  qemu-nostatic.patch
8  * -3: PATCHURL1  qemu-cc.patch
9  * -3: PATCHURL11 qemu-0.7.2-gcc4-opts.patch
10  * -3: PATCHURL13 qemu-dosguest.patch
11  * -3: PATCHURL3  qemu-dot.patch
12  * -3: PATCHURL4  qemu-gcc4_x86.patch
13  * -3: PATCHURL5  qemu-gcc4_ppc.patch
14  * -3: PATCHURL6  qemu-nosdlgui.patch
15  * -3: PATCHURL8  qemu-kde_virtual_workspaces_hack.patch
16  * -3: PATCHURL9  qemu-0.8.0-gcc4-hacks.patch
17  * -3: SOURCEURL0 http://fabrice.bellard.free.fr/qemu/qemu-0.9.0.tar.gz
18  * -3: SOURCEURL1 http://fabrice.bellard.free.fr/qemu/kqemu-1.3.0pre11.tar.gz
19  * -3: url http://rpm5.org/
20  *
21  *  $ rpm-specdump qemu.spec
22  *  h PACKAGE_NAME qemu
23  *  h PACKAGE_VERSION 0.9.0
24  *  h PACKAGE_RELEASE 60k
25  *  s PATCHURL13 qemu-dosguest.patch
26  *  s PATCHURL11 qemu-0.7.2-gcc4-opts.patch
27  *  s PATCHURL9 qemu-0.8.0-gcc4-hacks.patch
28  *  s PATCHURL8 qemu-kde_virtual_workspaces_hack.patch
29  *  s PATCHURL6 qemu-nosdlgui.patch
30  *  s PATCHURL5 qemu-gcc4_ppc.patch
31  *  s PATCHURL4 qemu-gcc4_x86.patch
32  *  s PATCHURL3 qemu-dot.patch
33  *  s PATCHURL1 qemu-cc.patch
34  *  s PATCHURL0 qemu-nostatic.patch
35  *  s SOURCEURL1 http://fabrice.bellard.free.fr/qemu/kqemu-1.3.0pre11.tar.gz
36  *  s SOURCEURL0 http://fabrice.bellard.free.fr/qemu/qemu-0.9.0.tar.gz
37  *
38  * And with NoSource: 1, NoSource: 2
39  *
40  *  $ rpmbuild --nodigest --nosignature --nobuild -bp --define 'prep %{echo:dummy: PACKAGE_NAME %{name} }%dump' ZendDebugger.spec 2>&1 | awk '$2 ~ /^SOURCEURL/ {print} $2 ~ /^PATCHURL/  {print} $2 ~ /^nosource/ {print} $2 ~ /^PACKAGE_/ {print}'
41  *  dummy: PACKAGE_NAME ZendDebugger ========================
42  *   -2: PACKAGE_RELEASE    0.4
43  *   -1: PACKAGE_VERSION    5.2.10
44  *   -3: SOURCEURL0 http://downloads.zend.com/pdt/server-debugger/ZendDebugger-5.2.10-linux-glibc21-i386.tar.gz
45  *   -3: SOURCEURL1 http://downloads.zend.com/pdt/server-debugger/ZendDebugger-5.2.10-linux-glibc23-x86_64.tar.gz
46  *   -3: nosource   1
47  *
48  *  $ rpm-specdump ZendDebugger.spec
49  *  h PACKAGE_NAME ZendDebugger
50  *  h PACKAGE_VERSION 5.2.10
51  *  h PACKAGE_RELEASE 0.4
52  *  s SOURCEURL1 http://downloads.zend.com/pdt/server-debugger/ZendDebugger-5.2.10-linux-glibc23-x86_64.tar.gz
53  *  s nosource 1
54  *  s SOURCEURL0 http://downloads.zend.com/pdt/server-debugger/ZendDebugger-5.2.10-linux-glibc21-i386.tar.gz
55  *  s nosource 0
56  *
57  * Compile with:
58  * gcc -lrpm -I/usr/include/rpm -lrpmbuild rpm-specdump.c -o rpm-specdump
59  *
60  * Version 0.1, 2008-01-23
61  * - initial version, based on getdeps.c
62  */
63
64 #define _GNU_SOURCE
65
66 #ifdef HAVE_CONFIG_H
67 #  include <config.h>
68 #endif
69
70 #include <getopt.h>
71 #include <stdbool.h>
72 #include <string.h>
73 #include <assert.h>
74 #include <unistd.h>
75 #include <stdint.h>
76 #include <grp.h>
77 #include <sys/time.h>
78 #include <sys/types.h>
79
80 // macros from kernel
81 #define RPM_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
82
83 #if !defined(RPM_FORMAT_VERSION)
84 #include <rpmversion.h>
85 #if defined(RPMLIB_VERSION) && RPMLIB_VENDOR_EQ('R','P','M','5')
86 #       if RPMLIB_VERSION >= RPMLIB_VERSION_ENCODE(5,4,r,0,0,_)
87 #               define  RPM_VERSION_CODE RPM_VERSION(5, 4, 0)
88 #       elif RPMLIB_VERSION >= RPMLIB_VERSION_ENCODE(5,3,r,0,0,_)
89 #               define  RPM_VERSION_CODE RPM_VERSION(5, 3, 0)
90 #       elif RPMLIB_VERSION >= RPMLIB_VERSION_ENCODE(5,2,r,0,0,_)
91 #               define  RPM_VERSION_CODE RPM_VERSION(5, 2, 0)
92 #       elif RPMLIB_VERSION >= RPMLIB_VERSION_ENCODE(5,1,r,0,0,_)
93 #               define  RPM_VERSION_CODE RPM_VERSION(5, 1, 0)
94 #       elif RPMLIB_VERSION >= RPMLIB_VERSION_ENCODE(5,0,r,0,0,_)
95 #               define  RPM_VERSION_CODE RPM_VERSION(5, 0, 0)
96 #       endif
97 #endif
98 #else
99 #       define  RPM_VERSION_CODE RPM_VERSION(RPM_FORMAT_VERSION, RPM_MAJOR_VERSION, RPM_MINOR_VERSION)
100 #endif
101
102 #include <rpmio.h>
103 #include <rpmbuild.h>
104 #if RPM_VERSION_CODE < RPM_VERSION(5,4,0)
105 #include <rpmlib.h>
106 #endif
107 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
108 #include <header.h>
109 #endif
110 #include <rpmts.h>
111
112 #define ARG_WITH        1024
113 #define ARG_WITHOUT     1025
114 #define ARG_DEFINE      1026
115 #define ARG_TARGET      1027
116 #define ARG_RCFILE      1028
117 #define ARG_CHROOT      1029
118 #define ARG_UID         1030
119 #define ARG_GID         1031
120
121 // RPM 4.4.2
122 #if RPM_VERSION_CODE < RPM_VERSION(4,4,9)
123 #       define RPMFILE_SOURCE RPMBUILD_ISSOURCE
124 #endif
125
126 #if !defined(EXIT_FAILURE)
127 #       define EXIT_FAILURE 1
128 #endif
129
130 static struct option const
131 CMDLINE_OPTIONS[] = {
132   { "help",     no_argument,  0, 'h' },
133   { "version",  no_argument,  0, 'v' },
134   { "with",     required_argument, 0, ARG_WITH    },
135   { "without",  required_argument, 0, ARG_WITHOUT },
136   { "define",   required_argument, 0, ARG_DEFINE  },
137   { "target",   required_argument, 0, ARG_TARGET  },
138   { "rcfile",   required_argument, 0, ARG_RCFILE  },
139   { "chroot",   required_argument, 0, ARG_CHROOT  },
140   { "uid",      required_argument, 0, ARG_UID },
141   { "gid",      required_argument, 0, ARG_GID },
142   { 0,0,0,0 }
143 };
144
145 struct Arguments
146 {
147     char const *        target;
148     char const *        rcfile;
149     char const *        chroot;
150     uid_t               uid;
151     gid_t               gid;
152     
153     struct {
154         char const **   values;
155         size_t          cnt;
156         size_t          reserved;
157     }                   macros;
158
159     char const *        specfile;
160 };
161
162 struct DepSet {
163     int32_t const *     flags;
164     char const **       name;
165     char const **       version;
166     ssize_t             cnt;
167 };
168
169 inline static void 
170 writeStr(int fd, char const *cmd)
171 {
172   (void)write(fd, cmd, strlen(cmd));
173 }
174
175 #define WRITE_MSG(FD,X)         (void)(write(FD,X,sizeof(X)-1))
176 #define WRITE_STR(FD,X)         writeStr(FD,X)
177
178 static void
179 showHelp(int fd, char const *cmd, int res)
180 {
181   char          tmp[strlen(cmd)+1];
182   strcpy(tmp, cmd);
183   
184   WRITE_MSG(fd, "Usage:  ");
185   WRITE_STR(fd, basename(tmp));
186   WRITE_MSG(fd,
187             " [--define '<macro> <value>']* [--with[out] <key>]* [--chroot <dir>]\n"
188             "                [--target <target>] [--rcfile <rcfile>] [--] <specfile>\n");
189   exit(res);
190 }
191
192 static void
193 addDefine(struct Arguments *args, char const *val)
194 {
195   register size_t       c = args->macros.cnt;
196   if (args->macros.reserved <= c) {
197     args->macros.reserved *= 2;
198     args->macros.reserved += 1;
199   
200     args->macros.values = realloc(args->macros.values,
201                                   args->macros.reserved * sizeof(char const *));
202     if (args->macros.values==0) {
203       perror("realloc()");
204       exit(1);
205     }
206   }
207
208   args->macros.values[c] = strdup(val);
209   ++args->macros.cnt;
210 }
211
212 static void
213 setWithMacro(struct Arguments *args,
214              char const *name, char const *prefix, size_t prefix_len)
215 {
216   size_t        len = strlen(name);
217   char          tmp[2*len + 2*prefix_len + sizeof("__ ---")];
218   char *        ptr = tmp;
219
220   // set '_<prefix>_<name>'
221   *ptr++ = '_';
222   memcpy(ptr, prefix, prefix_len); ptr += prefix_len;
223   *ptr++ = '_';
224   memcpy(ptr, name,   len);        ptr += len;
225   *ptr++ = ' ';
226
227   // append ' --<prefix>-<name>'
228   *ptr++ = '-';
229   *ptr++ = '-';
230   memcpy(ptr, prefix, prefix_len); ptr += prefix_len;
231   *ptr++ = '-';
232   memcpy(ptr, name,   len);        ptr += len;
233   *ptr   = '\0';
234
235   addDefine(args, tmp);
236 }
237
238
239 static void
240 parseArgs(struct Arguments *args, int argc, char *argv[])
241 {
242   while (1) {
243     int         c = getopt_long(argc, argv, "", CMDLINE_OPTIONS, 0);
244     if (c==-1) break;
245     switch (c) {
246       case 'h'          :  showHelp(1, argv[0], 0);
247       case ARG_TARGET   :  args->target = optarg; break;
248       case ARG_RCFILE   :  args->rcfile = optarg; break;
249       case ARG_CHROOT   :  args->chroot = optarg; break;
250       case ARG_UID      :  args->uid    = atoi(optarg); break;
251       case ARG_GID      :  args->gid    = atoi(optarg); break;
252       case ARG_DEFINE   :  addDefine(args, optarg); break;
253       case ARG_WITH     :  setWithMacro(args, optarg, "with",    4); break;
254       case ARG_WITHOUT  :  setWithMacro(args, optarg, "without", 7); break;
255       default:
256         WRITE_MSG(2, "Try '");
257         WRITE_STR(2, argv[0]);
258         WRITE_MSG(2, " --help\" for more information.\n");
259         exit(1);
260     }
261   }
262
263   if (optind+1!=argc) {
264     write(2, "No/too much specfile(s) given; aborting\n", 40);
265     exit(1);
266   }
267
268   if (args->gid==(gid_t)(-1))
269     args->gid = args->uid;
270
271   args->specfile = argv[optind];
272 }
273
274 static void
275 setMacros(char const * const *macros, size_t cnt)
276 {
277   size_t        i;
278   for (i=0; i<cnt; ++i)
279     rpmDefineMacro(rpmGlobalMacroContext, macros[i], 0);
280 }
281
282 int main(int argc, char *argv[])
283 {
284 struct Arguments args = { 0,0,0,-1,-1, {0,0,0}, 0 };
285 Spec s;
286
287         addDefine(&args, "patch %{nil}");
288
289         // instead of requiring rpm-build, we include some "builtin" macros
290         addDefine(&args, "bcond_without() %{expand:%%{!?_without_%{1}:%%global with_%{1} 1}}");
291         addDefine(&args, "bcond_with() %{expand:%%{?_with_%{1}:%%global with_%{1} 1}}");
292         addDefine(&args, "with() %{expand:%%{?with_%{1}:1}%%{!?with_%{1}:0}}");
293         addDefine(&args, "without() %{expand:%%{?with_%{1}:0}%%{!?with_%{1}:1}}");
294
295         // include macros.perl, etc
296         addDefine(&args, "include() %{nil}");
297
298         parseArgs(&args, argc, argv);
299
300         if ((args.chroot && chroot(args.chroot)==-1) ||
301                 (args.uid!=(uid_t)(-1) && (setgroups(0,0)  ==-1 || getgroups(0,0)!=0))  ||
302                 (args.gid!=(gid_t)(-1) && (setgid(args.gid)==-1 || getgid()!=args.gid)) ||
303                 (args.uid!=(uid_t)(-1) && (setuid(args.uid)==-1 || getuid()!=args.uid))) {
304
305                 perror("chroot/setuid/setgid()");
306                 return EXIT_FAILURE;
307         }
308   
309         rpmReadConfigFiles(args.rcfile, args.target);
310         setMacros(args.macros.values, args.macros.cnt);
311
312         rpmts ts = rpmtsCreate();
313 #if RPM_VERSION_CODE >= RPM_VERSION(4,4,9)
314         if (parseSpec(ts, args.specfile, NULL, 0, NULL, NULL, 1, 1, 0) != 0) {
315 #else
316         if (parseSpec(ts, args.specfile, NULL, NULL, 1, NULL, NULL, 1, 1) != 0) {
317 #endif
318                 return EXIT_FAILURE;
319         }
320   
321         s = rpmtsSpec(ts);
322
323         // here starts the code for builder
324         const char *name = NULL, *version = NULL, *release = NULL, *summary = NULL, *url = NULL;
325
326 #if RPM_VERSION_CODE >= RPM_VERSION(4,4,9)
327         initSourceHeader(s, NULL);
328 #else
329         initSourceHeader(s);
330 #endif
331         Header h = s->sourceHeader;
332
333 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
334         if (
335                 headerGetEntryMinMemory(h, RPMTAG_NAME, NULL, (void *)&name, NULL) == 0 ||
336                 headerGetEntryMinMemory(h, RPMTAG_VERSION, NULL, (void *)&version, NULL) == 0 ||
337                 headerGetEntryMinMemory(h, RPMTAG_RELEASE, NULL, (void *)&release, NULL) == 0
338                 ) {
339                 fprintf(stderr, "NVR query failed\n");
340                 return EXIT_FAILURE;
341         }
342
343 #else
344         {
345                 HE_t he;
346                 int rc;
347
348                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
349                 he->tag = (rpmTag) RPMTAG_NAME;
350                 rc = headerGet(h, he, 0);
351                 if (!rc) {
352                         fprintf(stderr, "Name (NVR) query failed\n");
353                         return EXIT_FAILURE;
354                 }
355                 name = (char *)he->p.ptr;
356
357                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
358                 he->tag = (rpmTag) RPMTAG_VERSION;
359                 rc = headerGet(h, he, 0);
360                 if (!rc) {
361                         fprintf(stderr, "Version (NVR) query failed\n");
362                         return EXIT_FAILURE;
363                 }
364                 version = (char *)he->p.ptr;
365
366                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
367                 he->tag = (rpmTag) RPMTAG_RELEASE;
368                 rc = headerGet(h, he, 0);
369                 if (!rc) {
370                         fprintf(stderr, "Release (NVR) query failed\n");
371                         return EXIT_FAILURE;
372                 }
373                 release = (char *)he->p.ptr;
374
375                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
376                 he->tag = (rpmTag) RPMTAG_SUMMARY;
377                 rc = headerGet(h, he, 0);
378                 if (!rc) {
379                         fprintf(stderr, "Summary query failed\n");
380                         return EXIT_FAILURE;
381                 }
382                 summary = (char *)he->p.ptr;
383
384                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
385                 he->tag = (rpmTag) RPMTAG_URL;
386                 rc = headerGet(h, he, 0);
387                 if (rc) {
388                         // URL field is not required
389                         url = (char *)he->p.ptr;
390                 }
391         }
392 #endif
393
394         printf("h PACKAGE_NAME %s\n", name);
395         printf("h PACKAGE_VERSION %s\n", version);
396         printf("h PACKAGE_RELEASE %s\n", release);
397
398         printf("h PACKAGE_SUMMARY %s\n", summary);
399         printf("h url %s\n", url);
400
401         struct Source *ps = s->sources;
402         while (ps) {
403                 const char *type = (ps->flags & RPMFILE_SOURCE) ? "SOURCE" : "PATCH";
404                 printf("s %sURL%d %s\n", type, ps->num, ps->fullSource);
405                 if (ps->flags & RPMFILE_GHOST) {
406                         printf("s nosource %d\n", ps->num);
407                 }
408                 ps = ps->next;
409         }
410
411         const char *arch = rpmExpand("%{_target_cpu}", NULL);
412         printf("m _target_cpu %s\n", arch);
413
414         return(0);
415 }
This page took 0.08099 seconds and 3 git commands to generate.