]> git.saurik.com Git - apple/icu.git/blame - icuSources/tools/pkgdata/pkgdata.c
ICU-3.13.tar.gz
[apple/icu.git] / icuSources / tools / pkgdata / pkgdata.c
CommitLineData
b75a7d8f
A
1/******************************************************************************
2*
3* Copyright (C) 2000-2003, International Business Machines
4* Corporation and others. All Rights Reserved.
5*
6*******************************************************************************
7* file name: pkgdata.c
8* encoding: ANSI X3.4 (1968)
9* tab size: 8 (not used)
10* indentation:4
11*
12* created on: 2000may15
13* created by: Steven \u24C7 Loomis
14*
15* This program packages the ICU data into different forms
16* (DLL, common data, etc.)
17*/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include "unicode/utypes.h"
22#include "unicode/putil.h"
23#include "cmemory.h"
24#include "cstring.h"
25#include "filestrm.h"
26#include "toolutil.h"
27#include "unewdata.h"
28#include "uoptions.h"
29
30#if U_HAVE_POPEN
31# include <unistd.h>
32#endif
33
34U_CDECL_BEGIN
35#include "pkgtypes.h"
36#include "makefile.h"
37U_CDECL_END
38
39static int executeMakefile(const UPKGOptions *o);
40static void loadLists(UPKGOptions *o, UErrorCode *status);
41
42/* always have this fcn, just might not do anything */
43static void fillInMakefileFromICUConfig(UOption *option);
44
45/* This sets the modes that are available */
46static struct
47{
48 const char *name, *alt_name;
49 UPKGMODE *fcn;
50 const char *desc;
51} modes[] =
52{
53 { "files", 0, pkg_mode_files, "Uses raw data files (no effect). Installation copies all files to the target location." },
54#ifdef WIN32
55 { "dll", "library", pkg_mode_windows, "Generates one common data file and one shared library, <package>.dll"},
56 { "common", "archive", pkg_mode_windows, "Generates just the common file, <package>.dat"},
57 { "static", "static", pkg_mode_windows, "Generates one statically linked library, " LIB_PREFIX "<package>" UDATA_LIB_SUFFIX }
58#else /*#ifdef WIN32*/
59#ifdef UDATA_SO_SUFFIX
60 { "dll", "library", pkg_mode_dll, "Generates one shared library, <package>" UDATA_SO_SUFFIX },
61#endif
62 { "common", "archive", pkg_mode_common, "Generates one common data file, <package>.dat" },
63 { "static", "static", pkg_mode_static, "Generates one statically linked library, " LIB_PREFIX "<package>" UDATA_LIB_SUFFIX }
64#endif /*#ifdef WIN32*/
65};
66
67static UOption options[]={
68 /*00*/ UOPTION_DEF( "name", 'p', UOPT_REQUIRES_ARG),
69 /*01*/ UOPTION_DEF( "bldopt", 'O', UOPT_REQUIRES_ARG), /* on Win32 it is release or debug */
70 /*02*/ UOPTION_DEF( "mode", 'm', UOPT_REQUIRES_ARG),
71 /*03*/ UOPTION_HELP_H, /* -h */
72 /*04*/ UOPTION_HELP_QUESTION_MARK, /* -? */
73 /*05*/ UOPTION_VERBOSE, /* -v */
74 /*06*/ UOPTION_COPYRIGHT, /* -c */
75 /*07*/ UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG),
76 /*08*/ UOPTION_DESTDIR, /* -d */
77 /*09*/ UOPTION_DEF( "clean", 'k', UOPT_NO_ARG),
78 /*10*/ UOPTION_DEF( "nooutput",'n', UOPT_NO_ARG),
79 /*11*/ UOPTION_DEF( "rebuild", 'F', UOPT_NO_ARG),
80 /*12*/ UOPTION_DEF( "tempdir", 'T', UOPT_REQUIRES_ARG),
81 /*13*/ UOPTION_DEF( "install", 'I', UOPT_REQUIRES_ARG),
82 /*14*/ UOPTION_SOURCEDIR ,
83 /*15*/ UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG),
84 /*16*/ UOPTION_DEF( "revision", 'r', UOPT_REQUIRES_ARG),
85 /*17*/ UOPTION_DEF( 0, 'M', UOPT_REQUIRES_ARG),
86 /*18*/ UOPTION_DEF( "force-prefix", 'f', UOPT_NO_ARG)
87};
88
89const char options_help[][160]={
90 "Set the data name",
91#ifdef WIN32
92 "R:icupath for release version or D:icupath for debug version, where icupath is the directory where ICU is located",
93#else
94 "Specify options for the builder. (Autdetected if icu-config is available)",
95#endif
96 "Specify the mode of building (see below; default: common)",
97 "This usage text",
98 "This usage text",
99 "Make the output verbose",
100 "Use the standard ICU copyright",
101 "Use a custom comment (instead of the copyright)",
102 "Specify the destination directory for files",
103 "Clean out generated & temporary files",
104 "Suppress output of data, just list files to be created",
105 "Force rebuilding of all data",
106 "Specify temporary dir (default: output dir)",
107 "Install the data (specify target)",
108 "Specify a custom source directory",
109 "Specify a custom entrypoint name (default: short name)",
110 "Specify a version when packaging in DLL or static mode",
111 "Pass the next argument to make(1)",
112 "Add package to all file names if not present"
113};
114
115const char *progname = "PKGDATA";
116
117int
118main(int argc, char* argv[]) {
119 FileStream *out;
120 UPKGOptions o;
121 CharList *tail;
122 UBool needsHelp = FALSE;
123 UErrorCode status = U_ZERO_ERROR;
124 char tmp[1024];
125 int32_t i;
126
127 U_MAIN_INIT_ARGS(argc, argv);
128
129 progname = argv[0];
130
131 options[2].value = "common";
132 options[17].value = "";
133
134 /* read command line options */
135 argc=u_parseArgs(argc, argv, sizeof(options)/sizeof(options[0]), options);
136
137 /* error handling, printing usage message */
138 /* I've decided to simply print an error and quit. This tool has too
139 many options to just display them all of the time. */
140
141 if(options[3].doesOccur || options[4].doesOccur) {
142 needsHelp = TRUE;
143 }
144 else {
145 if(!needsHelp && argc<0) {
146 fprintf(stderr,
147 "%s: error in command line argument \"%s\"\n",
148 progname,
149 argv[-argc]);
150 fprintf(stderr, "Run '%s --help' for help.\n", progname);
151 return 1;
152 }
153
154 if(!options[1].doesOccur) {
155 /* Try to fill in from icu-config or equivalent */
156 fillInMakefileFromICUConfig(&options[1]);
157 }
158
159 if(!options[1].doesOccur) {
160 fprintf(stderr, " required parameter is missing: -O is required \n");
161 fprintf(stderr, "Run '%s --help' for help.\n", progname);
162 return 1;
163 }
164
165 if(!options[0].doesOccur) /* -O we already have - don't report it. */
166 {
167 fprintf(stderr, " required parameter -p is missing \n");
168 fprintf(stderr, "Run '%s --help' for help.\n", progname);
169 return 1;
170 }
171
172 if(argc == 1) {
173 fprintf(stderr,
174 "No input files specified.\n"
175 "Run '%s --help' for help.\n", progname);
176 return 1;
177 }
178 } /* end !needsHelp */
179
180 if(argc<0 || needsHelp ) {
181 fprintf(stderr,
182 "usage: %s [-options] [-] [packageFile] \n"
183 "\tProduce packaged ICU data from the given list(s) of files.\n"
184 "\t'-' by itself means to read from stdin.\n"
185 "\tpackageFile is a text file containing the list of files to package.\n",
186 progname);
187
188 fprintf(stderr, "\n options:\n");
189 for(i=0;i<(sizeof(options)/sizeof(options[0]));i++) {
190 fprintf(stderr, "%-5s -%c %s%-10s %s\n",
191 (i<2?"[REQ]":""),
192 options[i].shortName,
193 options[i].longName ? "or --" : " ",
194 options[i].longName ? options[i].longName : "",
195 options_help[i]);
196 }
197
198 fprintf(stderr, "modes: (-m option)\n");
199 for(i=0;i<(sizeof(modes)/sizeof(modes[0]));i++) {
200 fprintf(stderr, " %-9s ", modes[i].name);
201 if (modes[i].alt_name) {
202 fprintf(stderr, "/ %-9s", modes[i].alt_name);
203 } else {
204 fprintf(stderr, " ");
205 }
206 fprintf(stderr, " %s\n", modes[i].desc);
207 }
208 return 1;
209 }
210
211 /* OK, fill in the options struct */
212 uprv_memset(&o, 0, sizeof(o));
213
214 o.mode = options[2].value;
215 o.version = options[16].doesOccur ? options[16].value : 0;
216 o.makeArgs = options[17].value;
217
218 o.fcn = NULL;
219
220 for(i=0;i<sizeof(modes)/sizeof(modes[0]);i++) {
221 if(!uprv_strcmp(modes[i].name, o.mode)) {
222 o.fcn = modes[i].fcn;
223 break;
224 } else if (modes[i].alt_name && !uprv_strcmp(modes[i].alt_name, o.mode)) {
225 o.mode = modes[i].name;
226 o.fcn = modes[i].fcn;
227 break;
228 }
229 }
230
231 if(o.fcn == NULL) {
232 fprintf(stderr, "Error: invalid mode '%s' specified. Run '%s --help' to list valid modes.\n", o.mode, progname);
233 return 1;
234 }
235
236 o.shortName = options[0].value;
237 /**/ {
238 int len = uprv_strlen(o.shortName);
239 char *csname, *cp;
240 const char *sp;
241
242 cp = csname = (char *) uprv_malloc((len + 1 + 1) * sizeof(*o.cShortName));
243 if (*(sp = o.shortName)) {
244 *cp++ = isalpha(*sp) ? * sp : '_';
245 for (++sp; *sp; ++sp) {
246 *cp++ = isalnum(*sp) ? *sp : '_';
247 }
248 }
249 *cp = 0;
250
251 o.cShortName = csname;
252 }
253
254#ifdef WIN32 /* format is R:pathtoICU or D:pathtoICU */
255 {
256 char *pathstuff = (char *)options[1].value;
257 if(options[1].value[uprv_strlen(options[1].value)-1] == '\\') {
258 pathstuff[uprv_strlen(options[1].value)-1] = '\0';
259 }
260 if(*pathstuff == 'R' || *pathstuff == 'D') {
261 o.options = pathstuff;
262 pathstuff++;
263 if(*pathstuff == ':') {
264 *pathstuff = '\0';
265 pathstuff++;
266 } else {
267 fprintf(stderr, "Error: invalid windows build mode, should be R (release) or D (debug).\n", o.mode, progname);
268 return 1;
269 }
270 } else {
271 fprintf(stderr, "Error: invalid windows build mode, should be R (release) or D (debug).\n", o.mode, progname);
272 return 1;
273 }
274 o.icuroot = pathstuff;
275 }
276#else /* on UNIX, we'll just include the file... */
277 o.options = options[1].value;
278#endif
279 o.verbose = options[5].doesOccur;
280 if(options[6].doesOccur) {
281 o.comment = U_COPYRIGHT_STRING;
282 } else if (options[7].doesOccur) {
283 o.comment = options[7].value;
284 }
285
286 if( options[8].doesOccur ) {
287 o.targetDir = options[8].value;
288 } else {
289 o.targetDir = "."; /* cwd */
290 }
291
292 o.clean = options[9].doesOccur;
293 o.nooutput = options[10].doesOccur;
294 o.rebuild = options[11].doesOccur;
295
296 if( options[12].doesOccur ) {
297 o.tmpDir = options[12].value;
298 } else {
299 o.tmpDir = o.targetDir;
300 }
301
302 if( options[13].doesOccur ) {
303 o.install = options[13].value;
304 }
305
306 if( options[14].doesOccur ) {
307 o.srcDir = options[14].value;
308 } else {
309 o.srcDir = ".";
310 }
311
312 if( options[15].doesOccur ) {
313 o.entryName = options[15].value;
314 } else {
315 o.entryName = o.cShortName;
316 }
317
318 /* OK options are set up. Now the file lists. */
319 tail = NULL;
320 for( i=1; i<argc; i++) {
321 if ( !uprv_strcmp(argv[i] , "-") ) {
322 /* stdin */
323 if( o.hadStdin == TRUE ) {
324 fprintf(stderr, "Error: can't specify '-' twice!\n"
325 "Run '%s --help' for help.\n", progname);
326 return 1;
327 }
328 o.hadStdin = TRUE;
329 }
330
331 o.fileListFiles = pkg_appendToList(o.fileListFiles, &tail, uprv_strdup(argv[i]));
332 }
333
334 /* load the files */
335 loadLists(&o, &status);
336 if( U_FAILURE(status) ) {
337 fprintf(stderr, "error loading input file lists: %s\n", u_errorName(status));
338 return 2;
339 }
340
341 /* Makefile pathname */
342 uprv_strcpy(tmp, o.tmpDir);
343 uprv_strcat(tmp, U_FILE_SEP_STRING);
344 uprv_strcat(tmp, o.shortName);
345 uprv_strcat(tmp, "_");
346 uprv_strcat(tmp, o.mode);
347 uprv_strcat(tmp, ".mak"); /* MAY NEED TO CHANGE PER PLATFORM */
348
349 o.makeFile = uprv_strdup(tmp);
350
351 out = T_FileStream_open(o.makeFile, "w");
352 if (out) {
353 pkg_mak_writeHeader(out, &o); /* need to take status */
354 o.fcn(&o, out, &status);
355 pkg_mak_writeFooter(out, &o);
356 T_FileStream_close(out);
357 } else {
358 fprintf(stderr, "warning: couldn't create %s, will use existing file if any\n", o.makeFile);
359 /*status = U_FILE_ACCESS_ERROR;*/
360 }
361
362 if(U_FAILURE(status)) {
363 fprintf(stderr, "Error creating makefile [%s]: %s\n", o.mode,
364 u_errorName(status));
365 return 1;
366 }
367
368 if(o.nooutput == TRUE) {
369 return 0; /* nothing to do. */
370 }
371
372 return executeMakefile(&o);
373}
374
375/* POSIX - execute makefile */
376static int executeMakefile(const UPKGOptions *o)
377{
378 char cmd[1024];
379 /*char pwd[1024];*/
380 const char *make;
381 int rc;
382
383 make = getenv("MAKE");
384
385 if(!make || !make[0]) {
386 make = U_MAKE;
387 }
388
389 /*getcwd(pwd, 1024);*/
390#ifdef WIN32
391 sprintf(cmd, "%s %s%s -f \"%s\" %s %s %s %s",
392 make,
393 o->install ? "INSTALLTO=" : "",
394 o->install ? o->install : "",
395 o->makeFile,
396 o->clean ? "clean" : "",
397 o->rebuild ? "rebuild" : "",
398 o->install ? "install" : "",
399 o->makeArgs);
400#elif OS400
401 sprintf(cmd, "CALL GNU/GMAKE PARM(%s%s%s '-f' '%s' %s %s %s %s)",
402 o->install ? "'INSTALLTO=" : "",
403 o->install ? o->install : "",
404 o->install ? "'" : "",
405 o->makeFile,
406 o->clean ? "'clean'" : "",
407 o->rebuild ? "'rebuild'" : "",
408 o->install ? "'install'" : "",
409 o->makeArgs);
410#else
411 sprintf(cmd, "%s %s%s -f %s %s %s %s %s",
412 make,
413 o->install ? "INSTALLTO=" : "",
414 o->install ? o->install : "",
415 o->makeFile,
416 o->clean ? "clean" : "",
417 o->rebuild ? "rebuild" : "",
418 o->install ? "install" : "",
419 o->makeArgs);
420#endif
421 if(o->verbose) {
422 puts(cmd);
423 }
424
425 rc = system(cmd);
426
427 if(rc < 0) {
428 fprintf(stderr, "# Failed, rc=%d\n", rc);
429 }
430
431 return rc < 128 ? rc : (rc >> 8);
432}
433
434
435static void loadLists(UPKGOptions *o, UErrorCode *status)
436{
437 CharList *l, *tail = NULL, *tail2 = NULL;
438 FileStream *in;
439 char line[16384];
440 char *linePtr, *lineNext;
441 const uint32_t lineMax = 16300;
442 char tmp[1024], tmp2[1024];
443 char pkgPrefix[1024];
444 int32_t pkgPrefixLen;
445 const char *baseName;
446 char *s;
447 int32_t ln;
448 UBool fixPrefix;
449
450
451 fixPrefix = options[18].doesOccur;
452
453 strcpy(pkgPrefix, o->shortName);
454 strcat(pkgPrefix, "_");
455 pkgPrefixLen=uprv_strlen(pkgPrefix);
456 for(l = o->fileListFiles; l; l = l->next) {
457 if(o->verbose) {
458 fprintf(stdout, "# Reading %s..\n", l->str);
459 }
460 /* TODO: stdin */
461 in = T_FileStream_open(l->str, "r");
462
463 if(!in) {
464 fprintf(stderr, "Error opening <%s>.\n", l->str);
465 *status = U_FILE_ACCESS_ERROR;
466 return;
467 }
468
469 ln = 0;
470
471 while(T_FileStream_readLine(in, line, sizeof(line))!=NULL) {
472 ln++;
473 if(uprv_strlen(line)>lineMax) {
474 fprintf(stderr, "%s:%d - line too long (over %d chars)\n", l->str, ln, lineMax);
475 exit(1);
476 }
477 /* remove spaces at the beginning */
478 linePtr = line;
479 while(isspace(*linePtr)) {
480 linePtr++;
481 }
482 s=linePtr;
483 /* remove trailing newline characters */
484 while(*s!=0) {
485 if(*s=='\r' || *s=='\n') {
486 *s=0;
487 break;
488 }
489 ++s;
490 }
491 if((*linePtr == 0) || (*linePtr == '#')) {
492 continue; /* comment or empty line */
493 }
494
495 /* Now, process the line */
496 lineNext = NULL;
497
498 while(linePtr && *linePtr) {
499 while(*linePtr == ' ') {
500 linePtr++;
501 }
502 /* Find the next */
503 if(linePtr[0] == '"')
504 {
505 lineNext = uprv_strchr(linePtr+1, '"');
506 if(lineNext == NULL) {
507 fprintf(stderr, "%s:%d - missing trailing double quote (\")\n",
508 l->str, ln);
509 exit(1);
510 } else {
511 lineNext++;
512 if(*lineNext) {
513 if(*lineNext != ' ') {
514 fprintf(stderr, "%s:%d - malformed quoted line at position %d, expected ' ' got '%c'\n",
515 l->str, ln, lineNext-line, (*lineNext)?*lineNext:'0');
516 exit(1);
517 }
518 *lineNext = 0;
519 lineNext++;
520 }
521 }
522 } else {
523 lineNext = uprv_strchr(linePtr, ' ');
524 if(lineNext) {
525 *lineNext = 0; /* terminate at space */
526 lineNext++;
527 }
528 }
529
530 /* add the file */
531 s = (char*)getLongPathname(linePtr);
532
533 baseName = findBasename(s);
534
535 if(s != baseName) {
536 /* s was something 'long' with a path */
537 if(fixPrefix && uprv_strncmp(pkgPrefix, baseName, pkgPrefixLen)) {
538 /* path don't have the prefix, add package prefix to short and longname */
539 uprv_strcpy(tmp, pkgPrefix);
540 uprv_strcpy(tmp+pkgPrefixLen, baseName);
541
542 uprv_strncpy(tmp2, s, uprv_strlen(s)-uprv_strlen(baseName)); /* should be: dirpath only, ending in sep */
543 tmp2[uprv_strlen(s)-uprv_strlen(baseName)]=0;
544 uprv_strcat(tmp2, pkgPrefix);
545 uprv_strcat(tmp2, baseName);
546
547 o->files = pkg_appendToList(o->files, &tail, uprv_strdup(tmp));
548 o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(tmp2));
549 } else {
550 /* paths already have the prefix */
551 o->files = pkg_appendToList(o->files, &tail, uprv_strdup(baseName));
552 o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(s));
553 }
554
555 } else { /* s was just a basename, we want to prepend source dir*/
556 /* check for prefix of package */
557 uprv_strcpy(tmp, o->srcDir);
558 uprv_strcat(tmp, o->srcDir[uprv_strlen(o->srcDir)-1]==U_FILE_SEP_CHAR?"":U_FILE_SEP_STRING);
559
560 if(fixPrefix && strncmp(pkgPrefix,s, pkgPrefixLen)) {
561 /* didn't have the prefix - add it */
562 uprv_strcat(tmp, pkgPrefix);
563 /* make up a new basename */
564 uprv_strcpy(tmp2, pkgPrefix);
565 uprv_strcat(tmp2, s);
566 o->files = pkg_appendToList(o->files, &tail, uprv_strdup(tmp2));
567 } else {
568 o->files = pkg_appendToList(o->files, &tail, uprv_strdup(baseName));
569 }
570 uprv_strcat(tmp, s);
571 o->filePaths = pkg_appendToList(o->filePaths, &tail2, uprv_strdup(tmp));
572 }
573 linePtr = lineNext;
574 }
575 }
576 T_FileStream_close(in);
577 }
578}
579
580/* Try calling icu-config directly to get information */
581void fillInMakefileFromICUConfig(UOption *option)
582{
583#if U_HAVE_POPEN
584 FILE *p;
585 size_t n;
586 static char buf[512] = "";
587 static const char cmd[] = "icu-config --incfile";
588
589 if(options[5].doesOccur)
590 {
591 /* informational */
592 fprintf(stderr, "%s: No -O option found, trying '%s'.\n", progname, cmd);
593 }
594
595 p = popen(cmd, "r");
596
597 if(p == NULL)
598 {
599 fprintf(stderr, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname);
600 return;
601 }
602
603 n = fread(buf, 1, 511, p);
604
605 pclose(p);
606
607 if(n<=0)
608 {
609 fprintf(stderr,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname);
610 return;
611 }
612
613 if(buf[strlen(buf)-1]=='\n')
614 {
615 buf[strlen(buf)-1]=0;
616 }
617
618 if(buf[0] == 0)
619 {
620 fprintf(stderr, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname);
621 return;
622 }
623
624 if(options[5].doesOccur)
625 {
626 /* informational */
627 fprintf(stderr, "%s: icu-config: using '-O %s'\n", progname, buf);
628 }
629 option->value = buf;
630 option->doesOccur = TRUE;
631#else /* ! U_HAVE_POPEN */
632
633 /* no popen available */
634 /* Put other OS specific ways to search for the Makefile.inc type
635 information or else fail.. */
636
637#endif
638}