1 /******************************************************************************
3 * Copyright (C) 2000-2003, International Business Machines
4 * Corporation and others. All Rights Reserved.
6 *******************************************************************************
8 * encoding: ANSI X3.4 (1968)
9 * tab size: 8 (not used)
12 * created on: 2000may15
13 * created by: Steven \u24C7 Loomis
15 * This program packages the ICU data into different forms
16 * (DLL, common data, etc.)
21 #include "unicode/utypes.h"
22 #include "unicode/putil.h"
39 static int executeMakefile(const UPKGOptions
*o
);
40 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
);
42 /* always have this fcn, just might not do anything */
43 static void fillInMakefileFromICUConfig(UOption
*option
);
45 /* This sets the modes that are available */
48 const char *name
, *alt_name
;
53 { "files", 0, pkg_mode_files
, "Uses raw data files (no effect). Installation copies all files to the target location." },
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
},
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*/
67 static 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
)
89 const char options_help
[][160]={
92 "R:icupath for release version or D:icupath for debug version, where icupath is the directory where ICU is located",
94 "Specify options for the builder. (Autdetected if icu-config is available)",
96 "Specify the mode of building (see below; default: common)",
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"
115 const char *progname
= "PKGDATA";
118 main(int argc
, char* argv
[]) {
122 UBool needsHelp
= FALSE
;
123 UErrorCode status
= U_ZERO_ERROR
;
127 U_MAIN_INIT_ARGS(argc
, argv
);
131 options
[2].value
= "common";
132 options
[17].value
= "";
134 /* read command line options */
135 argc
=u_parseArgs(argc
, argv
, sizeof(options
)/sizeof(options
[0]), options
);
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. */
141 if(options
[3].doesOccur
|| options
[4].doesOccur
) {
145 if(!needsHelp
&& argc
<0) {
147 "%s: error in command line argument \"%s\"\n",
150 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
154 if(!options
[1].doesOccur
) {
155 /* Try to fill in from icu-config or equivalent */
156 fillInMakefileFromICUConfig(&options
[1]);
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
);
165 if(!options
[0].doesOccur
) /* -O we already have - don't report it. */
167 fprintf(stderr
, " required parameter -p is missing \n");
168 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
174 "No input files specified.\n"
175 "Run '%s --help' for help.\n", progname
);
178 } /* end !needsHelp */
180 if(argc
<0 || needsHelp
) {
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",
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",
192 options
[i
].shortName
,
193 options
[i
].longName
? "or --" : " ",
194 options
[i
].longName
? options
[i
].longName
: "",
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
);
204 fprintf(stderr
, " ");
206 fprintf(stderr
, " %s\n", modes
[i
].desc
);
211 /* OK, fill in the options struct */
212 uprv_memset(&o
, 0, sizeof(o
));
214 o
.mode
= options
[2].value
;
215 o
.version
= options
[16].doesOccur
? options
[16].value
: 0;
216 o
.makeArgs
= options
[17].value
;
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
;
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
;
232 fprintf(stderr
, "Error: invalid mode '%s' specified. Run '%s --help' to list valid modes.\n", o
.mode
, progname
);
236 o
.shortName
= options
[0].value
;
238 int len
= uprv_strlen(o
.shortName
);
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
: '_';
251 o
.cShortName
= csname
;
254 #ifdef WIN32 /* format is R:pathtoICU or D:pathtoICU */
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';
260 if(*pathstuff
== 'R' || *pathstuff
== 'D') {
261 o
.options
= pathstuff
;
263 if(*pathstuff
== ':') {
267 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n", o
.mode
, progname
);
271 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n", o
.mode
, progname
);
274 o
.icuroot
= pathstuff
;
276 #else /* on UNIX, we'll just include the file... */
277 o
.options
= options
[1].value
;
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
;
286 if( options
[8].doesOccur
) {
287 o
.targetDir
= options
[8].value
;
289 o
.targetDir
= "."; /* cwd */
292 o
.clean
= options
[9].doesOccur
;
293 o
.nooutput
= options
[10].doesOccur
;
294 o
.rebuild
= options
[11].doesOccur
;
296 if( options
[12].doesOccur
) {
297 o
.tmpDir
= options
[12].value
;
299 o
.tmpDir
= o
.targetDir
;
302 if( options
[13].doesOccur
) {
303 o
.install
= options
[13].value
;
306 if( options
[14].doesOccur
) {
307 o
.srcDir
= options
[14].value
;
312 if( options
[15].doesOccur
) {
313 o
.entryName
= options
[15].value
;
315 o
.entryName
= o
.cShortName
;
318 /* OK options are set up. Now the file lists. */
320 for( i
=1; i
<argc
; i
++) {
321 if ( !uprv_strcmp(argv
[i
] , "-") ) {
323 if( o
.hadStdin
== TRUE
) {
324 fprintf(stderr
, "Error: can't specify '-' twice!\n"
325 "Run '%s --help' for help.\n", progname
);
331 o
.fileListFiles
= pkg_appendToList(o
.fileListFiles
, &tail
, uprv_strdup(argv
[i
]));
335 loadLists(&o
, &status
);
336 if( U_FAILURE(status
) ) {
337 fprintf(stderr
, "error loading input file lists: %s\n", u_errorName(status
));
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 */
349 o
.makeFile
= uprv_strdup(tmp
);
351 out
= T_FileStream_open(o
.makeFile
, "w");
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
);
358 fprintf(stderr
, "warning: couldn't create %s, will use existing file if any\n", o
.makeFile
);
359 /*status = U_FILE_ACCESS_ERROR;*/
362 if(U_FAILURE(status
)) {
363 fprintf(stderr
, "Error creating makefile [%s]: %s\n", o
.mode
,
364 u_errorName(status
));
368 if(o
.nooutput
== TRUE
) {
369 return 0; /* nothing to do. */
372 return executeMakefile(&o
);
375 /* POSIX - execute makefile */
376 static int executeMakefile(const UPKGOptions
*o
)
383 make
= getenv("MAKE");
385 if(!make
|| !make
[0]) {
389 /*getcwd(pwd, 1024);*/
391 sprintf(cmd
, "%s %s%s -f \"%s\" %s %s %s %s",
393 o
->install
? "INSTALLTO=" : "",
394 o
->install
? o
->install
: "",
396 o
->clean
? "clean" : "",
397 o
->rebuild
? "rebuild" : "",
398 o
->install
? "install" : "",
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
? "'" : "",
406 o
->clean
? "'clean'" : "",
407 o
->rebuild
? "'rebuild'" : "",
408 o
->install
? "'install'" : "",
411 sprintf(cmd
, "%s %s%s -f %s %s %s %s %s",
413 o
->install
? "INSTALLTO=" : "",
414 o
->install
? o
->install
: "",
416 o
->clean
? "clean" : "",
417 o
->rebuild
? "rebuild" : "",
418 o
->install
? "install" : "",
428 fprintf(stderr
, "# Failed, rc=%d\n", rc
);
431 return rc
< 128 ? rc
: (rc
>> 8);
435 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
)
437 CharList
*l
, *tail
= NULL
, *tail2
= NULL
;
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
;
451 fixPrefix
= options
[18].doesOccur
;
453 strcpy(pkgPrefix
, o
->shortName
);
454 strcat(pkgPrefix
, "_");
455 pkgPrefixLen
=uprv_strlen(pkgPrefix
);
456 for(l
= o
->fileListFiles
; l
; l
= l
->next
) {
458 fprintf(stdout
, "# Reading %s..\n", l
->str
);
461 in
= T_FileStream_open(l
->str
, "r");
464 fprintf(stderr
, "Error opening <%s>.\n", l
->str
);
465 *status
= U_FILE_ACCESS_ERROR
;
471 while(T_FileStream_readLine(in
, line
, sizeof(line
))!=NULL
) {
473 if(uprv_strlen(line
)>lineMax
) {
474 fprintf(stderr
, "%s:%d - line too long (over %d chars)\n", l
->str
, ln
, lineMax
);
477 /* remove spaces at the beginning */
479 while(isspace(*linePtr
)) {
483 /* remove trailing newline characters */
485 if(*s
=='\r' || *s
=='\n') {
491 if((*linePtr
== 0) || (*linePtr
== '#')) {
492 continue; /* comment or empty line */
495 /* Now, process the line */
498 while(linePtr
&& *linePtr
) {
499 while(*linePtr
== ' ') {
503 if(linePtr
[0] == '"')
505 lineNext
= uprv_strchr(linePtr
+1, '"');
506 if(lineNext
== NULL
) {
507 fprintf(stderr
, "%s:%d - missing trailing double quote (\")\n",
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');
523 lineNext
= uprv_strchr(linePtr
, ' ');
525 *lineNext
= 0; /* terminate at space */
531 s
= (char*)getLongPathname(linePtr
);
533 baseName
= findBasename(s
);
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
);
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
);
547 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(tmp
));
548 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(tmp2
));
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
));
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
);
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
));
568 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(baseName
));
571 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(tmp
));
576 T_FileStream_close(in
);
580 /* Try calling icu-config directly to get information */
581 void fillInMakefileFromICUConfig(UOption
*option
)
586 static char buf
[512] = "";
587 static const char cmd
[] = "icu-config --incfile";
589 if(options
[5].doesOccur
)
592 fprintf(stderr
, "%s: No -O option found, trying '%s'.\n", progname
, cmd
);
599 fprintf(stderr
, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname
);
603 n
= fread(buf
, 1, 511, p
);
609 fprintf(stderr
,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname
);
613 if(buf
[strlen(buf
)-1]=='\n')
615 buf
[strlen(buf
)-1]=0;
620 fprintf(stderr
, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname
);
624 if(options
[5].doesOccur
)
627 fprintf(stderr
, "%s: icu-config: using '-O %s'\n", progname
, buf
);
630 option
->doesOccur
= TRUE
;
631 #else /* ! U_HAVE_POPEN */
633 /* no popen available */
634 /* Put other OS specific ways to search for the Makefile.inc type
635 information or else fail.. */