1 /******************************************************************************
3 * Copyright (C) 2000-2004, 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"
31 We define __USE_POSIX2 so that we can get popen and pclose when
32 --enable-strict is used
35 # define __USE_POSIX2 1
47 static int executeMakefile(const UPKGOptions
*o
);
48 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
);
50 /* always have this fcn, just might not do anything */
51 static void fillInMakefileFromICUConfig(UOption
*option
);
53 /* This sets the modes that are available */
56 const char *name
, *alt_name
;
61 { "files", 0, pkg_mode_files
, "Uses raw data files (no effect). Installation copies all files to the target location." },
62 #ifdef U_MAKE_IS_NMAKE
63 { "dll", "library", pkg_mode_windows
, "Generates one common data file and one shared library, <package>.dll"},
64 { "common", "archive", pkg_mode_windows
, "Generates just the common file, <package>.dat"},
65 { "static", "static", pkg_mode_windows
, "Generates one statically linked library, " LIB_PREFIX
"<package>" UDATA_LIB_SUFFIX
}
66 #else /*#ifdef U_MAKE_IS_NMAKE*/
67 #ifdef UDATA_SO_SUFFIX
68 { "dll", "library", pkg_mode_dll
, "Generates one shared library, <package>" UDATA_SO_SUFFIX
},
70 { "common", "archive", pkg_mode_common
, "Generates one common data file, <package>.dat" },
71 { "static", "static", pkg_mode_static
, "Generates one statically linked library, " LIB_PREFIX
"<package>" UDATA_LIB_SUFFIX
}
72 #endif /*#ifdef U_MAKE_IS_NMAKE*/
75 static UOption options
[]={
76 /*00*/ UOPTION_DEF( "name", 'p', UOPT_REQUIRES_ARG
),
77 /*01*/ UOPTION_DEF( "bldopt", 'O', UOPT_REQUIRES_ARG
), /* on Win32 it is release or debug */
78 /*02*/ UOPTION_DEF( "mode", 'm', UOPT_REQUIRES_ARG
),
79 /*03*/ UOPTION_HELP_H
, /* -h */
80 /*04*/ UOPTION_HELP_QUESTION_MARK
, /* -? */
81 /*05*/ UOPTION_VERBOSE
, /* -v */
82 /*06*/ UOPTION_COPYRIGHT
, /* -c */
83 /*07*/ UOPTION_DEF( "comment", 'C', UOPT_REQUIRES_ARG
),
84 /*08*/ UOPTION_DESTDIR
, /* -d */
85 /*09*/ UOPTION_DEF( "clean", 'k', UOPT_NO_ARG
),
86 /*10*/ UOPTION_DEF( "nooutput",'n', UOPT_NO_ARG
),
87 /*11*/ UOPTION_DEF( "rebuild", 'F', UOPT_NO_ARG
),
88 /*12*/ UOPTION_DEF( "tempdir", 'T', UOPT_REQUIRES_ARG
),
89 /*13*/ UOPTION_DEF( "install", 'I', UOPT_REQUIRES_ARG
),
90 /*14*/ UOPTION_SOURCEDIR
,
91 /*15*/ UOPTION_DEF( "entrypoint", 'e', UOPT_REQUIRES_ARG
),
92 /*16*/ UOPTION_DEF( "revision", 'r', UOPT_REQUIRES_ARG
),
93 /*17*/ UOPTION_DEF( 0, 'M', UOPT_REQUIRES_ARG
),
94 /*18*/ UOPTION_DEF( "force-prefix", 'f', UOPT_NO_ARG
),
95 /*19*/ UOPTION_DEF( "numerictmp", 'N', UOPT_NO_ARG
),
96 /*20*/ UOPTION_DEF( "embed", 'E', UOPT_NO_ARG
),
97 /*21*/ UOPTION_DEF( "libname", 'L', UOPT_REQUIRES_ARG
),
98 /*22*/ UOPTION_DEF( "quiet", 'q', UOPT_NO_ARG
)
101 const char options_help
[][320]={
103 #ifdef U_MAKE_IS_NMAKE
104 "The directory where the ICU is located (e.g. <ICUROOT> which contains the bin directory)",
106 "Specify options for the builder. (Autdetected if icu-config is available)",
108 "Specify the mode of building (see below; default: common)",
111 "Make the output verbose",
112 "Use the standard ICU copyright",
113 "Use a custom comment (instead of the copyright)",
114 "Specify the destination directory for files",
115 "Clean out generated & temporary files",
116 "Suppress output of data, just list files to be created",
117 "Force rebuilding of all data",
118 "Specify temporary dir (default: output dir)",
119 "Install the data (specify target)",
120 "Specify a custom source directory",
121 "Specify a custom entrypoint name (default: short name)",
122 "Specify a version when packaging in DLL or static mode",
123 "Pass the next argument to make(1)",
124 "Add package to all file names if not present",
125 "Use short numeric temporary file names such as t1234.c",
126 "Use Embedded paths (such as 'mypackage_') - for compatibility.",
127 "Library name to build (if different than package name)",
128 "Quite mode. (e.g. Do not output a readme file for static libraries)"
131 const char *progname
= "PKGDATA";
134 main(int argc
, char* argv
[]) {
138 UBool needsHelp
= FALSE
;
139 UErrorCode status
= U_ZERO_ERROR
;
143 U_MAIN_INIT_ARGS(argc
, argv
);
147 options
[2].value
= "common";
148 options
[17].value
= "";
150 /* read command line options */
151 argc
=u_parseArgs(argc
, argv
, sizeof(options
)/sizeof(options
[0]), options
);
153 /* error handling, printing usage message */
154 /* I've decided to simply print an error and quit. This tool has too
155 many options to just display them all of the time. */
157 if(options
[3].doesOccur
|| options
[4].doesOccur
) {
161 if(!needsHelp
&& argc
<0) {
163 "%s: error in command line argument \"%s\"\n",
166 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
170 if(!options
[1].doesOccur
) {
171 /* Try to fill in from icu-config or equivalent */
172 fillInMakefileFromICUConfig(&options
[1]);
174 #ifdef U_MAKE_IS_NMAKE
176 fprintf(stderr
, "Warning: You are using the deprecated -O option\n"
177 "\tYou can fix this warning by installing pkgdata, gencmn and genccode\n"
178 "\tinto the same directory and not specifying the -O option to pkgdata.\n");
182 if(!options
[1].doesOccur
) {
183 fprintf(stderr
, " required parameter is missing: -O is required \n");
184 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
188 if(!options
[0].doesOccur
) /* -O we already have - don't report it. */
190 fprintf(stderr
, " required parameter -p is missing \n");
191 fprintf(stderr
, "Run '%s --help' for help.\n", progname
);
197 "No input files specified.\n"
198 "Run '%s --help' for help.\n", progname
);
201 } /* end !needsHelp */
203 if(argc
<0 || needsHelp
) {
205 "usage: %s [-options] [-] [packageFile] \n"
206 "\tProduce packaged ICU data from the given list(s) of files.\n"
207 "\t'-' by itself means to read from stdin.\n"
208 "\tpackageFile is a text file containing the list of files to package.\n",
211 fprintf(stderr
, "\n options:\n");
212 for(i
=0;i
<(sizeof(options
)/sizeof(options
[0]));i
++) {
213 fprintf(stderr
, "%-5s -%c %s%-10s %s\n",
215 options
[i
].shortName
,
216 options
[i
].longName
? "or --" : " ",
217 options
[i
].longName
? options
[i
].longName
: "",
221 fprintf(stderr
, "modes: (-m option)\n");
222 for(i
=0;i
<(sizeof(modes
)/sizeof(modes
[0]));i
++) {
223 fprintf(stderr
, " %-9s ", modes
[i
].name
);
224 if (modes
[i
].alt_name
) {
225 fprintf(stderr
, "/ %-9s", modes
[i
].alt_name
);
227 fprintf(stderr
, " ");
229 fprintf(stderr
, " %s\n", modes
[i
].desc
);
234 /* OK, fill in the options struct */
235 uprv_memset(&o
, 0, sizeof(o
));
237 o
.mode
= options
[2].value
;
238 o
.version
= options
[16].doesOccur
? options
[16].value
: 0;
239 o
.makeArgs
= options
[17].value
;
243 for(i
=0;i
<sizeof(modes
)/sizeof(modes
[0]);i
++) {
244 if(!uprv_strcmp(modes
[i
].name
, o
.mode
)) {
245 o
.fcn
= modes
[i
].fcn
;
247 } else if (modes
[i
].alt_name
&& !uprv_strcmp(modes
[i
].alt_name
, o
.mode
)) {
248 o
.mode
= modes
[i
].name
;
249 o
.fcn
= modes
[i
].fcn
;
255 fprintf(stderr
, "Error: invalid mode '%s' specified. Run '%s --help' to list valid modes.\n", o
.mode
, progname
);
259 o
.shortName
= options
[0].value
;
261 int32_t len
= (int32_t)uprv_strlen(o
.shortName
);
265 cp
= csname
= (char *) uprv_malloc((len
+ 1 + 1) * sizeof(*o
.cShortName
));
266 if (*(sp
= o
.shortName
)) {
267 *cp
++ = isalpha(*sp
) ? * sp
: '_';
268 for (++sp
; *sp
; ++sp
) {
269 *cp
++ = isalnum(*sp
) ? *sp
: '_';
274 o
.cShortName
= csname
;
277 if(options
[21].doesOccur
) { /* get libname from shortname, or explicit -L parameter */
278 o
.libName
= options
[21].value
;
280 o
.libName
= o
.shortName
;
283 if(options
[22].doesOccur
) {
289 o
.verbose
= options
[5].doesOccur
;
290 #ifdef U_MAKE_IS_NMAKE /* format is R:pathtoICU or D:pathtoICU */
292 char *pathstuff
= (char *)options
[1].value
;
293 if(options
[1].value
[uprv_strlen(options
[1].value
)-1] == '\\') {
294 pathstuff
[uprv_strlen(options
[1].value
)-1] = '\0';
296 if(*pathstuff
== PKGDATA_DERIVED_PATH
|| *pathstuff
== 'R' || *pathstuff
== 'D') {
297 o
.options
= pathstuff
;
299 if(*pathstuff
== ':') {
304 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n");
308 fprintf(stderr
, "Error: invalid windows build mode, should be R (release) or D (debug).\n");
311 o
.icuroot
= pathstuff
;
313 fprintf(stdout
, "# ICUROOT is %s\n", o
.icuroot
);
316 #else /* on UNIX, we'll just include the file... */
317 o
.options
= options
[1].value
;
319 if(options
[6].doesOccur
) {
320 o
.comment
= U_COPYRIGHT_STRING
;
321 } else if (options
[7].doesOccur
) {
322 o
.comment
= options
[7].value
;
325 if( options
[8].doesOccur
) {
326 o
.targetDir
= options
[8].value
;
328 o
.targetDir
= "."; /* cwd */
331 o
.clean
= options
[9].doesOccur
;
332 o
.nooutput
= options
[10].doesOccur
;
333 o
.rebuild
= options
[11].doesOccur
;
334 o
.numeric
= options
[19].doesOccur
;
336 o
.rebuild
= TRUE
; /* force rebuild if numeric */
339 o
.embed
= options
[20].doesOccur
;
341 if( options
[12].doesOccur
) {
342 o
.tmpDir
= options
[12].value
;
344 o
.tmpDir
= o
.targetDir
;
347 if( options
[13].doesOccur
) {
348 o
.install
= options
[13].value
;
351 if( options
[14].doesOccur
) {
352 o
.srcDir
= options
[14].value
;
357 if( options
[15].doesOccur
) {
358 o
.entryName
= options
[15].value
;
360 o
.entryName
= o
.cShortName
;
363 /* OK options are set up. Now the file lists. */
365 for( i
=1; i
<argc
; i
++) {
366 if ( !uprv_strcmp(argv
[i
] , "-") ) {
368 if( o
.hadStdin
== TRUE
) {
369 fprintf(stderr
, "Error: can't specify '-' twice!\n"
370 "Run '%s --help' for help.\n", progname
);
376 o
.fileListFiles
= pkg_appendToList(o
.fileListFiles
, &tail
, uprv_strdup(argv
[i
]));
380 loadLists(&o
, &status
);
381 if( U_FAILURE(status
) ) {
382 fprintf(stderr
, "error loading input file lists: %s\n", u_errorName(status
));
386 /* Makefile pathname */
387 uprv_strcpy(tmp
, o
.tmpDir
);
388 uprv_strcat(tmp
, U_FILE_SEP_STRING
);
389 uprv_strcat(tmp
, o
.shortName
);
390 uprv_strcat(tmp
, "_");
391 uprv_strcat(tmp
, o
.mode
);
392 uprv_strcat(tmp
, ".mak"); /* MAY NEED TO CHANGE PER PLATFORM */
394 o
.makeFile
= uprv_strdup(tmp
);
396 out
= T_FileStream_open(o
.makeFile
, "w");
398 pkg_mak_writeHeader(out
, &o
); /* need to take status */
399 o
.fcn(&o
, out
, &status
);
400 pkg_mak_writeFooter(out
, &o
);
401 T_FileStream_close(out
);
403 fprintf(stderr
, "warning: couldn't create %s, will use existing file if any\n", o
.makeFile
);
404 /*status = U_FILE_ACCESS_ERROR;*/
407 if(U_FAILURE(status
)) {
408 fprintf(stderr
, "Error creating makefile [%s]: %s\n", o
.mode
,
409 u_errorName(status
));
413 if(o
.nooutput
== TRUE
) {
414 return 0; /* nothing to do. */
417 return executeMakefile(&o
);
420 /* POSIX - execute makefile */
421 static int executeMakefile(const UPKGOptions
*o
)
428 make
= getenv("MAKE");
430 if(!make
|| !make
[0]) {
434 /*getcwd(pwd, 1024);*/
436 sprintf(cmd
, "%s %s%s -f \"%s\" %s %s %s %s",
438 o
->install
? "INSTALLTO=" : "",
439 o
->install
? o
->install
: "",
441 o
->clean
? "clean" : "",
442 o
->rebuild
? "rebuild" : "",
443 o
->install
? "install" : "",
446 sprintf(cmd
, "CALL GNU/GMAKE PARM(%s%s%s '-f' '%s' %s %s %s %s)",
447 o
->install
? "'INSTALLTO=" : "",
448 o
->install
? o
->install
: "",
449 o
->install
? "'" : "",
451 o
->clean
? "'clean'" : "",
452 o
->rebuild
? "'rebuild'" : "",
453 o
->install
? "'install'" : "",
456 sprintf(cmd
, "%s %s%s -f %s %s %s %s %s",
458 o
->install
? "INSTALLTO=" : "",
459 o
->install
? o
->install
: "",
461 o
->clean
? "clean" : "",
462 o
->rebuild
? "rebuild" : "",
463 o
->install
? "install" : "",
473 fprintf(stderr
, "# Failed, rc=%d\n", rc
);
476 return rc
< 128 ? rc
: (rc
>> 8);
480 static void loadLists(UPKGOptions
*o
, UErrorCode
*status
)
482 CharList
*l
, *tail
= NULL
, *tail2
= NULL
;
485 char *linePtr
, *lineNext
;
486 const uint32_t lineMax
= 16300;
488 char pkgPrefix
[1024];
489 int32_t pkgPrefixLen
;
490 const char *baseName
;
492 int32_t ln
=0; /* line number */
496 fixPrefix
= options
[18].doesOccur
;
498 strcpy(pkgPrefix
, o
->shortName
);
499 strcat(pkgPrefix
, "_");
500 pkgPrefixLen
=(int32_t)uprv_strlen(pkgPrefix
);
501 for(l
= o
->fileListFiles
; l
; l
= l
->next
) {
503 fprintf(stdout
, "# Reading %s..\n", l
->str
);
506 in
= T_FileStream_open(l
->str
, "r"); /* open files list */
509 fprintf(stderr
, "Error opening <%s>.\n", l
->str
);
510 *status
= U_FILE_ACCESS_ERROR
;
514 while(T_FileStream_readLine(in
, line
, sizeof(line
))!=NULL
) { /* for each line */
515 if((ln
== 0) && (!o
->embed
)) {
516 /* determine if we need to run in 'embed' (compatibility) mode */
517 if(!strncmp(findBasename(line
), pkgPrefix
, pkgPrefixLen
)) {
518 fprintf(stderr
, "Warning: Found path '%s' in file name. Assuming compatibility (-E) mode.\n", pkgPrefix
);
523 if(uprv_strlen(line
)>lineMax
) {
524 fprintf(stderr
, "%s:%d - line too long (over %d chars)\n", l
->str
, (int)ln
, (int)lineMax
);
527 /* remove spaces at the beginning */
529 while(isspace(*linePtr
)) {
533 /* remove trailing newline characters */
535 if(*s
=='\r' || *s
=='\n') {
541 if((*linePtr
== 0) || (*linePtr
== '#')) {
542 continue; /* comment or empty line */
545 /* Now, process the line */
548 while(linePtr
&& *linePtr
) { /* process space-separated items */
549 while(*linePtr
== ' ') {
552 /* Find the next quote */
553 if(linePtr
[0] == '"')
555 lineNext
= uprv_strchr(linePtr
+1, '"');
556 if(lineNext
== NULL
) {
557 fprintf(stderr
, "%s:%d - missing trailing double quote (\")\n",
563 if(*lineNext
!= ' ') {
564 fprintf(stderr
, "%s:%d - malformed quoted line at position %d, expected ' ' got '%c'\n",
565 l
->str
, (int)ln
, lineNext
-line
, (*lineNext
)?*lineNext
:'0');
573 lineNext
= uprv_strchr(linePtr
, ' ');
575 *lineNext
= 0; /* terminate at space */
581 s
= (char*)getLongPathname(linePtr
);
584 /* normal mode.. o->files is just the bare list without package names */
585 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(linePtr
));
586 uprv_strcpy(tmp
, o
->srcDir
);
587 uprv_strcat(tmp
, o
->srcDir
[uprv_strlen(o
->srcDir
)-1]==U_FILE_SEP_CHAR
?"":U_FILE_SEP_STRING
);
589 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(tmp
));
590 } else {/* embedded package_ mode */
591 baseName
= findBasename(s
);
594 /* s was something 'long' with a path */
595 /* paths already have the prefix */
596 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(baseName
));
597 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(s
));
598 } else { /* s was just a basename, we want to prepend source dir*/
599 /* check for prefix of package */
600 uprv_strcpy(tmp
, o
->srcDir
);
601 uprv_strcat(tmp
, o
->srcDir
[uprv_strlen(o
->srcDir
)-1]==U_FILE_SEP_CHAR
?"":U_FILE_SEP_STRING
);
602 o
->files
= pkg_appendToList(o
->files
, &tail
, uprv_strdup(baseName
));
604 o
->filePaths
= pkg_appendToList(o
->filePaths
, &tail2
, uprv_strdup(tmp
));
606 } /* end compatibility mode */
608 } /* for each entry on line */
609 } /* for each line */
610 T_FileStream_close(in
);
611 } /* for each file list file */
614 /* Try calling icu-config directly to get information */
615 static void fillInMakefileFromICUConfig(UOption
*option
)
620 static char buf
[512] = "";
621 static const char cmd
[] = "icu-config --incfile";
623 if(options
[5].doesOccur
)
626 fprintf(stderr
, "%s: No -O option found, trying '%s'.\n", progname
, cmd
);
633 fprintf(stderr
, "%s: icu-config: No icu-config found. (fix PATH or use -O option)\n", progname
);
637 n
= fread(buf
, 1, 511, p
);
643 fprintf(stderr
,"%s: icu-config: Could not read from icu-config. (fix PATH or use -O option)\n", progname
);
647 if(buf
[strlen(buf
)-1]=='\n')
649 buf
[strlen(buf
)-1]=0;
654 fprintf(stderr
, "%s: icu-config: invalid response from icu-config (fix PATH or use -O option)\n", progname
);
658 if(options
[5].doesOccur
)
661 fprintf(stderr
, "%s: icu-config: using '-O %s'\n", progname
, buf
);
664 option
->doesOccur
= TRUE
;
665 #else /* ! U_HAVE_POPEN */
668 char pathbuffer
[_MAX_PATH
] = {0};
669 char *fullEXEpath
= NULL
;
670 char *pathstuff
= NULL
;
672 if (strchr(progname
, U_FILE_SEP_CHAR
) != NULL
|| strchr(progname
, U_FILE_ALT_SEP_CHAR
) != NULL
) {
673 /* pkgdata was executed with relative path */
674 fullEXEpath
= _fullpath(pathbuffer
, progname
, sizeof(pathbuffer
));
675 pathstuff
= (char *)options
[1].value
;
678 pathstuff
= strrchr(fullEXEpath
, U_FILE_SEP_CHAR
);
681 uprv_memmove(fullEXEpath
+ 2, fullEXEpath
, uprv_strlen(fullEXEpath
)+1);
682 fullEXEpath
[0] = PKGDATA_DERIVED_PATH
;
683 fullEXEpath
[1] = ':';
684 option
->value
= uprv_strdup(fullEXEpath
);
685 option
->doesOccur
= TRUE
;
690 /* pkgdata was executed from the path */
691 /* Search for file in PATH environment variable: */
692 _searchenv("pkgdata.exe", "PATH", pathbuffer
);
693 if( *pathbuffer
!= '\0' ) {
694 fullEXEpath
= pathbuffer
;
695 pathstuff
= strrchr(pathbuffer
, U_FILE_SEP_CHAR
);
698 uprv_memmove(fullEXEpath
+ 2, fullEXEpath
, uprv_strlen(fullEXEpath
)+1);
699 fullEXEpath
[0] = PKGDATA_DERIVED_PATH
;
700 fullEXEpath
[1] = ':';
701 option
->value
= uprv_strdup(fullEXEpath
);
702 option
->doesOccur
= TRUE
;
706 /* else can't determine the path */
709 /* no popen available */
710 /* Put other OS specific ways to search for the Makefile.inc type
711 information or else fail.. */