winebuild: Add a --resources mode that compiles multiple resource files into a single object.

This commit is contained in:
Alexandre Julliard 2009-05-30 21:39:50 +02:00
parent a37b8f79af
commit a328834f5b
5 changed files with 175 additions and 6 deletions

View File

@ -205,6 +205,7 @@ extern int output( const char *format, ... )
extern const char *get_as_command(void); extern const char *get_as_command(void);
extern const char *get_ld_command(void); extern const char *get_ld_command(void);
extern const char *get_nm_command(void); extern const char *get_nm_command(void);
extern const char *get_windres_command(void);
extern char *get_temp_file_name( const char *prefix, const char *suffix ); extern char *get_temp_file_name( const char *prefix, const char *suffix );
extern void output_standard_file_header(void); extern void output_standard_file_header(void);
extern FILE *open_input_file( const char *srcdir, const char *name ); extern FILE *open_input_file( const char *srcdir, const char *name );
@ -250,6 +251,7 @@ extern void load_res16_file( const char *name, DLLSPEC *spec );
extern void output_res16_data( DLLSPEC *spec ); extern void output_res16_data( DLLSPEC *spec );
extern void output_res16_directory( DLLSPEC *spec ); extern void output_res16_directory( DLLSPEC *spec );
extern void output_spec16_file( DLLSPEC *spec ); extern void output_spec16_file( DLLSPEC *spec );
extern void output_res_o_file( DLLSPEC *spec );
extern void BuildRelays16(void); extern void BuildRelays16(void);
extern void BuildRelays32(void); extern void BuildRelays32(void);

View File

@ -96,7 +96,8 @@ enum exec_mode_values
MODE_EXE, MODE_EXE,
MODE_DEF, MODE_DEF,
MODE_RELAY16, MODE_RELAY16,
MODE_RELAY32 MODE_RELAY32,
MODE_RESOURCES
}; };
static enum exec_mode_values exec_mode = MODE_NONE; static enum exec_mode_values exec_mode = MODE_NONE;
@ -243,6 +244,7 @@ static const char usage_str[] =
" --exe Build a .c file for an executable\n" " --exe Build a .c file for an executable\n"
" --relay16 Build the 16-bit relay assembly routines\n" " --relay16 Build the 16-bit relay assembly routines\n"
" --relay32 Build the 32-bit relay assembly routines\n\n" " --relay32 Build the 32-bit relay assembly routines\n\n"
" --resources Build a .o file for the resource files\n\n"
"The mode options are mutually exclusive; you must specify one and only one.\n\n"; "The mode options are mutually exclusive; you must specify one and only one.\n\n";
enum long_options_values enum long_options_values
@ -257,6 +259,7 @@ enum long_options_values
LONG_OPT_NXCOMPAT, LONG_OPT_NXCOMPAT,
LONG_OPT_RELAY16, LONG_OPT_RELAY16,
LONG_OPT_RELAY32, LONG_OPT_RELAY32,
LONG_OPT_RESOURCES,
LONG_OPT_SAVE_TEMPS, LONG_OPT_SAVE_TEMPS,
LONG_OPT_SUBSYSTEM, LONG_OPT_SUBSYSTEM,
LONG_OPT_VERSION LONG_OPT_VERSION
@ -276,6 +279,7 @@ static const struct option long_options[] =
{ "nxcompat", 1, 0, LONG_OPT_NXCOMPAT }, { "nxcompat", 1, 0, LONG_OPT_NXCOMPAT },
{ "relay16", 0, 0, LONG_OPT_RELAY16 }, { "relay16", 0, 0, LONG_OPT_RELAY16 },
{ "relay32", 0, 0, LONG_OPT_RELAY32 }, { "relay32", 0, 0, LONG_OPT_RELAY32 },
{ "resources", 0, 0, LONG_OPT_RESOURCES },
{ "save-temps", 0, 0, LONG_OPT_SAVE_TEMPS }, { "save-temps", 0, 0, LONG_OPT_SAVE_TEMPS },
{ "subsystem", 1, 0, LONG_OPT_SUBSYSTEM }, { "subsystem", 1, 0, LONG_OPT_SUBSYSTEM },
{ "version", 0, 0, LONG_OPT_VERSION }, { "version", 0, 0, LONG_OPT_VERSION },
@ -463,6 +467,9 @@ static char **parse_options( int argc, char **argv, DLLSPEC *spec )
case LONG_OPT_RELAY32: case LONG_OPT_RELAY32:
set_exec_mode( MODE_RELAY32 ); set_exec_mode( MODE_RELAY32 );
break; break;
case LONG_OPT_RESOURCES:
set_exec_mode( MODE_RESOURCES );
break;
case LONG_OPT_SAVE_TEMPS: case LONG_OPT_SAVE_TEMPS:
save_temps = 1; save_temps = 1;
break; break;
@ -621,6 +628,10 @@ int main(int argc, char **argv)
if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] ); if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] );
BuildRelays32(); BuildRelays32();
break; break;
case MODE_RESOURCES:
load_resources( argv, spec );
output_res_o_file( spec );
break;
default: default:
usage(1); usage(1);
break; break;

View File

@ -21,6 +21,7 @@
#include "config.h" #include "config.h"
#include "wine/port.h" #include "wine/port.h"
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -55,6 +56,7 @@ struct resource
struct string_id name; struct string_id name;
const void *data; const void *data;
unsigned int data_size; unsigned int data_size;
unsigned short mem_options;
unsigned short lang; unsigned short lang;
}; };
@ -89,6 +91,9 @@ static const unsigned char *file_pos; /* current position in resource file */
static const unsigned char *file_end; /* end of resource file */ static const unsigned char *file_end; /* end of resource file */
static const char *file_name; /* current resource file name */ static const char *file_name; /* current resource file name */
static unsigned char *file_out_pos; /* current position in output resource file */
static unsigned char *file_out_end; /* end of output buffer */
/* size of a resource directory with n entries */ /* size of a resource directory with n entries */
#define RESOURCE_DIR_SIZE (4 * sizeof(unsigned int)) #define RESOURCE_DIR_SIZE (4 * sizeof(unsigned int))
#define RESOURCE_DIR_ENTRY_SIZE (2 * sizeof(unsigned int)) #define RESOURCE_DIR_ENTRY_SIZE (2 * sizeof(unsigned int))
@ -178,6 +183,41 @@ static void get_string( struct string_id *str )
} }
} }
/* put a word into the resource file */
static void put_word( unsigned short val )
{
if (byte_swapped) val = (val << 8) | (val >> 8);
*(unsigned short *)file_out_pos = val;
file_out_pos += sizeof(unsigned short);
assert( file_out_pos <= file_out_end );
}
/* put a dword into the resource file */
static void put_dword( unsigned int val )
{
if (byte_swapped)
val = ((val << 24) | ((val << 8) & 0x00ff0000) | ((val >> 8) & 0x0000ff00) | (val >> 24));
*(unsigned int *)file_out_pos = val;
file_out_pos += sizeof(unsigned int);
assert( file_out_pos <= file_out_end );
}
/* put a string into the resource file */
static void put_string( const struct string_id *str )
{
if (str->str)
{
const WCHAR *p = str->str;
while (*p) put_word( *p++ );
put_word( 0 );
}
else
{
put_word( 0xffff );
put_word( str->id );
}
}
/* check the file header */ /* check the file header */
/* all values must be zero except header size */ /* all values must be zero except header size */
static int check_header(void) static int check_header(void)
@ -204,7 +244,7 @@ static void load_next_resource( DLLSPEC *spec )
unsigned int hdr_size; unsigned int hdr_size;
struct resource *res = add_resource( spec ); struct resource *res = add_resource( spec );
res->data_size = (get_dword() + 3) & ~3; res->data_size = get_dword();
hdr_size = get_dword(); hdr_size = get_dword();
if (hdr_size & 3) fatal_error( "%s header size not aligned\n", file_name ); if (hdr_size & 3) fatal_error( "%s header size not aligned\n", file_name );
@ -213,12 +253,12 @@ static void load_next_resource( DLLSPEC *spec )
get_string( &res->name ); get_string( &res->name );
if ((unsigned long)file_pos & 2) get_word(); /* align to dword boundary */ if ((unsigned long)file_pos & 2) get_word(); /* align to dword boundary */
get_dword(); /* skip data version */ get_dword(); /* skip data version */
get_word(); /* skip mem options */ res->mem_options = get_word();
res->lang = get_word(); res->lang = get_word();
get_dword(); /* skip version */ get_dword(); /* skip version */
get_dword(); /* skip characteristics */ get_dword(); /* skip characteristics */
file_pos = (const unsigned char *)res->data + res->data_size; file_pos = (const unsigned char *)res->data + ((res->data_size + 3) & ~3);
if (file_pos > file_end) fatal_error( "%s is a truncated file\n", file_name ); if (file_pos > file_end) fatal_error( "%s is a truncated file\n", file_name );
} }
@ -441,7 +481,7 @@ void output_resources( DLLSPEC *spec )
for (i = 0, res = spec->resources; i < spec->nb_resources; i++, res++) for (i = 0, res = spec->resources; i < spec->nb_resources; i++, res++)
output( "\t.long .L__wine_spec_res_%d-.L__wine_spec_rva_base,%u,0,0\n", output( "\t.long .L__wine_spec_res_%d-.L__wine_spec_rva_base,%u,0,0\n",
i, res->data_size ); i, (res->data_size + 3) & ~3 );
/* dump the name strings */ /* dump the name strings */
@ -458,10 +498,98 @@ void output_resources( DLLSPEC *spec )
{ {
output( "\n\t.align %d\n", get_alignment(get_ptr_size()) ); output( "\n\t.align %d\n", get_alignment(get_ptr_size()) );
output( ".L__wine_spec_res_%d:\n", i ); output( ".L__wine_spec_res_%d:\n", i );
dump_bytes( res->data, res->data_size ); dump_bytes( res->data, (res->data_size + 3) & ~3 );
} }
output( ".L__wine_spec_resources_end:\n" ); output( ".L__wine_spec_resources_end:\n" );
output( "\t.byte 0\n" ); output( "\t.byte 0\n" );
free_resource_tree( tree ); free_resource_tree( tree );
} }
static unsigned int get_resource_header_size( const struct resource *res )
{
unsigned int size = 5 * sizeof(unsigned int) + 2 * sizeof(unsigned short);
if (!res->type.str) size += 2 * sizeof(unsigned short);
else size += (strlenW(res->type.str) + 1) * sizeof(WCHAR);
if (!res->name.str) size += 2 * sizeof(unsigned short);
else size += (strlenW(res->name.str) + 1) * sizeof(WCHAR);
return size;
}
/* output the resources into a .o file */
void output_res_o_file( DLLSPEC *spec )
{
unsigned int i, total_size;
unsigned char *data;
char *res_file, *cmd;
const char *prog;
int fd, err;
if (!spec->nb_resources) fatal_error( "--resources mode needs at least one resource file as input\n" );
if (!output_file_name) fatal_error( "No output file name specified\n" );
total_size = 32; /* header */
for (i = 0; i < spec->nb_resources; i++)
{
total_size += (get_resource_header_size( &spec->resources[i] ) + 3) & ~3;
total_size += (spec->resources[i].data_size + 3) & ~3;
}
data = xmalloc( total_size );
byte_swapped = 0;
file_out_pos = data;
file_out_end = data + total_size;
put_dword( 0 ); /* ResSize */
put_dword( 32 ); /* HeaderSize */
put_word( 0xffff ); /* ResType */
put_word( 0x0000 );
put_word( 0xffff ); /* ResName */
put_word( 0x0000 );
put_dword( 0 ); /* DataVersion */
put_word( 0 ); /* Memory options */
put_word( 0 ); /* Language */
put_dword( 0 ); /* Version */
put_dword( 0 ); /* Characteristics */
for (i = 0; i < spec->nb_resources; i++)
{
unsigned int header_size = get_resource_header_size( &spec->resources[i] );
put_dword( spec->resources[i].data_size );
put_dword( header_size );
put_string( &spec->resources[i].type );
put_string( &spec->resources[i].name );
if ((unsigned long)file_out_pos & 2) put_word( 0 );
put_dword( 0 );
put_word( spec->resources[i].mem_options );
put_word( spec->resources[i].lang );
put_dword( 0 );
put_dword( 0 );
memcpy( file_out_pos, spec->resources[i].data, spec->resources[i].data_size );
file_out_pos += spec->resources[i].data_size;
while ((unsigned long)file_out_pos & 3) *file_out_pos++ = 0;
}
assert( file_out_pos == file_out_end );
res_file = get_temp_file_name( output_file_name, ".res" );
if ((fd = open( res_file, O_WRONLY|O_CREAT|O_TRUNC, 0600 )) == -1)
fatal_error( "Cannot create %s\n", res_file );
if (write( fd, data, total_size ) != total_size)
fatal_error( "Error writing to %s\n", res_file );
close( fd );
free( data );
prog = get_windres_command();
cmd = xmalloc( strlen(prog) + strlen(res_file) + strlen(output_file_name) + 9 );
sprintf( cmd, "%s -i %s -o %s", prog, res_file, output_file_name );
if (verbose) fprintf( stderr, "%s\n", cmd );
err = system( cmd );
if (err) fatal_error( "%s failed with status %d\n", prog, err );
free( cmd );
output_file_name = NULL; /* so we don't try to assemble it */
}

View File

@ -319,6 +319,27 @@ const char *get_nm_command(void)
return nm_command; return nm_command;
} }
const char *get_windres_command(void)
{
static char *windres_command;
if (!windres_command)
{
if (target_alias)
{
windres_command = xmalloc( strlen(target_alias) + sizeof("-windres") );
strcpy( windres_command, target_alias );
strcat( windres_command, "-windres" );
}
else
{
static const char * const commands[] = { "windres", NULL };
if (!(windres_command = find_tool( commands ))) windres_command = xstrdup("windres");
}
}
return windres_command;
}
/* get a name for a temp file, automatically cleaned up on exit */ /* get a name for a temp file, automatically cleaned up on exit */
char *get_temp_file_name( const char *prefix, const char *suffix ) char *get_temp_file_name( const char *prefix, const char *suffix )
{ {

View File

@ -53,6 +53,13 @@ Wine internal usage only, you should never need to use this option.
.B \--relay32 .B \--relay32
Generate the assembly code for the 32-bit relay routines. This is for Generate the assembly code for the 32-bit relay routines. This is for
Wine internal usage only, you should never need to use this option. Wine internal usage only, you should never need to use this option.
.TP
.B \--resources
Generate a .o file containing all the input resources. This is useful
when building with a PE compiler, since the PE binutils cannot handle
multiple resource files as input. For a standard Unix build, the
resource files are automatically included when building the spec file,
so there's no need for an intermediate .o file.
.SH OPTIONS .SH OPTIONS
.TP .TP
.BI \--as-cmd= as-command .BI \--as-cmd= as-command