]> git.pld-linux.org Git - packages/rpm-specdump.git/blob - rpm-specdump.c
1fdcbc393bfc022a548ff578ce8a117829e2a75d
[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 Spec s;
264
265         addDefine(&args, "patch %{nil}");
266
267         // instead of requiring rpm-build, we include some "builtin" macros
268         addDefine(&args, "bcond_without() %{expand:%%{!?_without_%{1}:%%global with_%{1} 1}}");
269         addDefine(&args, "bcond_with() %{expand:%%{?_with_%{1}:%%global with_%{1} 1}}");
270         addDefine(&args, "with() %{expand:%%{?with_%{1}:1}%%{!?with_%{1}:0}}");
271         addDefine(&args, "without() %{expand:%%{?with_%{1}:0}%%{!?with_%{1}:1}}");
272
273         // include macros.perl, etc
274         addDefine(&args, "include() %{nil}");
275
276         parseArgs(&args, argc, argv);
277
278         if ((args.chroot && chroot(args.chroot)==-1) ||
279                 (args.uid!=(uid_t)(-1) && (setgroups(0,0)  ==-1 || getgroups(0,0)!=0))  ||
280                 (args.gid!=(gid_t)(-1) && (setgid(args.gid)==-1 || getgid()!=args.gid)) ||
281                 (args.uid!=(uid_t)(-1) && (setuid(args.uid)==-1 || getuid()!=args.uid))) {
282
283                 perror("chroot/setuid/setgid()");
284                 return EXIT_FAILURE;
285         }
286   
287         rpmReadConfigFiles(args.rcfile, args.target);
288         setMacros(args.macros.values, args.macros.cnt);
289
290         rpmts ts = rpmtsCreate();
291         if (parseSpec(ts, args.specfile, NULL, 0, NULL, NULL, 1, 1, 0) != 0) {
292                 return EXIT_FAILURE;
293         }
294   
295         s = rpmtsSpec(ts);
296
297         // here starts the code for builder
298         const char *name = NULL, *version = NULL, *release = NULL, *summary = NULL, *url = NULL;
299
300         initSourceHeader(s, NULL);
301         Header h = s->sourceHeader;
302
303 #if RPM_VERSION_CODE < RPM_VERSION(5,0,0)
304         if (
305                 headerGetEntryMinMemory(h, RPMTAG_NAME, NULL, (void *)&name, NULL) == 0 ||
306                 headerGetEntryMinMemory(h, RPMTAG_VERSION, NULL, (void *)&version, NULL) == 0 ||
307                 headerGetEntryMinMemory(h, RPMTAG_RELEASE, NULL, (void *)&release, NULL) == 0
308                 ) {
309                 fprintf(stderr, "NVR query failed\n");
310                 return EXIT_FAILURE;
311         }
312
313 #else
314         {
315                 HE_t he;
316                 int rc;
317
318                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
319                 he->tag = (rpmTag) RPMTAG_NAME;
320                 rc = headerGet(h, he, 0);
321                 if (!rc) {
322                         fprintf(stderr, "Name (NVR) query failed\n");
323                         return EXIT_FAILURE;
324                 }
325                 name = (char *)he->p.ptr;
326
327                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
328                 he->tag = (rpmTag) RPMTAG_VERSION;
329                 rc = headerGet(h, he, 0);
330                 if (!rc) {
331                         fprintf(stderr, "Version (NVR) query failed\n");
332                         return EXIT_FAILURE;
333                 }
334                 version = (char *)he->p.ptr;
335
336                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
337                 he->tag = (rpmTag) RPMTAG_RELEASE;
338                 rc = headerGet(h, he, 0);
339                 if (!rc) {
340                         fprintf(stderr, "Release (NVR) query failed\n");
341                         return EXIT_FAILURE;
342                 }
343                 release = (char *)he->p.ptr;
344
345                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
346                 he->tag = (rpmTag) RPMTAG_SUMMARY;
347                 rc = headerGet(h, he, 0);
348                 if (!rc) {
349                         fprintf(stderr, "Summary query failed\n");
350                         return EXIT_FAILURE;
351                 }
352                 summary = (char *)he->p.ptr;
353
354                 he = (HE_s*)memset(alloca(sizeof(*he)), 0, sizeof(*he));
355                 he->tag = (rpmTag) RPMTAG_URL;
356                 rc = headerGet(h, he, 0);
357                 if (rc) {
358                         // URL field is not required
359                         url = (char *)he->p.ptr;
360                 }
361         }
362 #endif
363
364         printf("h PACKAGE_NAME %s\n", name);
365         printf("h PACKAGE_VERSION %s\n", version);
366         printf("h PACKAGE_RELEASE %s\n", release);
367
368         printf("h PACKAGE_SUMMARY %s\n", summary);
369         printf("h url %s\n", url);
370
371         struct Source *ps = s->sources;
372         while (ps) {
373                 const char *type = (ps->flags & RPMFILE_SOURCE) ? "SOURCE" : "PATCH";
374                 printf("s %sURL%d %s\n", type, ps->num, ps->fullSource);
375                 if (ps->flags & RPMFILE_GHOST) {
376                         printf("s nosource %d\n", ps->num);
377                 }
378                 ps = ps->next;
379         }
380
381         const char *arch = rpmExpand("%{_target_cpu}", NULL);
382         printf("m _target_cpu %s\n", arch);
383
384         return(0);
385 }
This page took 0.170973 seconds and 2 git commands to generate.