1 /******************************************************************************
3 * Copyright (C) 2000-2006, 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.)
19 #include "unicode/utypes.h"
20 #include "unicode/putil.h"
25 #include "unicode/uclean.h"
32 We define __USE_POSIX2 so that we can get popen and pclose when
33 --enable-strict is used
36 # define __USE_POSIX2 1
48 static int executeMakefile(const UPKGOptions
*o
);
49 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
);
51 /* always have this fcn, just might not do anything */
52 static void fillInMakefileFromICUConfig(UOption
*option
);
54 /* This sets the modes that are available */
57 const char *name
, *alt_name
;
62 { "files", 0, pkg_mode_files
, "Uses raw data files (no effect). Installation copies all files to the target location." },
63 #ifdef U_MAKE_IS_NMAKE
64 { "dll", "library", pkg_mode_windows
, "Generates one common data file and one shared library, <package>.dll"},
65 { "common", "archive", pkg_mode_windows
, "Generates just the common file, <package>.dat"},
66 { "static", "static", pkg_mode_windows
, "Generates one statically linked library, " LIB_PREFIX
"<package>" UDATA_LIB_SUFFIX
}
67 #else /*#ifdef U_MAKE_IS_NMAKE*/
68 #ifdef UDATA_SO_SUFFIX
69 { "dll", "library", pkg_mode_dll
, "Generates one shared library, <package>" UDATA_SO_SUFFIX
},
71 { "common", "archive", pkg_mode_common
, "Generates one common data file, <package>.dat" },
72 { "static", "static", pkg_mode_static
, "Generates one statically linked library, " LIB_PREFIX
"<package>" UDATA_LIB_SUFFIX
}
73 #endif /*#ifdef U_MAKE_IS_NMAKE*/
100 static UOption options
[]={
101 /*00*/ UOPTION_DEF( "name", 'p', UOPT_REQUIRES_ARG
),
102 /*01*/ UOPTION_DEF( "bldopt", 'O', UOPT_REQUIRES_ARG
), /* on Win32 it is release or debug */
103 /*02*/ UOPTION_DEF( "mode", 'm', UOPT_REQUIRES_ARG
),
104 /*03*/ UOPTION_HELP_H
, /* -h */
105 /*04*/ UOPTION_HELP_QUESTION_MARK
, /* -? */
106 /*05*/ UOPTION_VERBOSE
, /* -v */
107 /*06*/ UOPTION_COPYRIGHT
, /* -c */
108 /*07*/ UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG
),
109 /*08*/ UOPTION_DESTDIR
, /* -d */
110 /*09*/ UOPTION_DEF( "clean", 'k', UOPT_NO_ARG
),
111 /*10*/ UOPTION_DEF( "nooutput",'n', UOPT_NO_ARG
),
112 /*11*/ UOPTION_DEF( "rebuild", 'F', UOPT_NO_ARG
),
113 /*12*/ UOPTION_DEF( "tempdir", 'T', UOPT_REQUIRES_ARG
),
114 /*13*/ UOPTION_DEF( "install", 'I', UOPT_REQUIRES_ARG
),
115 /*14*/ UOPTION_SOURCEDIR
,
116 /*15*/ UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG
),
117 /*16*/ UOPTION_DEF( "revision", 'r', UOPT_REQUIRES_ARG
),
118 /*17*/ UOPTION_DEF( "makearg", 'M', UOPT_REQUIRES_ARG
),
119 /*18*/ UOPTION_DEF( "force-prefix", 'f', UOPT_NO_ARG
),
120 /*19*/ UOPTION_DEF( "libname", 'L', UOPT_REQUIRES_ARG
),
121 /*20*/ UOPTION_DEF( "quiet", 'q', UOPT_NO_ARG
)
124 const char options_help
[][320]={
126 #ifdef U_MAKE_IS_NMAKE
127 "The directory where the ICU is located (e.g. <ICUROOT> which contains the bin directory)",
129 "Specify options for the builder. (Autdetected if icu-config is available)",
131 "Specify the mode of building (see below; default: common)",
134 "Make the output verbose",
135 "Use the standard ICU copyright",
136 "Use a custom comment (instead of the copyright)",
137 "Specify the destination directory for files",
138 "Clean out generated & temporary files",
139 "Suppress output of data, just list files to be created",
140 "Force rebuilding of all data",
141 "Specify temporary dir (default: output dir)",
142 "Install the data (specify target)",
143 "Specify a custom source directory",
144 "Specify a custom entrypoint name (default: short name)",
145 "Specify a version when packaging in DLL or static mode",
146 "Pass the next argument to make(1)",
147 "Add package to all file names if not present",
148 "Library name to build (if different than package name)",
149 "Quite mode. (e.g. Do not output a readme file for static libraries)"
152 const char *progname
= "PKGDATA";
155 main(int argc
, char* argv
[]) {
159 UBool needsHelp
= FALSE
;
160 UErrorCode status
= U_ZERO_ERROR
;
164 U_MAIN_INIT_ARGS(argc
, argv
);
168 options
[MODE
].value
= "common";
169 options
[MAKEARG
].value
= "";
171 /* read command line options */
172 argc
=u_parseArgs(argc
, argv
, sizeof(options
)/sizeof(options
[0]), options
);
174 /* error handling, printing usage message */
175 /* I've decided to simply print an error and quit. This tool has too
176 many options to just display them all of the time. */
178 if(options
[HELP
].doesOccur
|| options
[HELP_QUESTION_MARK
].doesOccur
) {
182 if(!needsHelp
&& argc
<0) {
184 "%s: error in command line argument \"%s\"\n",
187 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
191 if(!options
[BLDOPT
].doesOccur
) {
192 /* Try to fill in from icu-config or equivalent */
193 fillInMakefileFromICUConfig(&options
[1]);
195 #ifdef U_MAKE_IS_NMAKE
197 fprintf(stderr
, "Warning: You are using the deprecated -O option\n"
198 "\tYou can fix this warning by installing pkgdata, gencmn and genccode\n"
199 "\tinto the same directory and not specifying the -O option to pkgdata.\n");
203 if(!options
[BLDOPT
].doesOccur
) {
204 fprintf(stderr
, " required parameter is missing: -O is required \n");
205 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
209 if(!options
[NAME
].doesOccur
) /* -O we already have - don't report it. */
211 fprintf(stderr
, " required parameter -p is missing \n");
212 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
218 "No input files specified.\n"
219 "Run '%s --help' for help.\n", progname
);
222 } /* end !needsHelp */
224 if(argc
<0 || needsHelp
) {
226 "usage: %s [-options] [-] [packageFile] \n"
227 "\tProduce packaged ICU data from the given list(s) of files.\n"
228 "\t'-' by itself means to read from stdin.\n"
229 "\tpackageFile is a text file containing the list of files to package.\n",
232 fprintf(stderr
, "\n options:\n");
233 for(i
=0;i
<(sizeof(options
)/sizeof(options
[0]));i
++) {
234 fprintf(stderr
, "%-5s -%c %s%-10s %s\n",
236 options
[i
].shortName
,
237 options
[i
].longName
? "or --" : " ",
238 options
[i
].longName
? options
[i
].longName
: "",
242 fprintf(stderr
, "modes: (-m option)\n");
243 for(i
=0;i
<(sizeof(modes
)/sizeof(modes
[0]));i
++) {
244 fprintf(stderr
, " %-9s ", modes
[i
].name
);
245 if (modes
[i
].alt_name
) {
246 fprintf(stderr
, "/ %-9s", modes
[i
].alt_name
);
248 fprintf(stderr
, " ");
250 fprintf(stderr
, " %s\n", modes
[i
].desc
);
255 /* OK, fill in the options struct */
256 uprv_memset(&o
, 0, sizeof(o
));
258 o
.mode
= options
[MODE
].value
;
259 o
.version
= options
[REVISION
].doesOccur
? options
[REVISION
].value
: 0;
260 o
.makeArgs
= options
[MAKEARG
].value
;
264 for(i
=0;i
<sizeof(modes
)/sizeof(modes
[0]);i
++) {
265 if(!uprv_strcmp(modes
[i
].name
, o
.mode
)) {
266 o
.fcn
= modes
[i
].fcn
;
268 } else if (modes
[i
].alt_name
&& !uprv_strcmp(modes
[i
].alt_name
, o
.mode
)) {
269 o
.mode
= modes
[i
].name
;
270 o
.fcn
= modes
[i
].fcn
;
276 fprintf(stderr
, "Error: invalid mode '%s' specified. Run '%s --help' to list valid modes.\n", o
.mode
, progname
);
280 o
.shortName
= options
[0].value
;
282 int32_t len
= (int32_t)uprv_strlen(o
.shortName
);
286 cp
= csname
= (char *) uprv_malloc((len
+ 1 + 1) * sizeof(*o
.cShortName
));
287 if (*(sp
= o
.shortName
)) {
288 *cp
++ = isalpha(*sp
) ? * sp
: '_';
289 for (++sp
; *sp
; ++sp
) {
290 *cp
++ = isalnum(*sp
) ? *sp
: '_';
295 o
.cShortName
= csname
;
298 if(options
[LIBNAME
].doesOccur
) { /* get libname from shortname, or explicit -L parameter */
299 o
.libName
= options
[LIBNAME
].value
;
301 o
.libName
= o
.shortName
;
304 if(options
[QUIET
].doesOccur
) {
310 o
.verbose
= options
[VERBOSE
].doesOccur
;
311 #ifdef U_MAKE_IS_NMAKE /* format is R:pathtoICU or D:pathtoICU */
313 char *pathstuff
= (char *)options
[BLDOPT
].value
;
314 if(options
[1].value
[uprv_strlen(options
[BLDOPT
].value
)-1] == '\\') {
315 pathstuff
[uprv_strlen(options
[BLDOPT
].value
)-1] = '\0';
317 if(*pathstuff
== PKGDATA_DERIVED_PATH
|| *pathstuff
== 'R' || *pathstuff
== 'D') {
318 o
.options
= pathstuff
;
320 if(*pathstuff
== ':') {
325 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n");
329 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n");
332 o
.icuroot
= pathstuff
;
334 fprintf(stdout
, "# ICUROOT is %s\n", o
.icuroot
);
337 #else /* on UNIX, we'll just include the file... */
338 o
.options
= options
[BLDOPT
].value
;
340 if(options
[COPYRIGHT
].doesOccur
) {
341 o
.comment
= U_COPYRIGHT_STRING
;
342 } else if (options
[COMMENT
].doesOccur
) {
343 o
.comment
= options
[COMMENT
].value
;
346 if( options
[DESTDIR
].doesOccur
) {
347 o
.targetDir
= options
[DESTDIR
].value
;
349 o
.targetDir
= "."; /* cwd */
352 o
.clean
= options
[CLEAN
].doesOccur
;
353 o
.nooutput
= options
[NOOUTPUT
].doesOccur
;
354 o
.rebuild
= options
[REBUILD
].doesOccur
;
356 if( options
[TEMPDIR
].doesOccur
) {
357 o
.tmpDir
= options
[TEMPDIR
].value
;
359 o
.tmpDir
= o
.targetDir
;
362 if( options
[INSTALL
].doesOccur
) {
363 o
.install
= options
[INSTALL
].value
;
366 if( options
[SOURCEDIR
].doesOccur
) {
367 o
.srcDir
= options
[SOURCEDIR
].value
;
372 if( options
[ENTRYPOINT
].doesOccur
) {
373 o
.entryName
= options
[ENTRYPOINT
].value
;
375 o
.entryName
= o
.cShortName
;
378 /* OK options are set up. Now the file lists. */
380 for( i
=1; i
<argc
; i
++) {
381 if ( !uprv_strcmp(argv
[i
] , "-") ) {
383 if( o
.hadStdin
== TRUE
) {
384 fprintf(stderr
, "Error: can't specify '-' twice!\n"
385 "Run '%s --help' for help.\n", progname
);
391 o
.fileListFiles
= pkg_appendToList(o
.fileListFiles
, &tail
, uprv_strdup(argv
[i
]));
395 loadLists(&o
, &status
);
396 if( U_FAILURE(status
) ) {
397 fprintf(stderr
, "error loading input file lists: %s\n", u_errorName(status
));
401 /* Makefile pathname */
402 uprv_strcpy(tmp
, o
.tmpDir
);
403 #ifdef U_MAKE_IS_NMAKE
404 uprv_strcat(tmp
, U_FILE_SEP_STRING
);
406 uprv_strcat(tmp
, U_FILE_ALT_SEP_STRING
);
408 uprv_strcat(tmp
, o
.shortName
);
409 uprv_strcat(tmp
, "_");
410 uprv_strcat(tmp
, o
.mode
);
411 uprv_strcat(tmp
, ".mak"); /* MAY NEED TO CHANGE PER PLATFORM */
413 o
.makeFile
= uprv_strdup(tmp
);
415 out
= T_FileStream_open(o
.makeFile
, "w");
417 pkg_mak_writeHeader(out
, &o
); /* need to take status */
418 o
.fcn(&o
, out
, &status
);
419 pkg_mak_writeFooter(out
, &o
);
420 T_FileStream_close(out
);
422 fprintf(stderr
, "warning: couldn't create %s, will use existing file if any\n", o
.makeFile
);
423 /*status = U_FILE_ACCESS_ERROR;*/
426 if(U_FAILURE(status
)) {
427 fprintf(stderr
, "Error creating makefile [%s]: %s\n", o
.mode
,
428 u_errorName(status
));
432 if(o
.nooutput
== TRUE
) {
433 return 0; /* nothing to do. */
436 return executeMakefile(&o
);
439 /* POSIX - execute makefile */
440 static int executeMakefile(const UPKGOptions
*o
)
447 make
= getenv("MAKE");
449 if(!make
|| !make
[0]) {
453 /*getcwd(pwd, 1024);*/
455 sprintf(cmd
, "%s %s%s -f \"%s\" %s %s %s %s",
457 o
->install
? "INSTALLTO=" : "",
458 o
->install
? o
->install
: "",
460 o
->clean
? "clean" : "",
461 o
->rebuild
? "rebuild" : "",
462 o
->install
? "install" : "",
465 sprintf(cmd
, "CALL GNU/GMAKE PARM(%s%s%s '-f' '%s' %s %s %s %s%s%s)",
466 o
->install
? "'INSTALLTO=" : "",
467 o
->install
? o
->install
: "",
468 o
->install
? "'" : "",
470 o
->clean
? "'clean'" : "",
471 o
->rebuild
? "'rebuild'" : "",
472 o
->install
? "'install'" : "",
473 o
->makeArgs
&& *o
->makeArgs
? "'" : "",
474 o
->makeArgs
&& *o
->makeArgs
? o
->makeArgs
: "",
475 o
->makeArgs
&& *o
->makeArgs
? "'" : "");
477 sprintf(cmd
, "%s %s%s -f %s %s %s %s %s",
479 o
->install
? "INSTALLTO=" : "",
480 o
->install
? o
->install
: "",
482 o
->clean
? "clean" : "",
483 o
->rebuild
? "rebuild" : "",
484 o
->install
? "install" : "",
494 fprintf(stderr
, "# Failed, rc=%d\n", rc
);
497 return rc
< 128 ? rc
: (rc
>> 8);
501 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
)
503 CharList
*l
, *tail
= NULL
, *tail2
= NULL
;
506 char *linePtr
, *lineNext
;
507 const uint32_t lineMax
= 16300;
510 int32_t ln
=0; /* line number */
512 for(l
= o
->fileListFiles
; l
; l
= l
->next
) {
514 fprintf(stdout
, "# Reading %s..\n", l
->str
);
517 in
= T_FileStream_open(l
->str
, "r"); /* open files list */
520 fprintf(stderr
, "Error opening <%s>.\n", l
->str
);
521 *status
= U_FILE_ACCESS_ERROR
;
525 while(T_FileStream_readLine(in
, line
, sizeof(line
))!=NULL
) { /* for each line */
527 if(uprv_strlen(line
)>lineMax
) {
528 fprintf(stderr
, "%s:%d - line too long (over %d chars)\n", l
->str
, (int)ln
, (int)lineMax
);
531 /* remove spaces at the beginning */
533 while(isspace(*linePtr
)) {
537 /* remove trailing newline characters */
539 if(*s
=='\r' || *s
=='\n') {
545 if((*linePtr
== 0) || (*linePtr
== '#')) {
546 continue; /* comment or empty line */
549 /* Now, process the line */
552 while(linePtr
&& *linePtr
) { /* process space-separated items */
553 while(*linePtr
== ' ') {
556 /* Find the next quote */
557 if(linePtr
[0] == '"')
559 lineNext
= uprv_strchr(linePtr
+1, '"');
560 if(lineNext
== NULL
) {
561 fprintf(stderr
, "%s:%d - missing trailing double quote (\")\n",
567 if(*lineNext
!= ' ') {
568 fprintf(stderr
, "%s:%d - malformed quoted line at position %d, expected ' ' got '%c'\n",
569 l
->str
, (int)ln
, (int)(lineNext
-line
), (*lineNext
)?*lineNext
:'0');
577 lineNext
= uprv_strchr(linePtr
, ' ');
579 *lineNext
= 0; /* terminate at space */
585 s
= (char*)getLongPathname(linePtr
);
587 /* normal mode.. o->files is just the bare list without package names */
588 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(linePtr
));
589 if(uprv_pathIsAbsolute(s
)) {
590 fprintf(stderr
, "pkgdata: Error: absolute path encountered. Old style paths are not supported. Use relative paths such as 'fur.res' or 'translit%cfur.res'.\n\tBad path: '%s'\n", U_FILE_SEP_CHAR
, s
);
591 exit(U_ILLEGAL_ARGUMENT_ERROR
);
593 uprv_strcpy(tmp
, o
->srcDir
);
594 uprv_strcat(tmp
, o
->srcDir
[uprv_strlen(o
->srcDir
)-1]==U_FILE_SEP_CHAR
?"":U_FILE_SEP_STRING
);
596 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(tmp
));
598 } /* for each entry on line */
599 } /* for each line */
600 T_FileStream_close(in
);
601 } /* for each file list file */
604 /* Try calling icu-config directly to get information */
605 static void fillInMakefileFromICUConfig(UOption
*option
)
610 static char buf
[512] = "";
611 static const char cmd
[] = "icu-config --incfile";
613 if(options
[5].doesOccur
)
616 fprintf(stderr
, "%s: No -O option found, trying '%s'.\n", progname
, cmd
);
623 fprintf(stderr
, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname
);
627 n
= fread(buf
, 1, 511, p
);
633 fprintf(stderr
,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname
);
637 if(buf
[strlen(buf
)-1]=='\n')
639 buf
[strlen(buf
)-1]=0;
644 fprintf(stderr
, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname
);
648 if(options
[5].doesOccur
)
651 fprintf(stderr
, "%s: icu-config: using '-O %s'\n", progname
, buf
);
654 option
->doesOccur
= TRUE
;
655 #else /* ! U_HAVE_POPEN */
658 char pathbuffer
[_MAX_PATH
] = {0};
659 char *fullEXEpath
= NULL
;
660 char *pathstuff
= NULL
;
662 if (strchr(progname
, U_FILE_SEP_CHAR
) != NULL
|| strchr(progname
, U_FILE_ALT_SEP_CHAR
) != NULL
) {
663 /* pkgdata was executed with relative path */
664 fullEXEpath
= _fullpath(pathbuffer
, progname
, sizeof(pathbuffer
));
665 pathstuff
= (char *)options
[1].value
;
668 pathstuff
= strrchr(fullEXEpath
, U_FILE_SEP_CHAR
);
671 uprv_memmove(fullEXEpath
+ 2, fullEXEpath
, uprv_strlen(fullEXEpath
)+1);
672 fullEXEpath
[0] = PKGDATA_DERIVED_PATH
;
673 fullEXEpath
[1] = ':';
674 option
->value
= uprv_strdup(fullEXEpath
);
675 option
->doesOccur
= TRUE
;
680 /* pkgdata was executed from the path */
681 /* Search for file in PATH environment variable: */
682 _searchenv("pkgdata.exe", "PATH", pathbuffer
);
683 if( *pathbuffer
!= '\0' ) {
684 fullEXEpath
= pathbuffer
;
685 pathstuff
= strrchr(pathbuffer
, U_FILE_SEP_CHAR
);
688 uprv_memmove(fullEXEpath
+ 2, fullEXEpath
, uprv_strlen(fullEXEpath
)+1);
689 fullEXEpath
[0] = PKGDATA_DERIVED_PATH
;
690 fullEXEpath
[1] = ':';
691 option
->value
= uprv_strdup(fullEXEpath
);
692 option
->doesOccur
= TRUE
;
696 /* else can't determine the path */
699 /* no popen available */
700 /* Put other OS specific ways to search for the Makefile.inc type
701 information or else fail.. */