Repository: mesonbuild/meson

Issue:
Unable to link a static library with MSVC for ARM, when windows resources are included
When creating a library that contains a windows resource (to add metadata about the library), e.g. like this, building with MSVC can fail when targeting non-x86 architectures.
```
mylib_rc_obj = winmod.compile_resources(rc_file)
mylib = library('mylib', mylib_rc_obj, ...)
```
With MSVC tools, the output of `rc.exe` is an arch independent resource file (contrary to windres which by default outputs an arch specific object file). When including this in a static library, MSVC `lib.exe` will convert it into an arch specific object file. (Including it in static libraries is of course pointless, but since one doesn't know if the library is going to be static or shared, it gets included in both.)

`lib.exe` will infer what architecture to use, based on earlier object files processed, or based on the `/machine:` option. Normally the `/machine:` option isn't needed and thus Meson doesn't currently pass it.

However, if the resource file gets passed to `lib.exe` before any of the actual object files, creating the library will fail:

```
$ lib /NOLOGO /OUT:libmylib.a src/src_mylib.rc_mylib.res 'src/src@@mylib@sta/implementation1.c.obj' ...
LINK : warning LNK4068: /MACHINE not specified; defaulting to X86
src\src@@mylib@sta\implementation1.c.obj : fatal error LNK1112: module machine type 'ARM' conflicts with target machine type 'x86'
```

If the `lib.exe` invocation would include a `/machine:` parameter, this wouldn't be an issue. Being able to omit the resource file from the static library altogether (close to what is discussed in #4159) would also solve the situation, as including the resource file in the static library is mostly pointless, at least when the resource is just DLL metadata.


Discussion:
Sounds like we should detect this automatically and ignore the compiled resource file when generating a static library.
Hmm, I'm a little torn about that kind of fix. On one hand, it's sounds straightforward and probably right in most concievable cases. On the other hand, I'm wondering if there are legitimate cases of resources in static libraries - e.g. like if the library runs a GUI defined in the resource. But linking that would probably need `-Wl,---wholearchive` to actually pull in the resources. Not sure if anybody actually does anything like that.
I think adding the correct `/machine:` parameter would be straightforward after #4451, since we'd have access to the target architecture.

See also #4040

Resolving patch:
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -1302,6 +1302,13 @@ def __init__(self, exelist, version, is_cross, exe_wrap, target):
         self.base_options = ['b_pch', 'b_ndebug', 'b_vscrt'] # FIXME add lto, pgo and the like
         self.target = target
         self.is_64 = ('x64' in target) or ('x86_64' in target)
+        # do some canonicalization of target machine
+        if 'x86_64' in target:
+            self.machine = 'x64'
+        elif '86' in target:
+            self.machine = 'x86'
+        else:
+            self.machine = target
 
     # Override CCompiler.get_always_args
     def get_always_args(self):
@@ -1378,7 +1385,7 @@ def get_linker_always_args(self):
         return ['/nologo']
 
     def get_linker_output_args(self, outputname):
-        return ['/OUT:' + outputname]
+        return ['/MACHINE:' + self.machine, '/OUT:' + outputname]
 
     def get_linker_search_args(self, dirname):
         return ['/LIBPATH:' + dirname]
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -1050,7 +1050,7 @@ def detect_static_linker(self, compiler):
                 popen_exceptions[' '.join(linker + [arg])] = e
                 continue
             if '/OUT:' in out.upper() or '/OUT:' in err.upper():
-                return VisualStudioLinker(linker)
+                return VisualStudioLinker(linker, getattr(compiler, 'machine', None))
             if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
                 return ArmarLinker(linker)
             if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -26,8 +26,9 @@ def can_linker_accept_rsp(self):
 class VisualStudioLinker(StaticLinker):
     always_args = ['/NOLOGO']
 
-    def __init__(self, exelist):
+    def __init__(self, exelist, machine):
         self.exelist = exelist
+        self.machine = machine
 
     def get_exelist(self):
         return self.exelist[:]
@@ -39,7 +40,11 @@ def get_buildtype_linker_args(self, buildtype):
         return []
 
     def get_output_args(self, target):
-        return ['/OUT:' + target]
+        args = []
+        if self.machine:
+            args += ['/MACHINE:' + self.machine]
+        args += ['/OUT:' + target]
+        return args
 
     def get_coverage_link_args(self):
         return []


Test patch:
diff --git a/test cases/windows/5 resources/res/dummy.c b/test cases/windows/5 resources/res/dummy.c
new file mode 100644
diff --git a/test cases/windows/5 resources/res/meson.build b/test cases/windows/5 resources/res/meson.build
--- a/test cases/windows/5 resources/res/meson.build	
+++ b/test cases/windows/5 resources/res/meson.build	
@@ -3,3 +3,7 @@ win = import('windows')
 res = win.compile_resources('myres.rc',
   depend_files: 'sample.ico',
   include_directories : inc)
+
+# test that with MSVC tools, LIB/LINK invokes CVTRES with correct /MACHINE
+static_library('reslib', res, 'dummy.c')
+shared_library('shreslib', res, 'dummy.c')