diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 9758ed63297..ab52cec48bc 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -139,6 +139,8 @@ extern void fatal_error( const char *msg, ... ); extern void fatal_perror( const char *msg, ... ); extern void warning( const char *msg, ... ); extern void output_standard_file_header( FILE *outfile ); +extern FILE *open_input_file( const char *srcdir, const char *name ); +extern void close_input_file( FILE *file ); extern void dump_bytes( FILE *outfile, const unsigned char *data, int len, const char *label, int constant ); extern const char *make_c_identifier( const char *str ); @@ -146,6 +148,7 @@ extern int get_alignment(int alignBoundary); extern void add_import_dll( const char *name, int delay ); extern void add_ignore_symbol( const char *name ); +extern void read_undef_symbols( char **argv ); extern int resolve_imports( void ); extern int output_imports( FILE *outfile ); extern void load_res32_file( const char *name ); @@ -156,13 +159,13 @@ extern int output_res16_directory( unsigned char *buffer ); extern void output_dll_init( FILE *outfile, const char *constructor, const char *destructor ); extern void parse_debug_channels( const char *srcdir, const char *filename ); -extern void BuildGlue( FILE *outfile, FILE *infile ); +extern void BuildGlue( FILE *outfile, const char *srcdir, char **argv ); extern void BuildRelays16( FILE *outfile ); extern void BuildRelays32( FILE *outfile ); extern void BuildSpec16File( FILE *outfile ); extern void BuildSpec32File( FILE *outfile ); extern void BuildDef32File( FILE *outfile ); -extern void BuildDebugFile( FILE *outfile ); +extern void BuildDebugFile( FILE *outfile, const char *srcdir, char **argv ); extern SPEC_TYPE ParseTopLevel( FILE *file, int def_only ); /* global variables */ @@ -184,7 +187,7 @@ extern char DLLName[80]; extern char DLLFileName[80]; extern char owner_name[80]; extern char *init_func; -extern const char *input_file_name; +extern char *input_file_name; extern const char *output_file_name; extern char **debug_channels; extern char **lib_path; diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index 61246a12c3f..054e885935d 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "build.h" @@ -55,6 +56,8 @@ static char **ignore_symbols; /* list of symbols to ignore */ static int nb_ignore_symbols; static int ignore_size; +static const char *ld_tmp_file; /* ld temp file name */ + static struct import **dll_imports = NULL; static int nb_imports = 0; /* number of imported dlls (delayed or not) */ static int nb_delayed = 0; /* number of delayed dlls */ @@ -103,6 +106,12 @@ inline static void sort_symbols( char **table, int size ) qsort( table, size, sizeof(*table), name_cmp ); } +/* remove the temp file at exit */ +static void remove_ld_tmp_file(void) +{ + if (ld_tmp_file) unlink( ld_tmp_file ); +} + /* open the .so library for a given dll in a specified path */ static char *try_library_path( const char *path, const char *name ) { @@ -385,15 +394,46 @@ static void warn_unused( const struct import* imp ) current_line = curline; } +/* combine a list of object files with ld into a single object file */ +/* returns the name of the combined file */ +static const char *ldcombine_files( char **argv ) +{ + int i, len = 0; + char *cmd; + int fd, err; + char buffer[] = "/tmp/winebuild.tmp.XXXXXX"; + + if ((fd = mkstemp( buffer ) == -1)) fatal_error( "could not generate a temp file\n" ); + close( fd ); + ld_tmp_file = xstrdup( buffer ); + atexit( remove_ld_tmp_file ); + + for (i = 0; argv[i]; i++) len += strlen(argv[i]) + 1; + cmd = xmalloc( len + strlen(ld_tmp_file) + 10 ); + sprintf( cmd, "ld -r -o %s", ld_tmp_file ); + for (i = 0; argv[i]; i++) sprintf( cmd + strlen(cmd), " %s", argv[i] ); + err = system( cmd ); + if (err) fatal_error( "ld -r failed with status %d\n", err ); + free( cmd ); + return ld_tmp_file; +} + /* read in the list of undefined symbols */ -void read_undef_symbols( const char *name ) +void read_undef_symbols( char **argv ) { FILE *f; char buffer[1024]; int err; + const char *name; + + if (!argv[0]) return; undef_size = nb_undef_symbols = 0; + /* if we have multiple object files, link them together */ + if (argv[1]) name = ldcombine_files( argv ); + else name = argv[0]; + sprintf( buffer, "nm -u %s", name ); if (!(f = popen( buffer, "r" ))) fatal_error( "Cannot execute '%s'\n", buffer ); diff --git a/tools/winebuild/main.c b/tools/winebuild/main.c index 40d5fdff61b..9851edd9094 100644 --- a/tools/winebuild/main.c +++ b/tools/winebuild/main.c @@ -62,8 +62,8 @@ char *init_func = NULL; char **debug_channels = NULL; char **lib_path = NULL; -const char *input_file_name; -const char *output_file_name; +char *input_file_name = NULL; +const char *output_file_name = NULL; static FILE *input_file; static FILE *output_file; @@ -82,17 +82,6 @@ static enum MODE_RELAY32 } exec_mode = MODE_NONE; -/* open the input file */ -static void open_input( const char *name ) -{ - input_file_name = name; - if (!(input_file = fopen( name, "r" ))) - { - fprintf( stderr, "Cannot open input file '%s'\n", name ); - exit(1); - } -} - /* set the dll file name from the input file name */ static void set_dll_file_name( const char *name ) { @@ -135,11 +124,11 @@ static void do_module( const char *arg ); static void do_spec( const char *arg ); static void do_def( const char *arg ); static void do_exe( const char *arg ); -static void do_glue( const char *arg ); +static void do_glue(void); static void do_relay16(void); static void do_relay32(void); static void do_debug(void); -static void do_sym( const char *arg ); +static void do_sym(void); static void do_chdir( const char *arg ); static void do_lib( const char *arg ); static void do_import( const char *arg ); @@ -161,13 +150,13 @@ static const struct option_descr option_table[] = { "-l", 1, do_import, "-l lib.dll Import the specified library" }, { "-dl", 1, do_dimport, "-dl lib.dll Delay-import the specified library" }, { "-res", 1, do_rsrc, "-res rsrc.res Load resources from rsrc.res" }, - { "-o", 1, do_output, "-o name Set the output file name (default: stdout)" }, - { "-sym", 1, do_sym, "-sym file.o Read the list of undefined symbols from 'file.o'\n" }, + { "-o", 1, do_output, "-o name Set the output file name (default: stdout)\n" }, + { "-sym", 0, do_sym, NULL }, /* ignored for backwards compatibility */ { "-spec", 1, do_spec, "-spec file.spec Build a .c file from a spec file" }, { "-def", 1, do_def, "-def file.spec Build a .def file from a spec file" }, { "-exe", 1, do_exe, "-exe name Build a .c file from the named executable" }, { "-debug", 0, do_debug, "-debug [files] Build a .c file containing debug channels declarations" }, - { "-glue", 1, do_glue, "-glue file.c Build the 16-bit glue for a .c file" }, + { "-glue", 0, do_glue, "-glue [files] Build the 16-bit glue for the source files" }, { "-relay16", 0, do_relay16, "-relay16 Build the 16-bit relay assembly routines" }, { "-relay32", 0, do_relay32, "-relay32 Build the 32-bit relay assembly routines" }, { NULL, 0, NULL, NULL } @@ -194,7 +183,9 @@ static void do_usage(void) const struct option_descr *opt; fprintf( stderr, "Usage: winebuild [options]\n\n" ); fprintf( stderr, "Options:\n" ); - for (opt = option_table; opt->name; opt++) fprintf( stderr, " %s\n", opt->usage ); + for (opt = option_table; opt->name; opt++) + if (opt->usage) fprintf( stderr, " %s\n", opt->usage ); + fprintf( stderr, "\nExactly one of -spec, -def, -exe, -debug, -glue, -relay16 or -relay32 must be specified.\n\n" ); exit(1); } @@ -231,7 +222,7 @@ static void do_spec( const char *arg ) { if (exec_mode != MODE_NONE || !arg[0]) do_usage(); exec_mode = MODE_SPEC; - open_input( arg ); + input_file = open_input_file( NULL, arg ); set_dll_file_name( arg ); } @@ -239,7 +230,7 @@ static void do_def( const char *arg ) { if (exec_mode != MODE_NONE || !arg[0]) do_usage(); exec_mode = MODE_DEF; - open_input( arg ); + input_file = open_input_file( NULL, arg ); set_dll_file_name( arg ); } @@ -270,11 +261,10 @@ static void do_module( const char *arg ) strcpy( owner_name, arg ); } -static void do_glue( const char *arg ) +static void do_glue(void) { - if (exec_mode != MODE_NONE || !arg[0]) do_usage(); + if (exec_mode != MODE_NONE) do_usage(); exec_mode = MODE_GLUE; - open_input( arg ); } static void do_debug(void) @@ -300,10 +290,9 @@ static void do_relay32(void) exec_mode = MODE_RELAY32; } -static void do_sym( const char *arg ) +static void do_sym(void) { - extern void read_undef_symbols( const char *name ); - read_undef_symbols( arg ); + /* nothing */ } static void do_lib( const char *arg ) @@ -331,10 +320,10 @@ static void do_rsrc( const char *arg ) static void parse_options( char *argv[] ) { const struct option_descr *opt; - char * const * ptr; + char **ptr, **last; const char* arg=NULL; - for (ptr = argv + 1; *ptr; ptr++) + for (ptr = last = argv + 1; *ptr; ptr++) { for (opt = option_table; opt->name; opt++) { @@ -355,21 +344,18 @@ static void parse_options( char *argv[] ) } } - if (!opt->name) + if (opt->name) { - if (exec_mode == MODE_DEBUG && **ptr != '-') - { - /* this a file name to parse for debug channels */ - parse_debug_channels( current_src_dir, *ptr ); - continue; - } - fprintf( stderr, "Unrecognized option '%s'\n", *ptr ); - do_usage(); + if (opt->has_arg && arg != NULL) opt->func( arg ); + else opt->func( "" ); + } + else /* keep this argument */ + { + if (last != ptr) *last = *ptr; + last++; } - - if (opt->has_arg && arg!=NULL) opt->func( arg ); - else opt->func( "" ); } + *last = NULL; } @@ -387,18 +373,23 @@ int main(int argc, char **argv) switch (ParseTopLevel( input_file, 0 )) { case SPEC_WIN16: + if (argv[1]) + fatal_error( "file argument '%s' not allowed in this mode\n", argv[1] ); BuildSpec16File( output_file ); break; case SPEC_WIN32: + read_undef_symbols( argv + 1 ); BuildSpec32File( output_file ); break; default: assert(0); } break; case MODE_EXE: + read_undef_symbols( argv + 1 ); BuildSpec32File( output_file ); break; case MODE_DEF: + if (argv[1]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[1] ); switch (ParseTopLevel( input_file, 1 )) { case SPEC_WIN16: @@ -411,15 +402,17 @@ int main(int argc, char **argv) } break; case MODE_DEBUG: - BuildDebugFile( output_file ); + BuildDebugFile( output_file, current_src_dir, argv + 1 ); break; case MODE_GLUE: - BuildGlue( output_file, input_file ); + BuildGlue( output_file, current_src_dir, argv + 1 ); break; case MODE_RELAY16: + if (argv[1]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[1] ); BuildRelays16( output_file ); break; case MODE_RELAY32: + if (argv[1]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[1] ); BuildRelays32( output_file ); break; default: diff --git a/tools/winebuild/parser.c b/tools/winebuild/parser.c index 727cdb6210c..886bc4c0227 100644 --- a/tools/winebuild/parser.c +++ b/tools/winebuild/parser.c @@ -595,20 +595,8 @@ void parse_debug_channels( const char *srcdir, const char *filename ) { FILE *file; int eol_seen = 1; - char *fullname = NULL; - if (srcdir) - { - fullname = xmalloc( strlen(srcdir) + strlen(filename) + 2 ); - strcpy( fullname, srcdir ); - strcat( fullname, "/" ); - strcat( fullname, filename ); - } - else fullname = xstrdup( filename ); - - if (!(file = fopen( fullname, "r" ))) fatal_error( "Cannot open file '%s'\n", fullname ); - input_file_name = fullname; - current_line = 1; + file = open_input_file( srcdir, filename ); while (fgets( ParseBuffer, sizeof(ParseBuffer), file )) { char *channel, *end, *p = ParseBuffer; @@ -645,8 +633,5 @@ void parse_debug_channels( const char *srcdir, const char *filename ) } current_line++; } - fclose( file ); - input_file_name = NULL; - current_line = 0; - free( fullname ); + close_input_file( file ); } diff --git a/tools/winebuild/spec16.c b/tools/winebuild/spec16.c index 811a9718d64..80720cac053 100644 --- a/tools/winebuild/spec16.c +++ b/tools/winebuild/spec16.c @@ -458,10 +458,7 @@ static void BuildCallTo16Func( FILE *outfile, const char *profile, const char *p if (!strncmp( "word_", profile, 5 )) short_ret = 1; else if (strncmp( "long_", profile, 5 )) - { - fprintf( stderr, "Invalid function name '%s'.\n", profile ); - exit(1); - } + fatal_error( "Invalid function name '%s'\n", profile ); fprintf( outfile, "unsigned %s __stdcall %s_CallTo16_%s( void (*proc)()", short_ret? "short" : "int", prefix, profile ); @@ -473,6 +470,7 @@ static void BuildCallTo16Func( FILE *outfile, const char *profile, const char *p { case 'w': fprintf( outfile, "unsigned short" ); argsize += 2; break; case 'l': fprintf( outfile, "unsigned int" ); argsize += 4; break; + default: fatal_error( "Invalid letter '%c' in function name '%s'\n", args[i], profile ); } fprintf( outfile, " arg%d", i+1 ); } @@ -907,7 +905,7 @@ void BuildSpec16File( FILE *outfile ) * * Build the 16-bit-to-Wine/Wine-to-16-bit callback glue code */ -void BuildGlue( FILE *outfile, FILE *infile ) +void BuildGlue( FILE *outfile, const char *srcdir, char **argv ) { char buffer[1024]; @@ -924,26 +922,33 @@ void BuildGlue( FILE *outfile, FILE *infile ) /* Build the callback glue functions */ - while (fgets( buffer, sizeof(buffer), infile )) + while (*argv) { - if (strstr( buffer, "### start build ###" )) break; - } - while (fgets( buffer, sizeof(buffer), infile )) - { - char *p; - if ( (p = strstr( buffer, "CallTo16_" )) != NULL ) - { - char *q, *profile = p + strlen( "CallTo16_" ); - for (q = profile; (*q == '_') || isalpha(*q); q++ ) - ; - *q = '\0'; - for (q = p-1; q > buffer && ((*q == '_') || isalnum(*q)); q-- ) - ; - if ( ++q < p ) p[-1] = '\0'; else q = ""; - BuildCallTo16Func( outfile, profile, q ); - } - if (strstr( buffer, "### stop build ###" )) break; - } + FILE *infile = open_input_file( srcdir, *argv ); - fclose( infile ); + while (fgets( buffer, sizeof(buffer), infile )) + { + current_line++; + if (strstr( buffer, "### start build ###" )) break; + } + while (fgets( buffer, sizeof(buffer), infile )) + { + char *p; + if ( (p = strstr( buffer, "CallTo16_" )) != NULL ) + { + char *q, *profile = p + strlen( "CallTo16_" ); + for (q = profile; (*q == '_') || isalpha(*q); q++ ) + ; + *q = '\0'; + for (q = p-1; q > buffer && ((*q == '_') || isalnum(*q)); q-- ) + ; + if ( ++q < p ) p[-1] = '\0'; else q = ""; + BuildCallTo16Func( outfile, profile, q ); + } + current_line++; + if (strstr( buffer, "### stop build ###" )) break; + } + close_input_file( infile ); + argv++; + } } diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index 25fd2533969..01f19f37c7d 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -894,11 +894,13 @@ void BuildDef32File(FILE *outfile) * * Build the debugging channels source file. */ -void BuildDebugFile( FILE *outfile ) +void BuildDebugFile( FILE *outfile, const char *srcdir, char **argv ) { int nr_debug; char *prefix, *p; + while (*argv) parse_debug_channels( srcdir, *argv++ ); + output_standard_file_header( outfile ); nr_debug = output_debug( outfile ); if (!nr_debug) diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index 68b6e312724..cbc8e51a597 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -152,6 +152,46 @@ void dump_bytes( FILE *outfile, const unsigned char *data, int len, } +/******************************************************************* + * open_input_file + * + * Open a file in the given srcdir and set the input_file_name global variable. + */ +FILE *open_input_file( const char *srcdir, const char *name ) +{ + char *fullname; + FILE *file; + + if (srcdir) + { + fullname = xmalloc( strlen(srcdir) + strlen(name) + 2 ); + strcpy( fullname, srcdir ); + strcat( fullname, "/" ); + strcat( fullname, name ); + } + else fullname = xstrdup( name ); + + if (!(file = fopen( fullname, "r" ))) fatal_error( "Cannot open file '%s'\n", fullname ); + input_file_name = fullname; + current_line = 1; + return file; +} + + +/******************************************************************* + * close_input_file + * + * Close the current input file (must have been opened with open_input_file). + */ +void close_input_file( FILE *file ) +{ + fclose( file ); + free( input_file_name ); + input_file_name = NULL; + current_line = 0; +} + + /******************************************************************* * make_c_identifier * diff --git a/tools/winebuild/winebuild.man.in b/tools/winebuild/winebuild.man.in index 5db4125a243..0a40daf854c 100644 --- a/tools/winebuild/winebuild.man.in +++ b/tools/winebuild/winebuild.man.in @@ -3,7 +3,7 @@ .SH NAME winebuild \- Wine dll builder .SH SYNOPSIS -.BI "winebuild " "[options] " "[inputfile]" +.BI winebuild\ [options]\ [input\ files] .SH DESCRIPTION .B winebuild generates the C and assembly files that are necessary to build a Wine @@ -23,29 +23,42 @@ what you want winebuild to generate. Build a C file from a spec file (see \fBSPEC FILE SYNTAX\fR for details). The resulting C file must be compiled and linked to the other object files to build a working Wine dll. -.TP -.BI \-def\ file.spec -Build a .def file from a spec file. This is used when building dlls -with a PE (Win32) compiler. +.br +In that mode, the +.I input files +should be the list of all object files that will be linked into the +final dll, to allow +.B winebuild +to get the list of all undefined symbols that need to be imported from +other dlls. .TP .BI \-exe\ name Build a C file for the named executable. This is basically the same as the -spec mode except that it doesn't require a .spec file as input, since an executable doesn't export functions. The resulting C file must be compiled and linked to the other object files to build a -working Wine executable. +working Wine executable, and all the other object files must be listed +as +.I input files. .TP -.BI \-debug\ [files] -Build a C file containing the definitions for debugging channels. The -\fIfiles\fR argument is a list of C files to search for debug channel +.BI \-def\ file.spec +Build a .def file from a spec file. This is used when building dlls +with a PE (Win32) compiler. +.TP +.B \-debug +Build a C file containing the definitions for debugging channels. In +that mode the +.I input files +should be a list of C files to search for debug channel definitions. The resulting C file must be compiled and linked with the dll. .TP -.BI \-glue\ file.c +.B \-glue Build a C file containing the glue code for the 16-bit calls contained -in the \fIfile.c\fR source. These calls must be specified in the -source file using special markers, as described in the \fBGLUE -FUNCTIONS\fR section. +in the +.I input files. +These calls must be specified in the source files using special +markers, as described in the \fBGLUE FUNCTIONS\fR section. .TP .B \-relay16 Generate the assembly code for the 16-bit relay routines. This is for @@ -58,7 +71,8 @@ Wine internal usage only, you should never need to use this option. .TP .BI \-C\ directory Change to the specified directory before reading source files. Only -meaningful in \fB-debug\fR mode. +meaningful in +.BR \-debug\ and\ -glue\ modes. .TP .BI \-D\ symbol Ignored for compatibility with the C compiler. @@ -125,8 +139,6 @@ Load resources from the specified binary resource file. The \fIrsrc.res\fR can be produced from a source resource file with .BR wrc(1) (or with a Windows resource compiler). -.BI \-sym\ file.o -Read the list of undefined symbols from the object file file.o. .TP .B \-w Turn on warnings.