1 /******************************************************************************
2 * Copyright (C) 2000-2007, International Business Machines
3 * Corporation and others. All Rights Reserved.
4 *******************************************************************************
6 * encoding: ANSI X3.4 (1968)
7 * tab size: 8 (not used)
10 * created on: 2000may15
11 * created by: Steven \u24C7 Loomis
13 * This program packages the ICU data into different forms
14 * (DLL, common data, etc.)
18 * We define _XOPEN_SOURCE so that we can get popen and pclose.
20 #if !defined(_XOPEN_SOURCE)
21 #if __STDC_VERSION__ >= 199901L
22 /* It is invalid to compile an XPG3, XPG4, XPG4v2 or XPG5 application using c99 on Solaris */
23 #define _XOPEN_SOURCE 600
25 #define _XOPEN_SOURCE 4
29 #include "unicode/utypes.h"
30 #include "unicode/putil.h"
35 #include "unicode/uclean.h"
51 static int executeMakefile(const UPKGOptions
*o
);
52 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
);
54 /* always have this fcn, just might not do anything */
55 static void fillInMakefileFromICUConfig(UOption
*option
);
57 /* This sets the modes that are available */
60 const char *name
, *alt_name
;
65 { "files", 0, pkg_mode_files
, "Uses raw data files (no effect). Installation copies all files to the target location." },
66 #ifdef U_MAKE_IS_NMAKE
67 { "dll", "library", pkg_mode_windows
, "Generates one common data file and one shared library, <package>.dll"},
68 { "common", "archive", pkg_mode_windows
, "Generates just the common file, <package>.dat"},
69 { "static", "static", pkg_mode_windows
, "Generates one statically linked library, " LIB_PREFIX
"<package>" UDATA_LIB_SUFFIX
}
70 #else /*#ifdef U_MAKE_IS_NMAKE*/
71 #ifdef UDATA_SO_SUFFIX
72 { "dll", "library", pkg_mode_dll
, "Generates one shared library, <package>" UDATA_SO_SUFFIX
},
74 { "common", "archive", pkg_mode_common
, "Generates one common data file, <package>.dat" },
75 { "static", "static", pkg_mode_static
, "Generates one statically linked library, " LIB_PREFIX
"<package>" UDATA_LIB_SUFFIX
}
76 #endif /*#ifdef U_MAKE_IS_NMAKE*/
103 static UOption options
[]={
104 /*00*/ UOPTION_DEF( "name", 'p', UOPT_REQUIRES_ARG
),
105 /*01*/ UOPTION_DEF( "bldopt", 'O', UOPT_REQUIRES_ARG
), /* on Win32 it is release or debug */
106 /*02*/ UOPTION_DEF( "mode", 'm', UOPT_REQUIRES_ARG
),
107 /*03*/ UOPTION_HELP_H
, /* -h */
108 /*04*/ UOPTION_HELP_QUESTION_MARK
, /* -? */
109 /*05*/ UOPTION_VERBOSE
, /* -v */
110 /*06*/ UOPTION_COPYRIGHT
, /* -c */
111 /*07*/ UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG
),
112 /*08*/ UOPTION_DESTDIR
, /* -d */
113 /*09*/ UOPTION_DEF( "clean", 'k', UOPT_NO_ARG
),
114 /*10*/ UOPTION_DEF( "nooutput",'n', UOPT_NO_ARG
),
115 /*11*/ UOPTION_DEF( "rebuild", 'F', UOPT_NO_ARG
),
116 /*12*/ UOPTION_DEF( "tempdir", 'T', UOPT_REQUIRES_ARG
),
117 /*13*/ UOPTION_DEF( "install", 'I', UOPT_REQUIRES_ARG
),
118 /*14*/ UOPTION_SOURCEDIR
,
119 /*15*/ UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG
),
120 /*16*/ UOPTION_DEF( "revision", 'r', UOPT_REQUIRES_ARG
),
121 /*17*/ UOPTION_DEF( "makearg", 'M', UOPT_REQUIRES_ARG
),
122 /*18*/ UOPTION_DEF( "force-prefix", 'f', UOPT_NO_ARG
),
123 /*19*/ UOPTION_DEF( "libname", 'L', UOPT_REQUIRES_ARG
),
124 /*20*/ UOPTION_DEF( "quiet", 'q', UOPT_NO_ARG
)
127 const char options_help
[][320]={
129 #ifdef U_MAKE_IS_NMAKE
130 "The directory where the ICU is located (e.g. <ICUROOT> which contains the bin directory)",
132 "Specify options for the builder. (Autdetected if icu-config is available)",
134 "Specify the mode of building (see below; default: common)",
137 "Make the output verbose",
138 "Use the standard ICU copyright",
139 "Use a custom comment (instead of the copyright)",
140 "Specify the destination directory for files",
141 "Clean out generated & temporary files",
142 "Suppress output of data, just list files to be created",
143 "Force rebuilding of all data",
144 "Specify temporary dir (default: output dir)",
145 "Install the data (specify target)",
146 "Specify a custom source directory",
147 "Specify a custom entrypoint name (default: short name)",
148 "Specify a version when packaging in DLL or static mode",
149 "Pass the next argument to make(1)",
150 "Add package to all file names if not present",
151 "Library name to build (if different than package name)",
152 "Quite mode. (e.g. Do not output a readme file for static libraries)"
155 const char *progname
= "PKGDATA";
158 main(int argc
, char* argv
[]) {
162 UBool needsHelp
= FALSE
;
163 UErrorCode status
= U_ZERO_ERROR
;
167 U_MAIN_INIT_ARGS(argc
, argv
);
171 options
[MODE
].value
= "common";
172 options
[MAKEARG
].value
= "";
174 /* read command line options */
175 argc
=u_parseArgs(argc
, argv
, sizeof(options
)/sizeof(options
[0]), options
);
177 /* error handling, printing usage message */
178 /* I've decided to simply print an error and quit. This tool has too
179 many options to just display them all of the time. */
181 if(options
[HELP
].doesOccur
|| options
[HELP_QUESTION_MARK
].doesOccur
) {
185 if(!needsHelp
&& argc
<0) {
187 "%s: error in command line argument \"%s\"\n",
190 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
194 if(!options
[BLDOPT
].doesOccur
) {
195 /* Try to fill in from icu-config or equivalent */
196 fillInMakefileFromICUConfig(&options
[1]);
198 #ifdef U_MAKE_IS_NMAKE
200 fprintf(stderr
, "Warning: You are using the deprecated -O option\n"
201 "\tYou can fix this warning by installing pkgdata, gencmn and genccode\n"
202 "\tinto the same directory and not specifying the -O option to pkgdata.\n");
206 if(!options
[BLDOPT
].doesOccur
) {
207 fprintf(stderr
, " required parameter is missing: -O is required \n");
208 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
212 if(!options
[NAME
].doesOccur
) /* -O we already have - don't report it. */
214 fprintf(stderr
, " required parameter -p is missing \n");
215 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
221 "No input files specified.\n"
222 "Run '%s --help' for help.\n", progname
);
225 } /* end !needsHelp */
227 if(argc
<0 || needsHelp
) {
229 "usage: %s [-options] [-] [packageFile] \n"
230 "\tProduce packaged ICU data from the given list(s) of files.\n"
231 "\t'-' by itself means to read from stdin.\n"
232 "\tpackageFile is a text file containing the list of files to package.\n",
235 fprintf(stderr
, "\n options:\n");
236 for(i
=0;i
<(sizeof(options
)/sizeof(options
[0]));i
++) {
237 fprintf(stderr
, "%-5s -%c %s%-10s %s\n",
239 options
[i
].shortName
,
240 options
[i
].longName
? "or --" : " ",
241 options
[i
].longName
? options
[i
].longName
: "",
245 fprintf(stderr
, "modes: (-m option)\n");
246 for(i
=0;i
<(sizeof(modes
)/sizeof(modes
[0]));i
++) {
247 fprintf(stderr
, " %-9s ", modes
[i
].name
);
248 if (modes
[i
].alt_name
) {
249 fprintf(stderr
, "/ %-9s", modes
[i
].alt_name
);
251 fprintf(stderr
, " ");
253 fprintf(stderr
, " %s\n", modes
[i
].desc
);
258 /* OK, fill in the options struct */
259 uprv_memset(&o
, 0, sizeof(o
));
261 o
.mode
= options
[MODE
].value
;
262 o
.version
= options
[REVISION
].doesOccur
? options
[REVISION
].value
: 0;
263 o
.makeArgs
= options
[MAKEARG
].value
;
267 for(i
=0;i
<sizeof(modes
)/sizeof(modes
[0]);i
++) {
268 if(!uprv_strcmp(modes
[i
].name
, o
.mode
)) {
269 o
.fcn
= modes
[i
].fcn
;
271 } else if (modes
[i
].alt_name
&& !uprv_strcmp(modes
[i
].alt_name
, o
.mode
)) {
272 o
.mode
= modes
[i
].name
;
273 o
.fcn
= modes
[i
].fcn
;
279 fprintf(stderr
, "Error: invalid mode '%s' specified. Run '%s --help' to list valid modes.\n", o
.mode
, progname
);
283 o
.shortName
= options
[0].value
;
285 int32_t len
= (int32_t)uprv_strlen(o
.shortName
);
289 cp
= csname
= (char *) uprv_malloc((len
+ 1 + 1) * sizeof(*o
.cShortName
));
290 if (*(sp
= o
.shortName
)) {
291 *cp
++ = isalpha(*sp
) ? * sp
: '_';
292 for (++sp
; *sp
; ++sp
) {
293 *cp
++ = isalnum(*sp
) ? *sp
: '_';
298 o
.cShortName
= csname
;
301 if(options
[LIBNAME
].doesOccur
) { /* get libname from shortname, or explicit -L parameter */
302 o
.libName
= options
[LIBNAME
].value
;
304 o
.libName
= o
.shortName
;
307 if(options
[QUIET
].doesOccur
) {
313 o
.verbose
= options
[VERBOSE
].doesOccur
;
314 #ifdef U_MAKE_IS_NMAKE /* format is R:pathtoICU or D:pathtoICU */
316 char *pathstuff
= (char *)options
[BLDOPT
].value
;
317 if(options
[1].value
[uprv_strlen(options
[BLDOPT
].value
)-1] == '\\') {
318 pathstuff
[uprv_strlen(options
[BLDOPT
].value
)-1] = '\0';
320 if(*pathstuff
== PKGDATA_DERIVED_PATH
|| *pathstuff
== 'R' || *pathstuff
== 'D') {
321 o
.options
= pathstuff
;
323 if(*pathstuff
== ':') {
328 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n");
332 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n");
335 o
.icuroot
= pathstuff
;
337 fprintf(stdout
, "# ICUROOT is %s\n", o
.icuroot
);
340 #else /* on UNIX, we'll just include the file... */
341 o
.options
= options
[BLDOPT
].value
;
343 if(options
[COPYRIGHT
].doesOccur
) {
344 o
.comment
= U_COPYRIGHT_STRING
;
345 } else if (options
[COMMENT
].doesOccur
) {
346 o
.comment
= options
[COMMENT
].value
;
349 if( options
[DESTDIR
].doesOccur
) {
350 o
.targetDir
= options
[DESTDIR
].value
;
352 o
.targetDir
= "."; /* cwd */
355 o
.clean
= options
[CLEAN
].doesOccur
;
356 o
.nooutput
= options
[NOOUTPUT
].doesOccur
;
357 o
.rebuild
= options
[REBUILD
].doesOccur
;
359 if( options
[TEMPDIR
].doesOccur
) {
360 o
.tmpDir
= options
[TEMPDIR
].value
;
362 o
.tmpDir
= o
.targetDir
;
365 if( options
[INSTALL
].doesOccur
) {
366 o
.install
= options
[INSTALL
].value
;
369 if( options
[SOURCEDIR
].doesOccur
) {
370 o
.srcDir
= options
[SOURCEDIR
].value
;
375 if( options
[ENTRYPOINT
].doesOccur
) {
376 o
.entryName
= options
[ENTRYPOINT
].value
;
378 o
.entryName
= o
.cShortName
;
381 /* OK options are set up. Now the file lists. */
383 for( i
=1; i
<argc
; i
++) {
384 if ( !uprv_strcmp(argv
[i
] , "-") ) {
386 if( o
.hadStdin
== TRUE
) {
387 fprintf(stderr
, "Error: can't specify '-' twice!\n"
388 "Run '%s --help' for help.\n", progname
);
394 o
.fileListFiles
= pkg_appendToList(o
.fileListFiles
, &tail
, uprv_strdup(argv
[i
]));
398 loadLists(&o
, &status
);
399 if( U_FAILURE(status
) ) {
400 fprintf(stderr
, "error loading input file lists: %s\n", u_errorName(status
));
404 /* Makefile pathname */
405 uprv_strcpy(tmp
, o
.tmpDir
);
406 #ifdef U_MAKE_IS_NMAKE
407 uprv_strcat(tmp
, U_FILE_SEP_STRING
);
409 uprv_strcat(tmp
, U_FILE_ALT_SEP_STRING
);
411 uprv_strcat(tmp
, o
.shortName
);
412 uprv_strcat(tmp
, "_");
413 uprv_strcat(tmp
, o
.mode
);
414 uprv_strcat(tmp
, ".mak"); /* MAY NEED TO CHANGE PER PLATFORM */
416 o
.makeFile
= uprv_strdup(tmp
);
418 out
= T_FileStream_open(o
.makeFile
, "w");
420 pkg_mak_writeHeader(out
, &o
); /* need to take status */
421 o
.fcn(&o
, out
, &status
);
422 pkg_mak_writeFooter(out
, &o
);
423 T_FileStream_close(out
);
425 fprintf(stderr
, "warning: couldn't create %s, will use existing file if any\n", o
.makeFile
);
426 /*status = U_FILE_ACCESS_ERROR;*/
429 if(U_FAILURE(status
)) {
430 fprintf(stderr
, "Error creating makefile [%s]: %s\n", o
.mode
,
431 u_errorName(status
));
435 if(o
.nooutput
== TRUE
) {
436 return 0; /* nothing to do. */
439 return executeMakefile(&o
);
442 /* POSIX - execute makefile */
443 static int executeMakefile(const UPKGOptions
*o
)
450 make
= getenv("MAKE");
452 if(!make
|| !make
[0]) {
456 /*getcwd(pwd, 1024);*/
458 sprintf(cmd
, "%s %s%s -f \"%s\" %s %s %s %s",
460 o
->install
? "INSTALLTO=" : "",
461 o
->install
? o
->install
: "",
463 o
->clean
? "clean" : "",
464 o
->rebuild
? "rebuild" : "",
465 o
->install
? "install" : "",
468 sprintf(cmd
, "CALL GNU/GMAKE PARM(%s%s%s '-f' '%s' %s %s %s %s%s%s)",
469 o
->install
? "'INSTALLTO=" : "",
470 o
->install
? o
->install
: "",
471 o
->install
? "'" : "",
473 o
->clean
? "'clean'" : "",
474 o
->rebuild
? "'rebuild'" : "",
475 o
->install
? "'install'" : "",
476 o
->makeArgs
&& *o
->makeArgs
? "'" : "",
477 o
->makeArgs
&& *o
->makeArgs
? o
->makeArgs
: "",
478 o
->makeArgs
&& *o
->makeArgs
? "'" : "");
480 sprintf(cmd
, "%s %s%s -f %s %s %s %s %s",
482 o
->install
? "INSTALLTO=" : "",
483 o
->install
? o
->install
: "",
485 o
->clean
? "clean" : "",
486 o
->rebuild
? "rebuild" : "",
487 o
->install
? "install" : "",
497 fprintf(stderr
, "# Failed, rc=%d\n", rc
);
500 return rc
< 128 ? rc
: (rc
>> 8);
504 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
)
506 CharList
*l
, *tail
= NULL
, *tail2
= NULL
;
509 char *linePtr
, *lineNext
;
510 const uint32_t lineMax
= 16300;
513 int32_t ln
=0; /* line number */
515 for(l
= o
->fileListFiles
; l
; l
= l
->next
) {
517 fprintf(stdout
, "# Reading %s..\n", l
->str
);
520 in
= T_FileStream_open(l
->str
, "r"); /* open files list */
523 fprintf(stderr
, "Error opening <%s>.\n", l
->str
);
524 *status
= U_FILE_ACCESS_ERROR
;
528 while(T_FileStream_readLine(in
, line
, sizeof(line
))!=NULL
) { /* for each line */
530 if(uprv_strlen(line
)>lineMax
) {
531 fprintf(stderr
, "%s:%d - line too long (over %d chars)\n", l
->str
, (int)ln
, (int)lineMax
);
534 /* remove spaces at the beginning */
536 while(isspace(*linePtr
)) {
540 /* remove trailing newline characters */
542 if(*s
=='\r' || *s
=='\n') {
548 if((*linePtr
== 0) || (*linePtr
== '#')) {
549 continue; /* comment or empty line */
552 /* Now, process the line */
555 while(linePtr
&& *linePtr
) { /* process space-separated items */
556 while(*linePtr
== ' ') {
559 /* Find the next quote */
560 if(linePtr
[0] == '"')
562 lineNext
= uprv_strchr(linePtr
+1, '"');
563 if(lineNext
== NULL
) {
564 fprintf(stderr
, "%s:%d - missing trailing double quote (\")\n",
570 if(*lineNext
!= ' ') {
571 fprintf(stderr
, "%s:%d - malformed quoted line at position %d, expected ' ' got '%c'\n",
572 l
->str
, (int)ln
, (int)(lineNext
-line
), (*lineNext
)?*lineNext
:'0');
580 lineNext
= uprv_strchr(linePtr
, ' ');
582 *lineNext
= 0; /* terminate at space */
588 s
= (char*)getLongPathname(linePtr
);
590 /* normal mode.. o->files is just the bare list without package names */
591 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(linePtr
));
592 if(uprv_pathIsAbsolute(s
)) {
593 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
);
594 exit(U_ILLEGAL_ARGUMENT_ERROR
);
596 uprv_strcpy(tmp
, o
->srcDir
);
597 uprv_strcat(tmp
, o
->srcDir
[uprv_strlen(o
->srcDir
)-1]==U_FILE_SEP_CHAR
?"":U_FILE_SEP_STRING
);
599 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(tmp
));
601 } /* for each entry on line */
602 } /* for each line */
603 T_FileStream_close(in
);
604 } /* for each file list file */
607 /* Try calling icu-config directly to get information */
608 static void fillInMakefileFromICUConfig(UOption
*option
)
613 static char buf
[512] = "";
614 static const char cmd
[] = "icu-config --incfile";
616 if(options
[5].doesOccur
)
619 fprintf(stderr
, "%s: No -O option found, trying '%s'.\n", progname
, cmd
);
626 fprintf(stderr
, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname
);
630 n
= fread(buf
, 1, 511, p
);
636 fprintf(stderr
,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname
);
640 if(buf
[strlen(buf
)-1]=='\n')
642 buf
[strlen(buf
)-1]=0;
647 fprintf(stderr
, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname
);
651 if(options
[5].doesOccur
)
654 fprintf(stderr
, "%s: icu-config: using '-O %s'\n", progname
, buf
);
657 option
->doesOccur
= TRUE
;
658 #else /* ! U_HAVE_POPEN */
661 char pathbuffer
[_MAX_PATH
] = {0};
662 char *fullEXEpath
= NULL
;
663 char *pathstuff
= NULL
;
665 if (strchr(progname
, U_FILE_SEP_CHAR
) != NULL
|| strchr(progname
, U_FILE_ALT_SEP_CHAR
) != NULL
) {
666 /* pkgdata was executed with relative path */
667 fullEXEpath
= _fullpath(pathbuffer
, progname
, sizeof(pathbuffer
));
668 pathstuff
= (char *)options
[1].value
;
671 pathstuff
= strrchr(fullEXEpath
, U_FILE_SEP_CHAR
);
674 uprv_memmove(fullEXEpath
+ 2, fullEXEpath
, uprv_strlen(fullEXEpath
)+1);
675 fullEXEpath
[0] = PKGDATA_DERIVED_PATH
;
676 fullEXEpath
[1] = ':';
677 option
->value
= uprv_strdup(fullEXEpath
);
678 option
->doesOccur
= TRUE
;
683 /* pkgdata was executed from the path */
684 /* Search for file in PATH environment variable: */
685 _searchenv("pkgdata.exe", "PATH", pathbuffer
);
686 if( *pathbuffer
!= '\0' ) {
687 fullEXEpath
= pathbuffer
;
688 pathstuff
= strrchr(pathbuffer
, U_FILE_SEP_CHAR
);
691 uprv_memmove(fullEXEpath
+ 2, fullEXEpath
, uprv_strlen(fullEXEpath
)+1);
692 fullEXEpath
[0] = PKGDATA_DERIVED_PATH
;
693 fullEXEpath
[1] = ':';
694 option
->value
= uprv_strdup(fullEXEpath
);
695 option
->doesOccur
= TRUE
;
699 /* else can't determine the path */
702 /* no popen available */
703 /* Put other OS specific ways to search for the Makefile.inc type
704 information or else fail.. */