Using CMake with Ninja and MSVC on Cygwin's Mintty terminal.

Sorry in advance because this is a little long, but I have been trying to get this to work for about a week now with no luck. I have come pretty far, but there are still a few problems preventing me from finishing this.

I use cygwin on windows purely to act as my dev environment. From cygwin, I primarily use mintty with tmux and vim. I tend to avoid using other utilities from cygwin due to them not working with a lot of native windows utilities. For example, I use a native windows build of cmake (3.18.1) and not the one that is distributed with cygwin.

I have recently begun a new project and one of the first things I want to tackle is getting through all the hard parts involved in setting up the build system. After a lot of trial and error, I can now build my project with cmake and ninja while using mingw as the compiler. This is great, but if I am going to make windows builds that I eventually intend on distributing, I think it’s important that I can do 32bit and 64bit builds with msvc, but being able to do this within my tmux mintty session seems nearly impossible.

My only solution to this has been using the Developer Command Prompt that comes with Visual Studio (2019 Community in my case). This prompt calls C:\"Program Files (x86)"\"Microsoft Visual Studio"\2019\Community\Common7\Tools\VsDevCmd.bat on startup. From this command prompt, I can run the following and everything will build and link.

cmake -G "Ninja" ../../
ninja

If I instead run cmake -G "Ninja" ../../ from one my tmux panes, cmake doesn’t identify cl as the default compiler. It identifies the mingw compiler that I have already confirmed to work for building the project. I found out about cmake toolchain files and decided to create a simple one to specify cl as the compiler to be used. Here what msvc64.toolchain looks like.

set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)

set(compiler "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe")
set(CMAKE_C_COMPILER ${compiler})
set(CMAKE_CXX_COMPILER ${compiler})

This allows cmake to select the correct compiler for building the project, but it fails when compiling the simple cmake test program due to missing library files that need to be linked to build the simple test. I have encountered this problem before when I had a serious urge to simply use cl from the mintty terminal. I ended up creating this file (it’s weird, I know). This allows me to run clc to compile, and cll to link from my terminal using the microsoft compiler. I decided to use a similar solution in my msvc64.toolchain file and this is what it looks like now.

set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)

set(compiler "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe")
set(CMAKE_C_COMPILER ${compiler})
set(CMAKE_CXX_COMPILER ${compiler})

set(prgx86 "C:/Program Files (x86)")
set(winkit_include_root "${prgx86}/Windows Kits/10/Include/10.0.18326.0")
set(inc_a "/I\"${prgx86}/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/include\"")
set(inc_b "/I\"${winkit_include_root}/um\"")
set(inc_c "/I\"${winkit_include_root}/ucrt\"")
set(inc_d "/I\"${winkit_include_root}/shared\"")
set(CMAKE_C_FLAGS "${inc_a} ${inc_b} ${inc_c} ${inc_d}")
set(CMAKE_CXX_FLAGS "${inc_a} ${inc_b} ${inc_c} ${inc_d}")


set(lib_a "/LIBPATH:${prgx86}/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/lib/x64")
set(lib_b "/LIBPATH:${prgx86}/Windows Kits/10/Lib/10.0.18326/um/x64")
set(lib_c "/LIBPATH:${prgx86}/Windows Kits/10/Lib/10.0.18326/ucrt/x64")
set(CMAKE_EXE_LINKER_FLAGS "${lib_a} ${lib_b} ${lib_c}")

This definitely feels closer to something that is actually functional, but it is still not working. It’s actually a bit further away from functional. When I run cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="../msvc64.toolchain" ../../, the simple test program actually fails to compile. When I looked inside CMakeError.log, I found this monstrosity.

Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" failed.
Compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe 
Build flags: /I/"C:/Program;Files;(x86)/Microsoft;Visual;Studio/2019/Community/VC/Tools/MSVC/14.27.29110/include";/I/"C:/Program;Files;(x86)/Windows;Kits/10/Include/10.0.18326.0/um";/I/"C:/Program;Files;(x86)/Windows;Kits/10/Include/10.0.18326.0/ucrt";/I/"C:/Program;Files;(x86)/Windows;Kits/10/Include/10.0.18326.0/shared"
Id flags:  

The output was:
2
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

cl : Command line warning D9024 : unrecognized source file type 'Files', object file assumed
cl : Command line warning D9024 : unrecognized source file type '(x86)/Microsoft', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Visual', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Studio/2019/Community/VC/Tools/MSVC/14.27.29110/include"', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Files', object file assumed
cl : Command line warning D9024 : unrecognized source file type '(x86)/Windows', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Kits/10/Include/10.0.18326.0/um"', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Files', object file assumed
cl : Command line warning D9024 : unrecognized source file type '(x86)/Windows', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Kits/10/Include/10.0.18326.0/ucrt"', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Files', object file assumed
cl : Command line warning D9024 : unrecognized source file type '(x86)/Windows', object file assumed
cl : Command line warning D9024 : unrecognized source file type 'Kits/10/Include/10.0.18326.0/shared"', object file assumed
CMakeCXXCompilerId.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Files.exe 
Files 
(x86)/Microsoft 
Visual 
Studio/2019/Community/VC/Tools/MSVC/14.27.29110/include\ 
Files 
(x86)/Windows 
Kits/10/Include/10.0.18326.0/um\ 
Files 
(x86)/Windows 
Kits/10/Include/10.0.18326.0/ucrt\ 
Files 
(x86)/Windows 
Kits/10/Include/10.0.18326.0/shared\ 
CMakeCXXCompilerId.obj 
LINK : fatal error LNK1181: cannot open input file 'Files.obj'


Detecting C compiler ABI info failed to compile with the following output:
Change Dir: C:/Users/underdisc/home/tech/varkor/build/msvc64/CMakeFiles/CMakeTmp

It seems pretty obvious that cmake is interpretting every space as a delimeter for different flags to give to the compiler when it should just be a single flag. I have tried escaping these spaces with backslashes and the results are the same. I have also tried this C:/Program\\ Files\\ (x86)... because someone suggested it here. Is there a way to pass these flags to the compiler as intended?

This isn’t just about passing the compiler flags correctly though. I noticed that the CMakeCXXCompilerId.cpp file doesn’t actually have a single include in it. If I get rid of set(CMAKE_CXX_FLAGS...) and set(CMAKE_C_FLAGS...) The compiling step then succeeds but then the link step still fails. Here is the most important part of CMakeError.log after removing those sets.

Compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe 
Build flags: 
Id flags:  

The output was:
2
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

CMakeCXXCompilerId.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:CMakeCXXCompilerId.exe 
CMakeCXXCompilerId.obj 
LINK : fatal error LNK1104: cannot open file 'LIBCMT.lib'

This is odd to me because I know and verified exactly where that file is. It’s here C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/lib/x64 and I have included that directory as a library path for link.exe to search through. The only difference is that the name of the file is all lowercase. I know that this should work though. The bashrc file I linked earlier does work when compiling simple hello world programs, so something is going wrong when these additional arguments from CMAKE_EXE_LINKER_FLAGS are passed to link.exe, but I have no idea what. Does anyone see the problem here?

Furthermore, is there anything else I should be aware of as I continue attempting to get this working? I do feel like I am very close, but after hitting and jumping over so many blockades, I am not sure. The real goal here is to do the entire build process for my project from the mintty terminal I use and not the developer command prompt that ships with visual studio.

|Small Notes|
I am aware of the vcvars[32|64].bat files. I have already attempted to use them in the past and again now, but it has never worked and I am pretty certain it is from a conflict with my cygwin setup. I don’t know specifically why though.

My next step is diving into the cmake source, but I figure somebody here already knows their way around it and could point me in the right direction if necessary.

Thanks in advance for any help and/or ideas

The MSVC toolchain is finicky and weird. vcvarsall.bat (and friends) are really the only reliable way of using them. Usually other tools end up extracting what they need to do from it rather than hard-coding them. I really recommend using vcvarsall.bat as the basis for setting up your environment in tmux rather than trying to hard-code anything. If that involves some kind of grep to extract out and do simple parsing, that might be what is required.

Can you point me to an example that uses a .bat file or command line that allows one to specify the CMAKE_CXX_FLAGS directly?

Each variant I have tried has not seemed to work.

I want to specify something like this with cuda nvcc

-DCMAKE_CXX_COMPILER="C:/Users/kurt.sansom/tools/cuda_11.1.1_456.81_win10/cuda_nvcc/nvcc/bin/nvcc.exe" -DCMAKE_CXX_FLAGS="-ccbin \"C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.25.28610/bin/Hostx64/x64\" -L \"C:/Users/kurt.sansom/tools/cuda_11.1.1_456.81_win10/cuda_cudart/cudart/lib/x64\" -I \"C:/Users/kurt.sansom/tools/cuda_11.1.1_456.81_win10/cuda_cudart/cudart/include\""

the build flags in the CMakeError.log are

Build flags: -ccbin;"C:/Program;Files;(x86)/Microsoft;Visual;Studio/2019/BuildTools/VC/Tools/MSVC/14.25.28610/bin/Hostx64/x64";-L;"C:/Users/kurt.sansom/tools/cuda_11.1.1_456.81_win10/cuda_cudart/cudart/lib/x64";-I;"C:/Users/kurt.sansom/tools/cuda_11.1.1_456.81_win10/cuda_cudart/cudart/include"
Id flags:  

The output was:
1
Failed to run "C:/Program (Access is denied.

).
nvcc fatal   : Failed to preprocess host compiler properties.

This is exactly what I ended up doing. The main reason I attempted to do this was because I wanted to be able to perform a build within one of the tmux panes. I usually like just having one whole pane for building. I was happy to find out that you can just run cmd from mintty and the normal command prompt can be used inside the pane. So yeah, vcvars* all the way.

All I can say is that cmd.exe quoting is weird. I can never remember the rules, so I just play with it until I get what I need out of it. In this instance, using a -C script with a set of set(var "value" CACHE TYPE "doc") commands may just be a lot easier to handle with the nested quoting you’re looking to do.

@underdisc can you provide or point to an example of what you did?

Thank you @ben.boeckel. BLARG!