*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
+#include "wine/port.h"
#include <assert.h>
#include <stdio.h>
#include "build.h"
int UsePIC = 0;
-int nb_debug_channels = 0;
int nb_lib_paths = 0;
int nb_errors = 0;
int display_warnings = 0;
int kill_at = 0;
+int verbose = 0;
+int save_temps = 0;
+int link_ext_symbols = 0;
+
+#if defined(__i386__) || defined(__x86_64__)
+enum target_cpu target_cpu = CPU_x86;
+#elif defined(__x86_64__)
+enum target_cpu target_cpu = CPU_x86_64;
+#elif defined(__sparc__)
+enum target_cpu target_cpu = CPU_SPARC;
+#elif defined(__ALPHA__)
+enum target_cpu target_cpu = CPU_ALPHA;
+#elif defined(__powerpc__)
+enum target_cpu target_cpu = CPU_POWERPC;
+#else
+#error Unsupported CPU
+#endif
-/* we only support relay debugging on i386 */
-#ifdef __i386__
-int debugging = 1;
+#ifdef __APPLE__
+enum target_platform target_platform = PLATFORM_APPLE;
+#elif defined(_WINDOWS)
+enum target_platform target_platform = PLATFORM_WINDOWS;
#else
-int debugging = 0;
+enum target_platform target_platform = PLATFORM_UNSPECIFIED;
#endif
-char **debug_channels = NULL;
char **lib_path = NULL;
char *input_file_name = NULL;
+char *spec_file_name = NULL;
+FILE *output_file = NULL;
const char *output_file_name = NULL;
+static const char *output_file_source_name;
-char *ld_command = "ld";
-char *nm_command = "nm";
+char *as_command = NULL;
+char *ld_command = NULL;
+char *nm_command = NULL;
-static FILE *output_file;
-static const char *current_src_dir;
static int nb_res_files;
static char **res_files;
-static char *spec_file_name;
/* execution mode */
enum exec_mode_values
MODE_DLL,
MODE_EXE,
MODE_DEF,
- MODE_DEBUG,
MODE_RELAY16,
MODE_RELAY32,
MODE_PEDLL
static enum exec_mode_values exec_mode = MODE_NONE;
+static const struct
+{
+ const char *name;
+ enum target_cpu cpu;
+} cpu_names[] =
+{
+ { "i386", CPU_x86 },
+ { "i486", CPU_x86 },
+ { "i586", CPU_x86 },
+ { "i686", CPU_x86 },
+ { "i786", CPU_x86 },
+ { "x86_64", CPU_x86_64 },
+ { "sparc", CPU_SPARC },
+ { "alpha", CPU_ALPHA },
+ { "powerpc", CPU_POWERPC }
+};
+
+static const struct
+{
+ const char *name;
+ enum target_platform platform;
+} platform_names[] =
+{
+ { "macos", PLATFORM_APPLE },
+ { "darwin", PLATFORM_APPLE },
+ { "windows", PLATFORM_WINDOWS },
+ { "winnt", PLATFORM_WINDOWS }
+};
+
/* set the dll file name from the input file name */
static void set_dll_file_name( const char *name, DLLSPEC *spec )
{
{
if (!strcmp( p, ".spec" ) || !strcmp( p, ".def" )) *p = 0;
}
- if (!strchr( spec->file_name, '.' )) strcat( spec->file_name, ".dll" );
}
/* set the dll subsystem */
free( str );
}
+/* set the target CPU and platform */
+static void set_target( const char *target )
+{
+ unsigned int i;
+ char *p, *platform, *spec = xstrdup( target );
+
+ /* target specification is in the form CPU-MANUFACTURER-OS or CPU-MANUFACTURER-KERNEL-OS */
+
+ /* get the CPU part */
+
+ if (!(p = strchr( spec, '-' ))) fatal_error( "Invalid target specification '%s'\n", target );
+ *p++ = 0;
+ for (i = 0; i < sizeof(cpu_names)/sizeof(cpu_names[0]); i++)
+ {
+ if (!strcmp( cpu_names[i].name, spec )) break;
+ }
+ if (i < sizeof(cpu_names)/sizeof(cpu_names[0])) target_cpu = cpu_names[i].cpu;
+ else fatal_error( "Unrecognized CPU '%s'\n", spec );
+
+ platform = p;
+ if ((p = strrchr( p, '-' ))) platform = p + 1;
+
+ /* get the OS part */
+
+ target_platform = PLATFORM_UNSPECIFIED; /* default value */
+ for (i = 0; i < sizeof(platform_names)/sizeof(platform_names[0]); i++)
+ {
+ if (!strncmp( platform_names[i].name, platform, strlen(platform_names[i].name) ))
+ {
+ target_platform = platform_names[i].platform;
+ break;
+ }
+ }
+
+ free( spec );
+
+ if (!as_command)
+ {
+ as_command = xmalloc( strlen(target) + sizeof("-as") );
+ strcpy( as_command, target );
+ strcat( as_command, "-as" );
+ }
+ if (!ld_command)
+ {
+ ld_command = xmalloc( strlen(target) + sizeof("-ld") );
+ strcpy( ld_command, target );
+ strcat( ld_command, "-ld" );
+ }
+ if (!nm_command)
+ {
+ nm_command = xmalloc( strlen(target) + sizeof("-nm") );
+ strcpy( nm_command, target );
+ strcat( nm_command, "-nm" );
+ }
+}
+
/* cleanup on program exit */
static void cleanup(void)
{
static const char usage_str[] =
"Usage: winebuild [OPTIONS] [FILES]\n\n"
"Options:\n"
-" -C --source-dir=DIR Look for source files in DIR\n"
-" -d --delay-lib=LIB Import the specified library in delayed mode\n"
-" -D SYM Ignored for C flags compatibility\n"
-" -e --entry=FUNC Set the DLL entry point function (default: DllMain)\n"
-" -f FLAGS Compiler flags (only -fPIC is supported)\n"
-" -F --filename=DLLFILE Set the DLL filename (default: from input file name)\n"
-" -h --help Display this help message\n"
-" -H --heap=SIZE Set the heap size for a Win16 dll\n"
-" -i --ignore=SYM[,SYM] Ignore specified symbols when resolving imports\n"
-" -I DIR Ignored for C flags compatibility\n"
-" -k --kill-at Kill stdcall decorations in generated .def files\n"
-" -K FLAGS Compiler flags (only -KPIC is supported)\n"
+" --as-cmd=AS Command to use for assembling (default: as)\n"
+" -d, --delay-lib=LIB Import the specified library in delayed mode\n"
+" -D SYM Ignored for C flags compatibility\n"
+" -e, --entry=FUNC Set the DLL entry point function (default: DllMain)\n"
+" -E, --export=FILE Export the symbols defined in the .spec or .def file\n"
+" --external-symbols Allow linking to external symbols\n"
+" -f FLAGS Compiler flags (only -fPIC is supported)\n"
+" -F, --filename=DLLFILE Set the DLL filename (default: from input file name)\n"
+" -h, --help Display this help message\n"
+" -H, --heap=SIZE Set the heap size for a Win16 dll\n"
+" -i, --ignore=SYM[,SYM] Ignore specified symbols when resolving imports\n"
+" -I DIR Ignored for C flags compatibility\n"
+" -k, --kill-at Kill stdcall decorations in generated .def files\n"
+" -K, FLAGS Compiler flags (only -KPIC is supported)\n"
" --ld-cmd=LD Command to use for linking (default: ld)\n"
-" -l --library=LIB Import the specified library\n"
-" -L --library-path=DIR Look for imports libraries in DIR\n"
-" -M --main-module=MODULE Set the name of the main module for a Win16 dll\n"
+" -l, --library=LIB Import the specified library\n"
+" -L, --library-path=DIR Look for imports libraries in DIR\n"
+" -M, --main-module=MODULE Set the name of the main module for a Win16 dll\n"
" --nm-cmd=NM Command to use to get undefined symbols (default: nm)\n"
-" -N --dll-name=DLLNAME Set the DLL name (default: from input file name)\n"
-" -o --output=NAME Set the output file name (default: stdout)\n"
-" -r --res=RSRC.RES Load resources from RSRC.RES\n"
+" --nxcompat=y|n Set the NX compatibility flag (default: yes)\n"
+" -N, --dll-name=DLLNAME Set the DLL name (default: from input file name)\n"
+" -o, --output=NAME Set the output file name (default: stdout)\n"
+" -r, --res=RSRC.RES Load resources from RSRC.RES\n"
+" --save-temps Do not delete the generated intermediate files\n"
" --subsystem=SUBSYS Set the subsystem (one of native, windows, console)\n"
+" --target=TARGET Specify target CPU and platform for cross-compiling\n"
+" -u, --undefined=SYMBOL Add an undefined reference to SYMBOL when linking\n"
+" -v, --verbose Display the programs invoked\n"
" --version Print the version and exit\n"
-" -w --warnings Turn on warnings\n"
+" -w, --warnings Turn on warnings\n"
"\nMode options:\n"
-" --dll=FILE Build a .c file from a .spec or .def file\n"
-" --def=FILE.SPEC Build a .def file from a spec file\n"
-" --exe=NAME Build a .c file for the named executable\n"
-" --debug [FILES] Build a .c file with the debug channels declarations\n"
+" --dll Build a .c file from a .spec or .def file\n"
+" --def Build a .def file from a .spec file\n"
+" --exe Build a .c file for an executable\n"
" --relay16 Build the 16-bit relay assembly routines\n"
-" --relay32 Build the 32-bit relay assembly routines\n"
+" --relay32 Build the 32-bit relay assembly routines\n\n"
" --pedll Build a .c file for PE dll\n\n"
"The mode options are mutually exclusive; you must specify one and only one.\n\n";
LONG_OPT_DLL = 1,
LONG_OPT_DEF,
LONG_OPT_EXE,
- LONG_OPT_DEBUG,
+ LONG_OPT_ASCMD,
+ LONG_OPT_EXTERNAL_SYMS,
LONG_OPT_LDCMD,
LONG_OPT_NMCMD,
+ LONG_OPT_NXCOMPAT,
LONG_OPT_RELAY16,
LONG_OPT_RELAY32,
+ LONG_OPT_SAVE_TEMPS,
LONG_OPT_SUBSYSTEM,
+ LONG_OPT_TARGET,
LONG_OPT_VERSION,
LONG_OPT_PEDLL
};
-static const char short_options[] = "C:D:F:H:I:K:L:M:N:d:e:f:hi:kl:m:o:r:w";
+static const char short_options[] = "C:D:E:F:H:I:K:L:M:N:d:e:f:hi:kl:m:o:r:u:vw";
static const struct option long_options[] =
{
- { "dll", 1, 0, LONG_OPT_DLL },
- { "def", 1, 0, LONG_OPT_DEF },
- { "exe", 1, 0, LONG_OPT_EXE },
- { "debug", 0, 0, LONG_OPT_DEBUG },
- { "ld-cmd", 1, 0, LONG_OPT_LDCMD },
- { "nm-cmd", 1, 0, LONG_OPT_NMCMD },
- { "relay16", 0, 0, LONG_OPT_RELAY16 },
- { "relay32", 0, 0, LONG_OPT_RELAY32 },
- { "subsystem",1, 0, LONG_OPT_SUBSYSTEM },
- { "version", 0, 0, LONG_OPT_VERSION },
- { "pedll", 1, 0, LONG_OPT_PEDLL },
+ { "dll", 0, 0, LONG_OPT_DLL },
+ { "def", 0, 0, LONG_OPT_DEF },
+ { "exe", 0, 0, LONG_OPT_EXE },
+ { "as-cmd", 1, 0, LONG_OPT_ASCMD },
+ { "external-symbols", 0, 0, LONG_OPT_EXTERNAL_SYMS },
+ { "ld-cmd", 1, 0, LONG_OPT_LDCMD },
+ { "nm-cmd", 1, 0, LONG_OPT_NMCMD },
+ { "nxcompat", 1, 0, LONG_OPT_NXCOMPAT },
+ { "relay16", 0, 0, LONG_OPT_RELAY16 },
+ { "relay32", 0, 0, LONG_OPT_RELAY32 },
+ { "save-temps", 0, 0, LONG_OPT_SAVE_TEMPS },
+ { "subsystem", 1, 0, LONG_OPT_SUBSYSTEM },
+ { "target", 1, 0, LONG_OPT_TARGET },
+ { "version", 0, 0, LONG_OPT_VERSION },
+ { "pedll", 1, 0, LONG_OPT_PEDLL },
/* aliases for short options */
- { "source-dir", 1, 0, 'C' },
{ "delay-lib", 1, 0, 'd' },
+ { "export", 1, 0, 'E' },
{ "entry", 1, 0, 'e' },
{ "filename", 1, 0, 'F' },
{ "help", 0, 0, 'h' },
{ "dll-name", 1, 0, 'N' },
{ "output", 1, 0, 'o' },
{ "res", 1, 0, 'r' },
+ { "undefined", 1, 0, 'u' },
+ { "verbose", 0, 0, 'v' },
{ "warnings", 0, 0, 'w' },
{ NULL, 0, 0, 0 }
};
{
switch(optc)
{
- case 'C':
- current_src_dir = optarg;
- break;
case 'D':
/* ignored */
break;
+ case 'E':
+ spec_file_name = xstrdup( optarg );
+ set_dll_file_name( optarg, spec );
+ break;
case 'F':
spec->file_name = xstrdup( optarg );
break;
lib_path[nb_lib_paths++] = xstrdup( optarg );
break;
case 'M':
- spec->owner_name = xstrdup( optarg );
spec->type = SPEC_WIN16;
break;
case 'N':
spec->dll_name = xstrdup( optarg );
break;
case 'd':
- add_import_dll( optarg, 1 );
+ add_delayed_import( optarg );
break;
case 'e':
spec->init_func = xstrdup( optarg );
kill_at = 1;
break;
case 'l':
- add_import_dll( optarg, 0 );
+ add_import_dll( optarg, NULL );
break;
case 'o':
- if (unlink( optarg ) == -1 && errno != ENOENT)
- fatal_error( "Unable to create output file '%s'\n", optarg );
- if (!(output_file = fopen( optarg, "w" )))
- fatal_error( "Unable to create output file '%s'\n", optarg );
- output_file_name = xstrdup(optarg);
- atexit( cleanup ); /* make sure we remove the output file on exit */
+ {
+ char *ext = strrchr( optarg, '.' );
+
+ if (unlink( optarg ) == -1 && errno != ENOENT)
+ fatal_error( "Unable to create output file '%s'\n", optarg );
+ if (ext && !strcmp( ext, ".o" ))
+ {
+ output_file_source_name = get_temp_file_name( optarg, ".s" );
+ if (!(output_file = fopen( output_file_source_name, "w" )))
+ fatal_error( "Unable to create output file '%s'\n", optarg );
+ }
+ else
+ {
+ if (!(output_file = fopen( optarg, "w" )))
+ fatal_error( "Unable to create output file '%s'\n", optarg );
+ }
+ output_file_name = xstrdup(optarg);
+ atexit( cleanup ); /* make sure we remove the output file on exit */
+ }
break;
case 'r':
res_files = xrealloc( res_files, (nb_res_files+1) * sizeof(*res_files) );
res_files[nb_res_files++] = xstrdup( optarg );
break;
+ case 'u':
+ add_extra_ld_symbol( optarg );
+ break;
+ case 'v':
+ verbose++;
+ break;
case 'w':
display_warnings = 1;
break;
case LONG_OPT_DLL:
set_exec_mode( MODE_DLL );
- spec_file_name = xstrdup( optarg );
- set_dll_file_name( optarg, spec );
break;
case LONG_OPT_DEF:
set_exec_mode( MODE_DEF );
- spec_file_name = xstrdup( optarg );
- set_dll_file_name( optarg, spec );
break;
case LONG_OPT_EXE:
set_exec_mode( MODE_EXE );
- if ((p = strrchr( optarg, '/' ))) p++;
- else p = optarg;
- spec->file_name = xmalloc( strlen(p) + 5 );
- strcpy( spec->file_name, p );
- if (!strchr( spec->file_name, '.' )) strcat( spec->file_name, ".exe" );
if (!spec->subsystem) spec->subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
break;
- case LONG_OPT_DEBUG:
- set_exec_mode( MODE_DEBUG );
+ case LONG_OPT_ASCMD:
+ as_command = xstrdup( optarg );
+ break;
+ case LONG_OPT_EXTERNAL_SYMS:
+ link_ext_symbols = 1;
break;
case LONG_OPT_LDCMD:
ld_command = xstrdup( optarg );
case LONG_OPT_NMCMD:
nm_command = xstrdup( optarg );
break;
+ case LONG_OPT_NXCOMPAT:
+ if (optarg[0] == 'n' || optarg[0] == 'N')
+ spec->dll_characteristics &= ~IMAGE_DLLCHARACTERISTICS_NX_COMPAT;
+ break;
case LONG_OPT_RELAY16:
set_exec_mode( MODE_RELAY16 );
break;
case LONG_OPT_RELAY32:
set_exec_mode( MODE_RELAY32 );
break;
+ case LONG_OPT_SAVE_TEMPS:
+ save_temps = 1;
+ break;
case LONG_OPT_SUBSYSTEM:
set_subsystem( optarg, spec );
break;
+ case LONG_OPT_TARGET:
+ set_target( optarg );
+ break;
case LONG_OPT_VERSION:
printf( "winebuild version " PACKAGE_VERSION "\n" );
exit(0);
break;
}
}
+
+ if (spec->file_name && !strchr( spec->file_name, '.' ))
+ strcat( spec->file_name, exec_mode == MODE_EXE ? ".exe" : ".dll" );
+
return &argv[optind];
}
}
}
+/* add input files that look like import libs to the import list */
+static void load_import_libs( char *argv[] )
+{
+ char **ptr, **last;
+
+ for (ptr = last = argv; *ptr; ptr++)
+ {
+ if (strendswith( *ptr, ".def" ))
+ add_import_dll( NULL, *ptr );
+ else
+ *last++ = *ptr; /* not an import dll, keep it in the list */
+ }
+ *last = NULL;
+}
+
static int parse_input_file( DLLSPEC *spec )
{
FILE *input_file = open_input_file( NULL, spec_file_name );
char *extension = strrchr( spec_file_name, '.' );
+ int result;
+ spec->src_name = xstrdup( input_file_name );
if (extension && !strcmp( extension, ".def" ))
- return parse_def_file( input_file, spec );
+ result = parse_def_file( input_file, spec );
else
- return parse_spec_file( input_file, spec );
+ result = parse_spec_file( input_file, spec );
close_input_file( input_file );
+ return result;
}
switch(exec_mode)
{
case MODE_DLL:
- spec->characteristics |= IMAGE_FILE_DLL;
+ if (spec->subsystem != IMAGE_SUBSYSTEM_NATIVE)
+ spec->characteristics |= IMAGE_FILE_DLL;
load_resources( argv, spec );
+ load_import_libs( argv );
+ if (!spec_file_name) fatal_error( "missing .spec file\n" );
if (!parse_input_file( spec )) break;
switch (spec->type)
{
fatal_error( "Win16 specs are not supported in ReactOS version of winebuild\n" );
break;
case SPEC_WIN32:
- read_undef_symbols( argv );
- BuildSpec32File( output_file, spec );
+ read_undef_symbols( spec, argv );
+ BuildSpec32File( spec );
break;
default: assert(0);
}
break;
case MODE_EXE:
if (spec->type == SPEC_WIN16) fatal_error( "Cannot build 16-bit exe files\n" );
+ if (!spec->file_name) fatal_error( "executable must be named via the -F option\n" );
load_resources( argv, spec );
- read_undef_symbols( argv );
- BuildSpec32File( output_file, spec );
+ load_import_libs( argv );
+ if (spec_file_name && !parse_input_file( spec )) break;
+ read_undef_symbols( spec, argv );
+ BuildSpec32File( spec );
break;
case MODE_DEF:
if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] );
if (spec->type == SPEC_WIN16) fatal_error( "Cannot yet build .def file for 16-bit dlls\n" );
+ if (!spec_file_name) fatal_error( "missing .spec file\n" );
if (!parse_input_file( spec )) break;
- BuildDef32File( output_file, spec );
- break;
- case MODE_DEBUG:
- BuildDebugFile( output_file, current_src_dir, argv );
+ BuildDef32File( spec );
break;
case MODE_RELAY16:
fatal_error( "Win16 relays are not supported in ReactOS version of winebuild\n" );
case MODE_PEDLL:
if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] );
if (!parse_input_file( spec )) break;
- BuildPedllFile( output_file, spec );
+ BuildPedllFile( spec );
break;
default:
usage(1);
if (nb_errors) exit(1);
if (output_file_name)
{
- fclose( output_file );
+ if (fclose( output_file ) < 0) fatal_perror( "fclose" );
+ if (output_file_source_name) assemble_file( output_file_source_name, output_file_name );
output_file_name = NULL;
}
return 0;