]> git.pld-linux.org Git - packages/rpm-specdump.git/blob - rpm-specdump.c
- drop support for rpm < 4.5
[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 #define RPM_VERSION_CODE RPM_VERSION(RPM_FORMAT_VERSION, RPM_MAJOR_VERSION, RPM_MINOR_VERSION)
84
85 #include <rpmio.h>
86 #include <rpmbuild.h>
87 #if RPM_VERSION_CODE < RPM_VERSION(5,4,0)
88 #include <rpmlib.h>
89 #endif
90 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
91 #include <header.h>
92 #endif
93 #include <rpmts.h>
94
95 #define ARG_WITH        1024
96 #define ARG_WITHOUT     1025
97 #define ARG_DEFINE      1026
98 #define ARG_TARGET      1027
99 #define ARG_RCFILE      1028
100 #define ARG_CHROOT      1029
101 #define ARG_UID         1030
102 #define ARG_GID         1031
103
104 #if !defined(EXIT_FAILURE)
105 #       define EXIT_FAILURE 1
106 #endif
107
108 static struct option const
109 CMDLINE_OPTIONS[] = {
110   { "help",     no_argument,  0, 'h' },
111   { "version",  no_argument,  0, 'v' },
112   { "with",     required_argument, 0, ARG_WITH    },
113   { "without",  required_argument, 0, ARG_WITHOUT },
114   { "define",   required_argument, 0, ARG_DEFINE  },
115   { "target",   required_argument, 0, ARG_TARGET  },
116   { "rcfile",   required_argument, 0, ARG_RCFILE  },
117   { "chroot",   required_argument, 0, ARG_CHROOT  },
118   { "uid",      required_argument, 0, ARG_UID },
119   { "gid",      required_argument, 0, ARG_GID },
120   { 0,0,0,0 }
121 };
122
123 struct Arguments
124 {
125     char const *        target;
126     char const *        rcfile;
127     char const *        chroot;
128     uid_t               uid;
129     gid_t               gid;
130     
131     struct {
132         char const **   values;
133         size_t          cnt;
134         size_t          reserved;
135     }                   macros;
136
137     char const *        specfile;
138 };
139
140 struct DepSet {
141     int32_t const *     flags;
142     char const **       name;
143     char const **       version;
144     ssize_t             cnt;
145 };
146
147 inline static void 
148 writeStr(int fd, char const *cmd)
149 {
150   (void)write(fd, cmd, strlen(cmd));
151 }
152
153 #define WRITE_MSG(FD,X)         (void)(write(FD,X,sizeof(X)-1))
154 #define WRITE_STR(FD,X)         writeStr(FD,X)
155
156 static void
157 showHelp(int fd, char const *cmd, int res)
158 {
159   char          tmp[strlen(cmd)+1];
160   strcpy(tmp, cmd);
161   
162   WRITE_MSG(fd, "Usage:  ");
163   WRITE_STR(fd, basename(tmp));
164   WRITE_MSG(fd,
165             " [--define '<macro> <value>']* [--with[out] <key>]* [--chroot <dir>]\n"
166             "                [--target <target>] [--rcfile <rcfile>] [--] <specfile>\n");
167   exit(res);
168 }
169
170 static void
171 addDefine(struct Arguments *args, char const *val)
172 {
173   register size_t       c = args->macros.cnt;
174   if (args->macros.reserved <= c) {
175     args->macros.reserved *= 2;
176     args->macros.reserved += 1;
177   
178     args->macros.values = realloc(args->macros.values,
179                                   args->macros.reserved * sizeof(char const *));
180     if (args->macros.values==0) {
181       perror("realloc()");
182       exit(1);
183     }
184   }
185
186   args->macros.values[c] = strdup(val);
187   ++args->macros.cnt;
188 }
189
190 static void
191 setWithMacro(struct Arguments *args,
192              char const *name, char const *prefix, size_t prefix_len)
193 {
194   size_t        len = strlen(name);
195   char          tmp[2*len + 2*prefix_len + sizeof("__ ---")];
196   char *        ptr = tmp;
197
198   // set '_<prefix>_<name>'
199   *ptr++ = '_';
200   memcpy(ptr, prefix, prefix_len); ptr += prefix_len;
201   *ptr++ = '_';
202   memcpy(ptr, name,   len);        ptr += len;
203   *ptr++ = ' ';
204
205   // append ' --<prefix>-<name>'
206   *ptr++ = '-';
207   *ptr++ = '-';
208   memcpy(ptr, prefix, prefix_len); ptr += prefix_len;
209   *ptr++ = '-';
210   memcpy(ptr, name,   len);        ptr += len;
211   *ptr   = '\0';
212
213   addDefine(args, tmp);
214 }
215
216
217 static void
218 parseArgs(struct Arguments *args, int argc, char *argv[])
219 {
220   while (1) {
221     int         c = getopt_long(argc, argv, "", CMDLINE_OPTIONS, 0);
222     if (c==-1) break;
223     switch (c) {
224       case 'h'          :  showHelp(1, argv[0], 0); break;
225       case ARG_TARGET   :  args->target = optarg; break;
226       case ARG_RCFILE   :  args->rcfile = optarg; break;
227       case ARG_CHROOT   :  args->chroot = optarg; break;
228       case ARG_UID      :  args->uid    = atoi(optarg); break;
229       case ARG_GID      :  args->gid    = atoi(optarg); break;
230       case ARG_DEFINE   :  addDefine(args, optarg); break;
231       case ARG_WITH     :  setWithMacro(args, optarg, "with",    4); break;
232       case ARG_WITHOUT  :  setWithMacro(args, optarg, "without", 7); break;
233       default:
234         WRITE_MSG(2, "Try '");
235         WRITE_STR(2, argv[0]);
236         WRITE_MSG(2, " --help\" for more information.\n");
237         exit(1);
238     }
239   }
240
241   if (optind+1!=argc) {
242     write(2, "No/too much specfile(s) given; aborting\n", 40);
243     exit(1);
244   }
245
246   if (args->gid==(gid_t)(-1))
247     args->gid = args->uid;
248
249   args->specfile = argv[optind];
250 }
251
252 static void
253 setMacros(char const * const *macros, size_t cnt)
254 {
255   size_t        i;
256   for (i=0; i<cnt; ++i)
257     rpmDefineMacro(rpmGlobalMacroContext, macros[i], 0);
258 }
259
260 int main(int argc, char *argv[])
261 {
262 struct Arguments args = { 0,0,0,-1,-1, {0,0,0}, 0 };
263 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
264 rpmSpec s;
265 #else
266 Spec s;
267 #endif
268
269         addDefine(&args, "patch %{nil}");
270
271         // instead of requiring rpm-build, we include some "builtin" macros
272         addDefine(&args, "bcond_without() %{expand:%%{!?_without_%{1}:%%global with_%{1} 1}}");
273         addDefine(&args, "bcond_with() %{expand:%%{?_with_%{1}:%%global with_%{1} 1}}");
274         addDefine(&args, "with() %{expand:%%{?with_%{1}:1}%%{!?with_%{1}:0}}");
275         addDefine(&args, "without() %{expand:%%{?with_%{1}:0}%%{!?with_%{1}:1}}");
276
277         // include macros.perl, etc
278         addDefine(&args, "include() %{nil}");
279
280         parseArgs(&args, argc, argv);
281
282         if ((args.chroot && chroot(args.chroot)==-1) ||
283                 (args.uid!=(uid_t)(-1) && (setgroups(0,0)  ==-1 || getgroups(0,0)!=0))  ||
284                 (args.gid!=(gid_t)(-1) && (setgid(args.gid)==-1 || getgid()!=args.gid)) ||
285                 (args.uid!=(uid_t)(-1) && (setuid(args.uid)==-1 || getuid()!=args.uid))) {
286
287                 perror("chroot/setuid/setgid()");
288                 return EXIT_FAILURE;
289         }
290   
291         rpmReadConfigFiles(args.rcfile, args.target);
292         setMacros(args.macros.values, args.macros.cnt);
293
294         // here starts the code for builder
295         const char *name = NULL, *version = NULL, *release = NULL, *summary = NULL, *url = NULL;
296
297 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
298 #define GET_TAG(t) tag = t; rc = headerGet(h, tag, td, 0);
299 #define TAG_VALUE rpmtdGetString(td)
300         s = rpmSpecParse(args.specfile, 0, NULL);
301         Header h = rpmSpecSourceHeader(s);
302         rpmtd td = rpmtdNew();
303         rpmTagVal tag;
304 #else
305 #define GET_TAG(t) he->tag = (rpmTag)t; he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he)); rc = headerGet(h, he, 0);
306 #define TAG_VALUE (char *)he->p.ptr
307         rpmts ts = rpmtsCreate();
308         if (parseSpec(ts, args.specfile, NULL, 0, NULL, NULL, 1, 1, 0) != 0) {
309                 return EXIT_FAILURE;
310         }
311
312         s = rpmtsSpec(ts);
313
314         Header h = s->sourceHeader;
315         HE_t he;
316 #endif
317         int rc;
318
319         GET_TAG(RPMTAG_NAME);
320         if (!rc) {
321                 fprintf(stderr, "Name (NVR) query failed\n");
322                 return EXIT_FAILURE;
323         }
324         name = TAG_VALUE;
325
326         GET_TAG(RPMTAG_VERSION);
327         if (!rc) {
328                 fprintf(stderr, "Version (NVR) query failed\n");
329                 return EXIT_FAILURE;
330         }
331         version = TAG_VALUE;
332
333         GET_TAG(RPMTAG_RELEASE);
334         if (!rc) {
335                 fprintf(stderr, "Release (NVR) query failed\n");
336                 return EXIT_FAILURE;
337         }
338         release = TAG_VALUE;
339
340         GET_TAG(RPMTAG_SUMMARY);
341         if (!rc) {
342                 fprintf(stderr, "Summary query failed\n");
343                 return EXIT_FAILURE;
344         }
345         summary = TAG_VALUE;
346
347         GET_TAG(RPMTAG_URL);
348         if (rc) {
349                 // URL field is not required
350                 url = TAG_VALUE;
351         }
352
353         printf("h PACKAGE_NAME %s\n", name);
354         printf("h PACKAGE_VERSION %s\n", version);
355         printf("h PACKAGE_RELEASE %s\n", release);
356
357         printf("h PACKAGE_SUMMARY %s\n", summary);
358         printf("h url %s\n", url);
359
360 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
361         rpmSpecSrcIter si = rpmSpecSrcIterInit(s);
362         rpmSpecSrc ps;
363         while ((ps = rpmSpecSrcIterNext(si)) != NULL) {
364                 const char *type = (rpmSpecSrcFlags(ps) & RPMBUILD_ISSOURCE) ? "SOURCE" : "PATCH";
365                 printf("s %sURL%d %s\n", type, rpmSpecSrcNum(ps), rpmSpecSrcFilename(ps, 1));
366                 if (rpmSpecSrcFlags(ps) & RPMBUILD_ISNO) {
367                         printf("s nosource %d\n", rpmSpecSrcNum(ps));
368                 }
369         }
370         rpmSpecSrcIterFree(si);
371 #else
372         struct Source *ps = s->sources;
373         while (ps) {
374                 const char *type = (ps->flags & RPMFILE_SOURCE) ? "SOURCE" : "PATCH";
375                 printf("s %sURL%d %s\n", type, ps->num, ps->fullSource);
376                 if (ps->flags & RPMFILE_GHOST) {
377                         printf("s nosource %d\n", ps->num);
378                 }
379                 ps = ps->next;
380         }
381 #endif
382
383         const char *arch = rpmExpand("%{_target_cpu}", NULL);
384         printf("m _target_cpu %s\n", arch);
385
386         return(0);
387 }
This page took 0.132796 seconds and 3 git commands to generate.