It is a fairly common practice to compile Windows application on Linux build servers. However, this is usually done through an approach called cross-compiling. The essence of this approach is using a compiler for Windows applications, but the compiler itself is a Linux application. Usually, the compiler used for this is MinGW (or MinGW-w64 these days), a GCC implementation for Windows.
This works great when porting traditional Unix applications to Windows, since it meshes nicely with the traditional build system on Unix-like systems. But it is rather poor for standalone single
.exe applications, which are more common in the Windows world. MinGW has a few DLLs that are needed to run the applications it compiles, and that ruins the single executable experience.
The traditional way to build these simple applications in the Windows world is with the Microsoft compilers, usually in the form of Visual C++. These compilers are fairly nice, but they have one problem: they do not exist as cross compilers. (Well, they can cross compile between different processors, but the compilers themselves will only run on Windows.) What do we do then? Do we resign ourselves into not having single executable applications, or do give up and buy a Windows build machine?
We don’t have to do either. Enter
wine, a compatibility shim that allows Windows applications to run on Linux. We can use it to run many Windows applications on Linux, albeit with potentially reduced functionality.
So I went ahead and copied the Visual C++ compiler to a Linux machine with Wine installed. After some trial and error, I found that the 32-bit compiler binaries work fine on Wine, save one flaw: the linker,
link.exe, cannot generate
.pdb files. So we simply have to sacrifice these files, which is fine for my use on my build server. My only goal here is to automatically compile binaries for every single commit I push to GitHub.
I only tested using the basic Microsoft compiler
cl.exe, and Microsoft version of
nmake.exe. Your mileage may vary if you need to use
msbuild.exe. I suspect it probably work, but I have not tried it.
To set up this environment, first install Wine and
winetricks. (Google for instructions for your system.)
You then run these two commands:
You can of course change
WINEPREFIX to whatever suits you.
You will also need to copy your Visual C++ compilers and your Windows SDK into Wine:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VCon your Windows machine, copy
C:\Program Files (x86)\Windows Kits\10on Windows, copy
C:\Program Files (x86)\Windows Kits\10\Include, pick a directory, and copy its contents into
C:\Program Files (x86)\Windows Kits\10\Lib, pick the same directory as
include, and merge the contents of the
You should adapt the paths based on which version of Visual C++ you are using. You can also rearrange the stuff on the Linux side if you wish to change my build script.
Here’s the bash script that I use to run batch files that build my applications: (save it in
$WINEPREFIX or change the path finding logic)
You can pass the batch files to build your application as command line arguments, or through stdin.
An example use with stdin and a heredoc could be:
Here’s an example of this setup in action on Jenkins, the output for which I have attached just in case it somehow vanishes:
[workspace] $ /bin/sh -xe /tmp/jenkins3918947166186784406.sh + rm -rf dist build + /home/jenkins/wine-vc14/run Z:\home\jenkins\jenkins\jobs\MusicKeyboard\workspace>nmake NOPDB=1 Microsoft (R) Program Maintenance Utility Version 14.00.23506.0 Copyright (C) Microsoft Corporation. All rights reserved. rc /nologo /iinclude /fobuild\Release\keyboard.res keyboard.rc cl /nologo /c /O1 /Iinclude /W4 /DWIN32_LEAN_AND_MEAN /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DUNICODE /D_UNICODE /Fobuild\Release\ /Fdbuild\Release\ src\Keyboard.cpp src\MainWindow.cpp src\PianoControl.cpp src\Window.cpp Keyboard.cpp MainWindow.cpp src\MainWindow.cpp(313): warning C4458: declaration of 'piano' hides class member include\MainWindow.hpp(104): note: see declaration of 'MainWindow::piano' src\MainWindow.cpp(650): warning C4800: 'UINT': forcing value to bool 'true' or 'false' (performance warning) PianoControl.cpp src\PianoControl.cpp(85): warning C4458: declaration of 'octaves' hides class member include\PianoControl.hpp(99): note: see declaration of 'PianoControl::octaves' Window.cpp Generating Code... cl /nologo /c /O1 /Iinclude /W4 /DWIN32_LEAN_AND_MEAN /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DUNICODE /D_UNICODE /Fobuild\Release\ /Fdbuild\Release\ src\midifile.c midifile.c link /nologo /out:dist\Release\Keyboard.exe /subsystem:windows /incremental:no /opt:REF build\Release\Keyboard.obj build\Release\MainWindow.obj build\Release\PianoControl.obj build\Release\Window.obj build\Release\midifile.obj build\Release\keyboard.res
As you can see,
nmake.exe work just as well as they do Windows for basic compiling tasks. Note that this does not mean we can run the actual Visual Studio IDE on Wine, as we are only using the command line compiler component.