CI with MSVC is unnecessarily difficult. Can't we just use Docker?
It turns out we can-- by running MSVC in Wine. Lots of folks have tried to do this over the years [1], but the setup is involved and fiddly. But scripting complicated setups is what Docker was made for!
The big blocker to getting MSVC in Wine is that even though the software itself works under Wine, the installers don't. We dodge that problem by using Vagrant to drive the MSVC installer in a real Windows OS within VirtualBox, export a snapshot of the installation, and then Docker copies the snapshot into Wine.
- Docker
- If on Linux, allow Docker to be used without sudo
- VirtualBox
- Vagrant
To create an msvc:15
Docker image:
make clean
make msvc15
MSVC 9, 10, 11, 12, 14, 15, and 16 are supported.
Note: The snapshot step can take quite some time, as the MSVC installers are notoriously gigantic and slow.
Let's simplify our Docker command:
function vcwine() { docker run -v$HOME:/host/$HOME -w/host/$PWD -u $(id -u):$(id -g) -eMSVCARCH=$MSVCARCH --rm -t -i msvc:15 "$@"; }
The Docker images are setup to run (nearly) everything through Wine. So for example, we can do DOS things like dir
:
✗ vcwine cmd /c dir
Volume in drive Z has no label.
Volume Serial Number is 0000-0000
Directory of Z:\host\Users\paleozogt\Development\test\MSVCDocker
8/31/2018 9:08 PM <DIR> .
8/31/2018 9:21 PM <DIR> ..
8/31/2018 8:55 PM <DIR> build
8/31/2018 9:07 PM 3,421 Dockerfile
8/31/2018 9:07 PM <DIR> dockertools
8/31/2018 9:07 PM 464 Makefile
8/31/2018 9:08 PM 45 README.md
8/31/2018 9:07 PM <DIR> test
8/31/2018 9:07 PM 2,654 Vagrantfile
8/31/2018 9:07 PM <DIR> vagranttools
4 files 6,584 bytes
6 directories 97,359,118,336 bytes free
Compiling a Hello World:
✗ vcwine cl test/helloworld.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.15.26726 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
helloworld.cpp
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Tools\MSVC\14.15.26726\include\xlocale(319): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
Microsoft (R) Incremental Linker Version 14.15.26726.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:helloworld.exe
helloworld.obj
✗ vcwine helloworld.exe
hello world from win x86_64 msvc v1915
Even though its 2018, maybe we want to build for 32-bit:
✗ MSVCARCH=32 vcwine cl test/helloworld.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
helloworld.cpp
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\xlocale(337) : warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
Microsoft (R) Incremental Linker Version 12.00.31101.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:helloworld.exe
helloworld.obj
✗ vcwine helloworld.exe
hello world from win x86 msvc v1915
Clang can cross-compile MSVC-compatible binaries with clang-cl. A linux version is included (ie, it doesn't use Wine), but it still needs the MSVC installation for headers/libs, and Wine is still useful for running the resulting binary.
Compiling a Hello World:
✗ vcwine clang-cl test/helloworld.cpp
✗ vcwine helloworld.exe
hello world from win x86_64 clang v7
Even though its 2018, maybe we want to build for 32-bit:
✗ MSVCARCH=32 vcwine clang-cl test/helloworld.cpp
✗ vcwine helloworld.exe
hello world from win x86 clang v7
For more examples, including the use of CMake and high-level language bindings, see the examples subfolder.
-
MSBuild doesn't work, so we can't do things like
vcwine cmake ../../test -G "Visual Studio 15 2017 Win64" vcwine msbuild
If you're using CMake, use the "NMake Makefiles", "NMake Makefiles JOM", or "Ninja" generators.
-
When using LLVM's clang-cl, paths that begin with
/U
(such as/Users/
) will cause strange errors:clang-7: warning: '/Users/paleozogt/Development/test/MSVCDocker/build/test/CMakeFiles/CMakeTmp/testCCompiler.c' treated as the '/U' option [-Wslash-u-filename] clang-7: note: Use '--' to treat subsequent arguments as filenames clang-7: error: no input files
It appears that
/Users/...
is getting mistaken for a cl flag/U
.