From 0dccfbcec9818047c7fec959005453d022905835 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Sun, 18 Jan 2015 05:42:10 +0100 Subject: ntoskrnl.exe/tests: Add initial driver testing framework and corresponding changes to Makefile system. (rev 2) --- aclocal.m4 | 31 +++ configure.ac | 2 + dlls/ntoskrnl.exe/tests/Makefile.in | 8 + dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in | 11 + dlls/ntoskrnl.exe/tests/driver.sys/driver.c | 134 ++++++++++++ dlls/ntoskrnl.exe/tests/driver.sys/driver.h | 28 +++ dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec | 1 + dlls/ntoskrnl.exe/tests/ntoskrnl.c | 226 +++++++++++++++++++++ tools/make_makefiles | 9 +- tools/makedep.c | 165 ++++++++++++++- 10 files changed, 612 insertions(+), 3 deletions(-) create mode 100644 dlls/ntoskrnl.exe/tests/Makefile.in create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/driver.c create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/driver.h create mode 100644 dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec create mode 100644 dlls/ntoskrnl.exe/tests/ntoskrnl.c diff --git a/aclocal.m4 b/aclocal.m4 index f4d7424..9d8296a 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -547,6 +547,28 @@ $ac_dir/crosstest: __builddeps__ dummy fi } +wine_fn_config_resource () +{ + ac_dir=$[1] + ac_name=$[2] + ac_flags=$[3] + ac_dll=$ac_name + + case $ac_name in + *.*) ;; + *) ac_dll=$ac_dll.dll ;; + esac + + ac_clean= + test -n "$CROSSTARGET" && ac_clean=`expr $ac_dir/$ac_dll : "\\(.*\\)\\."`_crossres.`expr $ac_dll : ".*\\.\\(.*\\)"` + test -n "$DLLEXT" || ac_clean="$ac_dir/$ac_dll" + + AS_VAR_IF([enable_tests],[no],[wine_fn_disabled_rules $ac_clean; return]) + + wine_fn_append_file SUBDIRS $ac_dir + wine_fn_clean_rules $ac_clean +} + wine_fn_config_tool () { ac_dir=$[1] @@ -650,6 +672,15 @@ wine_fn_config_test $1 ac_name[]ac_suffix [$2]dnl m4_popdef([ac_suffix])dnl m4_popdef([ac_name])]) +dnl **** Create a test resource makefile from config.status **** +dnl +dnl Usage: WINE_CONFIG_RESOURCE(dir,flags) +dnl +AC_DEFUN([WINE_CONFIG_RESOURCE],[AC_REQUIRE([WINE_CONFIG_HELPERS])dnl +m4_pushdef([ac_name],[m4_bpatsubst([$1],[.*/\([^/\]*\)$],[\1])])dnl +wine_fn_config_resource $1 ac_name [$2]dnl +m4_popdef([ac_name])]) + dnl **** Create a static lib makefile from config.status **** dnl dnl Usage: WINE_CONFIG_LIB(name,flags) diff --git a/configure.ac b/configure.ac index bb390e2..d766249 100644 --- a/configure.ac +++ b/configure.ac @@ -3216,6 +3216,8 @@ WINE_CONFIG_TEST(dlls/ntdll/tests) WINE_CONFIG_DLL(ntdsapi,,[implib]) WINE_CONFIG_TEST(dlls/ntdsapi/tests) WINE_CONFIG_DLL(ntoskrnl.exe,,[implib],[ntoskrnl]) +WINE_CONFIG_TEST(dlls/ntoskrnl.exe/tests) +WINE_CONFIG_RESOURCE(dlls/ntoskrnl.exe/tests/driver.sys) WINE_CONFIG_DLL(ntprint) WINE_CONFIG_TEST(dlls/ntprint/tests) WINE_CONFIG_DLL(objsel,,[clean]) diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in new file mode 100644 index 0000000..f0c2460 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/Makefile.in @@ -0,0 +1,8 @@ +TESTDLL = ntoskrnl.exe +IMPORTS = advapi32 + +C_SRCS = \ + ntoskrnl.c + +RC_DLLS = \ + driver.sys diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in b/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in new file mode 100644 index 0000000..bc040e4 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver.sys/Makefile.in @@ -0,0 +1,11 @@ +RESOURCE = driver.sys +IMPORTS = ntoskrnl.exe + +WINEFLAGS = -Wb,--subsystem,native +CROSSFLAGS = -nostartfiles -nostdlib -nodefaultlibs \ + -Wl,--subsystem,native \ + -Wl,--image-base,0x10000 \ + -Wl,-entry,_DriverEntry@8 + +C_SRCS = \ + driver.c diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.c b/dlls/ntoskrnl.exe/tests/driver.sys/driver.c new file mode 100644 index 0000000..35f78d1 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.c @@ -0,0 +1,134 @@ +/* + * ntoskrnl.exe testing framework + * + * Copyright 2015 Sebastian Lackner + * Copyright 2015 Michael Müller + * Copyright 2015 Christian Costa + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" +#include "ddk/wdm.h" + +#include "driver.h" + +const WCHAR driver_device[] = {'\\','D','e','v','i','c','e', + '\\','W','i','n','e','T','e','s','t','D','r','i','v','e','r',0}; +const WCHAR driver_link[] = {'\\','D','o','s','D','e','v','i','c','e','s', + '\\','W','i','n','e','T','e','s','t','D','r','i','v','e','r',0}; + + +static NTSTATUS test_basic_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info) +{ + const char str[] = "Wine is not an emulator"; + ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength; + char *buffer = irp->AssociatedIrp.SystemBuffer; + int i; + + if (!buffer) + return STATUS_ACCESS_VIOLATION; + + if (length < sizeof(str)-1) + return STATUS_BUFFER_TOO_SMALL; + + for (i = 0; i < sizeof(str)-1; i++) + buffer[i] = str[i]; + + *info = sizeof(str)-1; + return STATUS_SUCCESS; +} + + +static NTSTATUS WINAPI driver_Create(DEVICE_OBJECT *device, IRP *irp) +{ + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + NTSTATUS status = STATUS_NOT_SUPPORTED; + ULONG_PTR information = 0; + + switch (stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_WINETEST_BASIC_IOCTL: + status = test_basic_ioctl(irp, stack, &information); + break; + + default: + break; + } + + irp->IoStatus.Status = status; + irp->IoStatus.Information = information; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return status; +} + +static NTSTATUS WINAPI driver_Close(DEVICE_OBJECT *device, IRP *irp) +{ + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; +} + +static VOID WINAPI driver_Unload(DRIVER_OBJECT *driver) +{ + UNICODE_STRING linkW; + + DbgPrint("unloading driver\n"); + + RtlInitUnicodeString(&linkW, driver_link); + IoDeleteSymbolicLink(&linkW); + + IoDeleteDevice(driver->DeviceObject); +} + +NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, PUNICODE_STRING registry) +{ + UNICODE_STRING nameW, linkW; + DEVICE_OBJECT *device; + NTSTATUS status; + + DbgPrint("loading driver\n"); + + /* Allow unloading of the driver */ + driver->DriverUnload = driver_Unload; + + /* Set driver functions */ + driver->MajorFunction[IRP_MJ_CREATE] = driver_Create; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_IoControl; + driver->MajorFunction[IRP_MJ_CLOSE] = driver_Close; + + RtlInitUnicodeString(&nameW, driver_device); + RtlInitUnicodeString(&linkW, driver_link); + + if (!(status = IoCreateDevice(driver, 0, &nameW, FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, FALSE, &device))) + status = IoCreateSymbolicLink(&linkW, &nameW); + + return status; +} diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.h b/dlls/ntoskrnl.exe/tests/driver.sys/driver.h new file mode 100644 index 0000000..372e908 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.h @@ -0,0 +1,28 @@ +/* + * ntoskrnl.exe testing framework + * + * Copyright 2015 Sebastian Lackner + * Copyright 2015 Michael Müller + * Copyright 2015 Christian Costa + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +/* + * All custom IOCTLs need to have a function value >= 0x800. + */ + +#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec b/dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec new file mode 100644 index 0000000..76421d7 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/driver.sys/driver.sys.spec @@ -0,0 +1 @@ +# nothing to export diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c new file mode 100644 index 0000000..9b8a6a7 --- /dev/null +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -0,0 +1,226 @@ +/* + * ntoskrnl.exe testing framework + * + * Copyright 2015 Sebastian Lackner + * Copyright 2015 Michael Müller + * Copyright 2015 Christian Costa + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windows.h" +#include "winsvc.h" +#include "winioctl.h" +#include "wine/test.h" + +#include "driver.sys/driver.h" + +static const char driver_name[] = "WineTestDriver"; +static const char device_path[] = "\\\\.\\WineTestDriver"; + +/* extracts a driver from a resource to a filename */ +static BOOL extract_resource(const char *name, const char *filename) +{ + DWORD size, written = 0; + HGLOBAL reshandle; + HRSRC resinfo; + HANDLE file; + char *data; + + resinfo = FindResourceA(NULL, name, (LPCSTR)RT_RCDATA); + if (!resinfo) + return FALSE; + + reshandle = LoadResource(NULL, resinfo); + if (!reshandle) + return FALSE; + + data = LockResource(reshandle); + if (!data) + return FALSE; + + size = SizeofResource(NULL, resinfo); + if (!size) + return FALSE; + + file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return FALSE; + + while (size) + { + if (!WriteFile(file, data, size, &written, NULL)) + { + CloseHandle(file); + return FALSE; + } + + data += written; + size -= written; + } + + CloseHandle(file); + return TRUE; +} + +static void wait_driver(SC_HANDLE service, SERVICE_STATUS *status) +{ + for (;;) + { + Sleep(100); + + if (!QueryServiceStatus(service, status)) + { + ok(0, "QueryServiceStatus failed with %x\n", GetLastError()); + status->dwCurrentState = SERVICE_STOPPED; + return; + } + + if (status->dwCurrentState != SERVICE_STOP_PENDING && + status->dwCurrentState != SERVICE_START_PENDING) + return; + } +} + +/* unload a driver and delete the temporary file */ +static void unload_driver(SC_HANDLE service, const char *filename) +{ + SERVICE_STATUS status; + + if (service) + { + /* Wine specific hack - when the test is the first application + * in a wineprefix, then the driver will need some time to start up, + * before we can stop them again. */ + wait_driver(service, &status); + + ControlService(service, SERVICE_CONTROL_STOP, &status); + wait_driver(service, &status); + ok(status.dwCurrentState == SERVICE_STOPPED, + "expected SERVICE_STOPPED, got %d\n", status.dwCurrentState); + + DeleteService(service); + CloseServiceHandle(service); + } + if (filename) + { + ok(DeleteFileA(filename), + "DeleteFileA failed with %x\n", GetLastError()); + } +} + +/* load a driver and return the service handle */ +static SC_HANDLE load_driver(char *filename) +{ + SC_HANDLE manager, service; + SERVICE_STATUS status; + char temp[MAX_PATH]; + + manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (!manager) + return FALSE; + + /* before we start with the actual tests, make sure to terminate + * any old wine test drivers. */ + service = OpenServiceA(manager, driver_name, SERVICE_ALL_ACCESS); + if (service) unload_driver(service, NULL); + + /* extract the new driver which is embedded into a resource */ + GetTempPathA(sizeof(temp), temp); + GetTempFileNameA(temp, "drv", 0, filename); + + if (!extract_resource("driver.sys", filename)) + { + ok(0, "Failed to extract driver resource\n"); + goto err; + } + + trace("Trying to load driver %s\n", filename); + + /* load the new driver */ + service = CreateServiceA(manager, driver_name, driver_name, + SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, + SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + filename, NULL, NULL, NULL, NULL, NULL); + if (!service) + { + ok(0, "CreateServiceA failed with %x\n", GetLastError()); + goto err; + } + + ok(StartServiceA(service, 0, NULL), + "StartServiceA failed with %x\n", GetLastError()); + + CloseServiceHandle(manager); + + /* wait for the service to start up properly */ + wait_driver(service, &status); + ok(status.dwCurrentState == SERVICE_RUNNING, + "expected SERVICE_RUNNING, got %d\n", status.dwCurrentState); + + if (status.dwCurrentState != SERVICE_RUNNING) + { + unload_driver(service, filename); + return NULL; + } + + return service; + +err: + CloseServiceHandle(manager); + unload_driver(NULL, filename); + return NULL; +} + +static void test_basic_ioctl(void) +{ + const char str[] = "Wine is not an emulator"; + DWORD bytes_returned; + char buf[32]; + HANDLE file; + BOOL res; + + file = CreateFileA(device_path, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (file == INVALID_HANDLE_VALUE) + { + ok(0, "Connecting to driver failed with %x\n", GetLastError()); + return; + } + + res = DeviceIoControl(file, IOCTL_WINETEST_BASIC_IOCTL, NULL, 0, buf, + sizeof(buf), &bytes_returned, NULL); + ok(res, "DeviceIoControl failed with %x\n", GetLastError()); + ok(bytes_returned == sizeof(str)-1, "Unexpected number of bytes\n"); + ok(!memcmp(buf, str, sizeof(str)-1), "Unexpected response data\n"); + + CloseHandle(file); +} + +START_TEST(ntoskrnl) +{ + char filename[MAX_PATH]; + SC_HANDLE service; + + if (!(service = load_driver(filename))) + { + win_skip("Failed to load driver, skipping tests.\n"); + return; + } + + test_basic_ioctl(); + + unload_driver(service, filename); +} diff --git a/tools/make_makefiles b/tools/make_makefiles index ce38c53..d60a3fd 100755 --- a/tools/make_makefiles +++ b/tools/make_makefiles @@ -204,7 +204,7 @@ sub parse_makefile($) { die "Configure substitution is not allowed in $file" unless $file eq "Makefile"; } - if (/^\s*(MODULE|IMPORTLIB|TESTDLL|PARENTSRC|APPMODE)\s*=\s*(.*)/) + if (/^\s*(MODULE|IMPORTLIB|TESTDLL|RESOURCE|PARENTSRC|APPMODE)\s*=\s*(.*)/) { my $var = $1; $make{$var} = $2; @@ -440,6 +440,13 @@ sub update_makefiles(@) (my $dir = $file) =~ s/^(.*)\/Makefile/$1/; push @lines, "WINE_CONFIG_TEST($dir$flag_args)\n"; } + elsif (defined($make{"RESOURCE"})) # test resource + { + die "MODULE should not be defined in $file" if defined $make{"MODULE"}; + die "STATICLIB should not be defined in $file" if defined $make{"STATICLIB"}; + (my $dir = $file) =~ s/^(.*)\/Makefile/$1/; + push @lines, "WINE_CONFIG_RESOURCE($dir$flag_args)\n"; + } elsif (defined($make{"MODULE"}) && $make{"MODULE"} =~ /\.a$/) # import lib { die "MODULE should not be defined as static lib in $file" unless $file =~ /^dlls\//; diff --git a/tools/makedep.c b/tools/makedep.c index 12f5395..a7723a0 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -175,4 +175,5 @@ struct makefile const char *module; const char *testdll; + const char *resource; const char *sharedlib; const char *staticlib; @@ -480,6 +481,30 @@ static char *get_extension( char *filename ) /******************************************************************* + * prepend_extension + */ +static char *prepend_extension( const char *name, const char *prepend, const char *new_ext ) +{ + int name_len, prepend_len = strlen(prepend); + const char *ext; + char *ret; + + ext = strrchr( name, '.' ); + if (ext && strchr(ext, '/')) ext = NULL; + name_len = ext ? (ext - name) : strlen( name ); + + if (!ext) ext = new_ext; + if (!ext) ext = ""; + + ret = xmalloc( name_len + prepend_len + strlen(ext) + 1 ); + memcpy( ret, name, name_len ); + memcpy( ret + name_len, prepend, prepend_len ); + strcpy( ret + name_len + prepend_len, ext ); + return ret; +} + + +/******************************************************************* * replace_extension */ static char *replace_extension( const char *name, const char *old_ext, const char *new_ext ) @@ -2277,6 +2302,7 @@ static struct strarray output_sources( const struct makefile *make ) struct strarray includes = empty_strarray; struct strarray phony_targets = empty_strarray; struct strarray all_targets = empty_strarray; + struct strarray resource_dlls = get_expanded_make_var_array( make, "RC_DLLS" ); struct strarray install_rules[NB_INSTALL_RULES]; char *ldrpath_local = get_expanded_make_variable( make, "LDRPATH_LOCAL" ); char *ldrpath_install = get_expanded_make_variable( make, "LDRPATH_INSTALL" ); @@ -2559,7 +2585,7 @@ static struct strarray output_sources( const struct makefile *make ) } else { - int need_cross = make->testdll || + int need_cross = make->testdll || make->resource || (source->file->flags & FLAG_C_IMPLIB) || (make->module && make->staticlib); @@ -2573,7 +2599,7 @@ static struct strarray output_sources( const struct makefile *make ) output_filenames( includes ); output_filenames( make->define_args ); output_filenames( extradefs ); - if (make->module || make->staticlib || make->sharedlib || make->testdll) + if (make->module || make->staticlib || make->sharedlib || make->testdll || make->resource) { output_filenames( dll_flags ); if (make->use_msvcrt) output_filenames( msvcrt_flags ); @@ -2625,6 +2651,65 @@ static struct strarray output_sources( const struct makefile *make ) output( "\n" ); } + /* rules for resource dlls, call Makefile in subdirectory */ + + if (resource_dlls.count) + { + for (i = 0; i < resource_dlls.count; i++) + { + output( "%s/%s%s: %s/Makefile\n", resource_dlls.str[i], + resource_dlls.str[i], dll_ext, resource_dlls.str[i] ); + output( "\t@cd %s && $(MAKE) %s%s\n", resource_dlls.str[i], + resource_dlls.str[i], dll_ext ); + } + + output( "%s:", obj_dir_path( make, "resource_dlls.o" )); + for (i = 0; i < resource_dlls.count; i++) + output( " %s/%s%s", resource_dlls.str[i], resource_dlls.str[i], dll_ext ); + output( "\n" ); + output( "\t(" ); + for (i = 0; i < resource_dlls.count; i++) + { + if (i != 0) output( "; \\\n " ); + output( "echo \"%s RCDATA %s/%s%s\"", resource_dlls.str[i], + resource_dlls.str[i], resource_dlls.str[i], dll_ext ); + } + output( ") | %s -o $@\n", tools_path( make, "wrc" ) ); + strarray_add( &clean_files, "resource_dlls.o" ); + strarray_add( &object_files, "resource_dlls.o" ); + + if (crosstarget && (make->testdll || (make->module && make->staticlib))) + { + for (i = 0; i < resource_dlls.count; i++) + { + char *name = prepend_extension( resource_dlls.str[i], "_crossres", ".dll" ); + output( "%s/%s: %s/Makefile\n", resource_dlls.str[i], name, resource_dlls.str[i] ); + output( "\t@cd %s && $(MAKE) %s\n", resource_dlls.str[i], name ); + free( name ); + } + + output( "%s:", obj_dir_path( make, "resource_dlls.cross.o" )); + for (i = 0; i < resource_dlls.count; i++) + { + char *name = prepend_extension( resource_dlls.str[i], "_crossres", ".dll" ); + output( " %s/%s", resource_dlls.str[i], name ); + free( name ); + } + output( "\n" ); + output( "\t(" ); + for (i = 0; i < resource_dlls.count; i++) + { + char *name = prepend_extension( resource_dlls.str[i], "_crossres", ".dll" ); + if (i != 0) output( "; \\\n " ); + output( "echo \"%s RCDATA %s/%s\"", resource_dlls.str[i], resource_dlls.str[i], name ); + free( name ); + } + output( ") | %s -o $@\n", tools_path( make, "wrc" ) ); + strarray_add( &clean_files, "resource_dlls.cross.o" ); + strarray_add( &crossobj_files, "resource_dlls.cross.o" ); + } + } + if (make->module && !make->staticlib) { struct strarray all_libs = empty_strarray; @@ -3024,6 +3109,81 @@ static struct strarray output_sources( const struct makefile *make ) add_install_rule( make, install_rules, make->scripts.str[i], make->scripts.str[i], strmake( "S$(bindir)/%s", make->scripts.str[i] )); + if (make->resource) + { + char *extra_wine_flags = get_expanded_make_variable( make, "WINEFLAGS" ); + char *extra_cross_flags = get_expanded_make_variable( make, "CROSSFLAGS" ); + struct strarray all_libs = empty_strarray; + char *module_path = obj_dir_path( make, make->resource ); + char *spec_file = NULL; + + if (!make->appmode.count) + spec_file = src_dir_path( make, replace_extension( make->resource, ".dll", ".spec" )); + for (i = 0; i < make->imports.count; i++) + strarray_add( &all_libs, strmake( "-l%s", make->imports.str[i] )); + strarray_addall( &all_libs, get_expanded_make_var_array( make, "LIBS" )); + + if (*dll_ext) + { + strarray_add( &all_targets, strmake( "%s%s", make->resource, dll_ext )); + strarray_add( &all_targets, strmake( "%s.fake", make->resource )); + output( "%s%s %s.fake:", module_path, dll_ext, module_path ); + } + else + { + strarray_add( &all_targets, make->resource ); + output( "%s:", module_path ); + } + if (spec_file) output_filename( spec_file ); + output_filenames_obj_dir( make, object_files ); + output_filenames_obj_dir( make, res_files ); + output( "\n" ); + output( "\t%s -o $@", tools_path( make, "winegcc" )); + output_filename( strmake( "-B%s", tools_dir_path( make, "winebuild" ))); + if (tools_dir) output_filename( strmake( "--sysroot=%s", top_obj_dir_path( make, "" ))); + output_filenames( target_flags ); + output_filenames( unwind_flags ); + if (spec_file) + { + output( " -shared %s", spec_file ); + output_filenames( make->extradllflags ); + } + else output_filenames( make->appmode ); + if (extra_wine_flags) output( " %s", extra_wine_flags ); + output_filenames_obj_dir( make, object_files ); + output_filenames_obj_dir( make, res_files ); + output_filenames( all_libs ); + output_filename( "$(LDFLAGS)" ); + output( "\n" ); + + if (crosstarget) + { + char *crossres = prepend_extension( make->resource, "_crossres", ".dll" ); + + strarray_add( &clean_files, crossres ); + output( "%s:", obj_dir_path( make, crossres )); + output_filenames_obj_dir( make, crossobj_files ); + output_filenames_obj_dir( make, res_files ); + output( "\n" ); + output( "\t%s -o $@ -b %s", tools_path( make, "winegcc" ), crosstarget ); + output_filename( strmake( "-B%s", tools_dir_path( make, "winebuild" ))); + if (tools_dir) output_filename( strmake( "--sysroot=%s", top_obj_dir_path( make, "" ))); + output_filename( "--lib-suffix=.cross.a" ); + if (spec_file) + { + output( " -shared %s", spec_file ); + output_filenames( make->extradllflags ); + } + else output_filenames( make->appmode ); + if (extra_cross_flags) output( " %s", extra_cross_flags ); + output_filenames_obj_dir( make, crossobj_files ); + output_filenames_obj_dir( make, res_files ); + output_filenames( all_libs ); + output_filename( "$(LDFLAGS)" ); + output( "\n" ); + } + } + if (!make->disabled) { if (all_targets.count) @@ -3381,4 +3541,5 @@ static void load_sources( struct makefile *make ) make->module = get_expanded_make_variable( make, "MODULE" ); make->testdll = get_expanded_make_variable( make, "TESTDLL" ); + make->resource = get_expanded_make_variable( make, "RESOURCE" ); make->sharedlib = get_expanded_make_variable( make, "SHAREDLIB" ); make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); -- 2.7.1