In -spec and -exe mode, accept multiple object files and link them

together internally to find the undefined symbols.
In -glue mode, accept multiple C files and generate a single glue
file for all of them.
This commit is contained in:
Alexandre Julliard 2002-08-01 18:34:12 +00:00
parent 2a91e3f99b
commit 1862a67d2d
8 changed files with 186 additions and 106 deletions

View File

@ -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;

View File

@ -26,6 +26,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#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 );

View File

@ -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:

View File

@ -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 );
}

View File

@ -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++;
}
}

View File

@ -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)

View File

@ -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
*

View File

@ -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.