[RTL]
[reactos.git] / reactos / tools / buildno / buildno.cpp
1 /*
2 * buildno - Generate the build number for ReactOS
3 *
4 * Copyright (c) 1999,2000 Emanuele Aliberti
5 * Copyright (c) 2006 Christoph von Wittich
6 * Copyright (c) 2008 Hervé Poussineau
7 *
8 * The build number is the day on which the build took
9 * place, as YYYYMMDD
10 *
11 * The build number is stored in the output file as a set of macros
12 *
13 * REVISIONS
14 * ---------
15 * 2008-01-12 (hpoussin)
16 * Add -t option to change the build tag
17 * 2006-09-09 (cwittich)
18 * Read binary entries files from SVN 1.4.x
19 * 2000-01-22 (ea)
20 * Fixed bugs: tm_year is (current_year - 1900),
21 * tm_month is 0-11 not 1-12 and code ignored TZ.
22 * 2000-12-10 (ea)
23 * Added -p option to make it simply print the
24 * version number, but skip buildno.h generation.
25 */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <time.h>
29 #include <string.h>
30 #include "version.h"
31 #include "xml.h"
32
33 #define FALSE 0
34 #define TRUE 1
35
36 static const char * argv0 = "";
37 static char * filename = NULL;
38 static char * build_tag = NULL;
39
40 int count_wide_string( const wchar_t *str )
41 {
42 int i;
43 for( i = 0; str[i]; i++ )
44 ;
45 return i;
46 }
47
48 long
49 GetRev(char *Revision, size_t length)
50 {
51 long revno = 0;
52 char *p;
53
54 FILE *fp = NULL;
55 fp = fopen(".svn/entries", "r");
56 if (fp != NULL)
57 {
58 if (fgets(Revision, length, fp) != NULL)
59 {
60 /* If the first character of the file is not a digit,
61 then it is probably in XML format. */
62 if (isdigit(Revision[0]))
63 {
64 while (fgets(Revision, length, fp) != NULL)
65 {
66 revno = strtol(Revision, &p, 10);
67 if (revno != 0)
68 {
69 *p = '\0';
70 fclose(fp);
71 return revno;
72 }
73 }
74 }
75 }
76 fclose(fp);
77 }
78
79 try
80 {
81 XMLElement *head;
82
83 try
84 {
85 head = XMLLoadFile(".svn/entries");
86 }
87 catch(XMLFileNotFoundException)
88 {
89 head = XMLLoadFile("_svn/entries");
90 }
91 XMLElement *entries = head->subElements[0];
92 for (size_t i = 0; i < entries->subElements.size(); i++)
93 {
94 XMLElement *entry = entries->subElements[i];
95 if ("entry" == entry->name)
96 {
97 bool GotName = false;
98 bool GotKind = false;
99 bool GotRevision = false;
100 for (size_t j = 0; j < entry->attributes.size(); j++)
101 {
102 XMLAttribute *Attribute = entry->attributes[j];
103 if ("name" == Attribute->name && "" == Attribute->value)
104 {
105 GotName = true;
106 }
107 if ("kind" == Attribute->name && "dir" == Attribute->value)
108 {
109 GotKind = true;
110 }
111 if ("revision" == Attribute->name)
112 {
113 if (length <= Attribute->value.length() + 1)
114 {
115 strcpy(Revision, "revtoobig");
116 }
117 else
118 {
119 strcpy(Revision, Attribute->value.c_str());
120 revno = strtol(Revision, NULL, 10);
121 }
122 GotRevision = true;
123 }
124 if (GotName && GotKind && GotRevision)
125 {
126 delete head;
127 return revno;
128 }
129 }
130 }
131 }
132
133 delete head;
134 }
135 catch(...)
136 {
137 ;
138 }
139
140 strcpy(Revision, "UNKNOWN");
141 return revno;
142 }
143
144 void
145 write_h (int build, char *buildstr, long revno)
146 {
147 FILE *h = NULL;
148 char* s;
149 char* s1;
150 unsigned int length;
151 int dllversion = KERNEL_VERSION_MAJOR + 42;
152
153 s1 = s = (char *) malloc(256 * 1024);
154
155 s = s + sprintf (s, "/* Do not edit - Machine generated */\n");
156
157 s = s + sprintf (s, "#ifndef _INC_REACTOS_BUILDNO\n" );
158 s = s + sprintf (s, "#define _INC_REACTOS_BUILDNO\n" );
159
160 s = s + sprintf (s, "#define KERNEL_VERSION_BUILD\t%d\n", build);
161 s = s + sprintf (s, "#define KERNEL_VERSION_BUILD_HEX\t0x%lx\n", revno);
162 s = s + sprintf (s, "#define KERNEL_VERSION_BUILD_STR\t\"%s\"\n", buildstr);
163 s = s + sprintf (s, "#define KERNEL_VERSION_BUILD_RC\t\"%s\\0\"\n", buildstr);
164 s = s + sprintf (s, "#define KERNEL_RELEASE_RC\t\"%d.%d",
165 KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR);
166 if (0 != KERNEL_VERSION_PATCH_LEVEL)
167 {
168 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
169 }
170 s = s + sprintf (s, "%s\\0\"\n", build_tag);
171 s = s + sprintf (s, "#define KERNEL_RELEASE_STR\t\"%d.%d",
172 KERNEL_VERSION_MAJOR,
173 KERNEL_VERSION_MINOR);
174 if (0 != KERNEL_VERSION_PATCH_LEVEL)
175 {
176 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
177 }
178 s = s + sprintf (s, "%s\"\n", build_tag);
179 s = s + sprintf (s, "#define KERNEL_VERSION_RC\t\"%d.%d",
180 KERNEL_VERSION_MAJOR,
181 KERNEL_VERSION_MINOR);
182 if (0 != KERNEL_VERSION_PATCH_LEVEL)
183 {
184 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
185 }
186 s = s + sprintf (s, "%s\\0\"\n", build_tag);
187 s = s + sprintf (s, "#define KERNEL_VERSION_STR\t\"%d.%d",
188 KERNEL_VERSION_MAJOR,
189 KERNEL_VERSION_MINOR);
190 if (0 != KERNEL_VERSION_PATCH_LEVEL)
191 {
192 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
193 }
194 s = s + sprintf (s, "%s\"\n", build_tag);
195 s = s + sprintf (s, "#define REACTOS_DLL_VERSION_MAJOR\t%d\n", dllversion);
196 s = s + sprintf (s, "#define REACTOS_DLL_RELEASE_RC\t\"%d.%d",
197 dllversion, KERNEL_VERSION_MINOR);
198 if (0 != KERNEL_VERSION_PATCH_LEVEL)
199 {
200 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
201 }
202 s = s + sprintf (s, "%s\\0\"\n", build_tag);
203 s = s + sprintf (s, "#define REACTOS_DLL_RELEASE_STR\t\"%d.%d",
204 dllversion,
205 KERNEL_VERSION_MINOR);
206 if (0 != KERNEL_VERSION_PATCH_LEVEL)
207 {
208 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
209 }
210 s = s + sprintf (s, "%s\"\n", build_tag);
211 s = s + sprintf (s, "#define REACTOS_DLL_VERSION_RC\t\"%d.%d",
212 dllversion,
213 KERNEL_VERSION_MINOR);
214 if (0 != KERNEL_VERSION_PATCH_LEVEL)
215 {
216 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
217 }
218 s = s + sprintf (s, "%s\\0\"\n", build_tag);
219 s = s + sprintf (s, "#define REACTOS_DLL_VERSION_STR\t\"%d.%d",
220 dllversion,
221 KERNEL_VERSION_MINOR);
222 if (0 != KERNEL_VERSION_PATCH_LEVEL)
223 {
224 s = s + sprintf (s, ".%d", KERNEL_VERSION_PATCH_LEVEL);
225 }
226 s = s + sprintf (s, "%s\"\n", build_tag);
227 s = s + sprintf (s, "#endif\n/* EOF */\n");
228
229 h = fopen (filename, "rb");
230 if (h != NULL)
231 {
232 fseek(h, 0, SEEK_END);
233 length = ftell(h);
234 if (length == strlen(s1))
235 {
236 char* orig;
237
238 orig = (char *) malloc(length);
239 if (orig == NULL)
240 {
241 fclose(h);
242 free(s1);
243 return;
244 }
245 fseek(h, 0, SEEK_SET);
246 fread(orig, 1, length, h);
247 if (memcmp(s1, orig, length) == 0)
248 {
249 fclose(h);
250 free(s1);
251 free(orig);
252 return;
253 }
254 free(orig);
255 }
256 fclose(h);
257 }
258
259 h = fopen (filename, "wb");
260 if (!h)
261 {
262 fprintf (stderr,
263 "%s: can not create file \"%s\"!\n",
264 argv0,
265 filename);
266 free(s1);
267 return;
268 }
269 fwrite(s1, 1, strlen(s1), h);
270 fclose(h);
271 }
272
273 void
274 usage (void)
275 {
276 fprintf (
277 stderr,
278 "Usage: %s [-{p|q}] [-t tag] path-to-header\n\n"
279 " -p print version number and exit\n"
280 " -q run in quiet mode\n"
281 " -t specify a build tag\n",
282 argv0);
283 exit (EXIT_SUCCESS);
284 }
285
286
287 int
288 main (int argc, char * argv [])
289 {
290 int i, length;
291 int print_only = FALSE;
292 int quiet = FALSE;
293
294 int build = 0;
295 long revno;
296 char buildstr[64], revision[10];
297
298 time_t t1 = 0;
299 struct tm * t1_tm = NULL;
300
301 argv0 = argv[0];
302
303 /* Check arguments */
304 for (i = 1; i < argc; i++)
305 {
306 if (*argv[i] == '-')
307 {
308 switch (argv[i][1])
309 {
310 case 'p':
311 print_only = TRUE;
312 break;
313 case 'q':
314 quiet = TRUE;
315 break;
316 case 't':
317 if (i + 1 != argc)
318 {
319 build_tag = argv[++i];
320 break;
321 }
322 /* fall through */
323 default:
324 usage();
325 return EXIT_SUCCESS;
326 }
327 }
328 else if (!filename)
329 filename = argv[i];
330 else
331 {
332 usage();
333 return EXIT_SUCCESS;
334 }
335 }
336 if (!filename)
337 {
338 usage();
339 return EXIT_SUCCESS;
340 }
341
342 /* Set TZ information. */
343 tzset ();
344 /* We are building TODAY! */
345 if (! quiet)
346 {
347 printf ( "\nReactOS Build Number Generator\n\n");
348 }
349
350 time (& t1); /* current build time */
351 t1_tm = gmtime (& t1);
352
353 t1_tm->tm_year += 1900;
354 if (! quiet)
355 {
356 printf (
357 "Current date: %4d-%02d-%02d\n\n",
358 t1_tm->tm_year,
359 (t1_tm->tm_mon + 1),
360 t1_tm->tm_mday);
361 }
362
363 /* Compute build number. */
364 build = t1_tm->tm_year * 10000 + (t1_tm->tm_mon + 1) * 100 + t1_tm->tm_mday;
365
366 if (!build_tag)
367 {
368 /* Create default build tag */
369 length = count_wide_string(KERNEL_VERSION_BUILD_TYPE);
370 build_tag = (char *)malloc(length+2);
371 if (length > 0)
372 {
373 build_tag[0] = '-';
374 for (i = 0; KERNEL_VERSION_BUILD_TYPE[i]; i++)
375 {
376 build_tag[i + 1] = KERNEL_VERSION_BUILD_TYPE[i];
377 }
378 build_tag[i+1] = 0;
379 }
380 else
381 build_tag[0] = 0;
382 }
383 else if (*build_tag)
384 {
385 /* Prepend '-' */
386 length = strlen(build_tag);
387 char *new_build_tag = (char *)malloc(length + 2);
388 strcpy(new_build_tag, "-");
389 strcat(new_build_tag, build_tag);
390 build_tag = new_build_tag;
391 }
392
393 revno = GetRev(revision, sizeof(revision));
394 sprintf(buildstr, "%d-r%s", build, revision);
395
396 if (! quiet)
397 {
398 printf (
399 "ROS Version : %d.%d",
400 KERNEL_VERSION_MAJOR,
401 KERNEL_VERSION_MINOR);
402 if (0 != KERNEL_VERSION_PATCH_LEVEL)
403 {
404 printf(".%d", KERNEL_VERSION_PATCH_LEVEL);
405 }
406 printf("%s (Build %s)\n\n", build_tag, buildstr);
407 }
408 /* (Over)write the include file, unless the user switched on -p. */
409 if (! print_only)
410 {
411 write_h (build, buildstr, revno);
412 }
413 else
414 {
415 printf ("%s: no code generated", argv [0]);
416 }
417
418 return EXIT_SUCCESS;
419 }
420
421 /* EOF */