Search code examples
windowscmakemingwzlibmingw-w64

CMake-generated MinGW Makefile has quoting errors


I was trying to build zlib with CMake 3.9.0, output set to MinGW Makefiles, and noticed upon trying to call mingw32-make in the output dir that there was a weird error message which very much looks like a quoting error to me.

D:\zlib-1.2-11> mingw32-make
[  2%] Generating zlib1rc.obj
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
C:\Program Files\mingw-w64\x86_64-7.1.0-win32-seh-rt_v5-rev0\mingw64\bin\windres.exe: preprocessing failed.
CMakeFiles\zlib.dir\build.make:60: recipe for target 'zlib1rc.obj' failed
mingw32-make[2]: *** [zlib1rc.obj] Error 1
CMakeFiles\Makefile2:103: recipe for target 'CMakeFiles/zlib.dir/all' failed
mingw32-make[1]: *** [CMakeFiles/zlib.dir/all] Error 2
Makefile:139: recipe for target 'all' failed
mingw32-make: *** [all] Error 2

What could be the cause of this error and how can I fix it? If it were only zlib, I could scrape the net for pre-built binaries, but this has happened with some other builds, too.


Solution

  • This appears to be a bug in MinGW's version of windres.exe, although I'm also going to heap some blame onto CMake for it's appalling method of invoking windres, which is what is causing this to fail.

    The Problem

    CMake understands that Windows Resource .rc files are a thing, and that they are compiled with the Windows Resource Compiler (aka windres.exe), which it wraps in the default variable CMAKE_RC_COMPILER.

    The problem is, that rather than just invoking windres like a normal person, CMake thinks it's being clever by invoking it like so...

    cmd.exe /C "cd /D C:\Users\username\zlib-1.2.11\build && "C:\Program Files\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev0\mingw64\bin\windres.exe" -D GCC_WINDRES -I C:/Users/username/zlib-1.2.11 -I C:/Users/username/zlib-1.2.11/build -o C:/Users/username/zlib-1.2.11/build/zlib1rc.obj -i C:/Users/username/zlib-1.2.11/win32/zlib1.rc"
    

    Evidently it doesn't understand the notion of the current working directory, or the system path variable (which it used to find windres in the first place). If we were to simplify the command, it would look like this...

    windres -D GCC_WINDRES -I.. -I. -ozlib1rc.obj -i ../win32/zlib1.rc
    

    Those two commands carry the exact same meaning, except the second one actually works.

    The Solution

    We have to step in and stop CMake from trying to be clever.

    cmake .. -DCMAKE_RC_COMPILER=windres
    

    I have MSVC 2017 installed, and CMake assumes that I want to use that by default, despite none of its environment variables being set and it not being in the path (in normal usage, one must invoke the vcvars64.bat file before using MSVC, this behaviour predates CMake). So I have to use -G "MinGW Makefiles", except that I also have sh.exe in my path (because Git), and that just blows CMake's mind, so I need the command...

    cmake .. -G"MSYS Makefiles" -DCMAKE_RC_COMPILER=windres