Skip to content

Instantly share code, notes, and snippets.

@RobertTheSable
Created March 16, 2023 23:48
Show Gist options
  • Save RobertTheSable/3a4e3ef19d7b69f14ccd7987e828eb2b to your computer and use it in GitHub Desktop.
Save RobertTheSable/3a4e3ef19d7b69f14ccd7987e828eb2b to your computer and use it in GitHub Desktop.
How to Build MSVC ABI-compatible Binaries on Linux with Clang, CMake, and Conan.

Rationale

I write a lot of hobby projects in C++. I work on Linux, but most of the end users of my tools waill be Windows users, so this necessitates setting up methods for producing windows binaries on Linux.

There are tools I've used for this before, including mingw-w64 and MXE. These work, but they have downsides:

  • They aren't binary compatible with MSVC's runtime libraries, so you're forced to bundle a separate runtime somehow.
  • With mingw-w64, you can get stuck in dependency hell as you're usually on the hook for building your dependencies manually.
  • MXE can help with the dependency issue, but it doesn't have scripts for everything. And you're still stuck waiting for your dependencies to build.

Recently I've found a solution that works much better for my needs: Using Conan to install MSVC-compiled dependencies and link against them and the MSVC toolchain with a CMake toolchain.

Disclaimer

This setup works for my needs, which is mainly producing binaries for release.

It probably doesn't cover a lot of other use cases, and I can't promise to help figure those out. Caveat emptor.

This also assumes you have dependencies with pre-built MSVC binaries available from a Conan remote somewhere.

Setting Up

Basic Dependencies

If you haven't already, you'll need to install CMake and Conan.

  • CMake should be available in your system's package manager.
  • Conan can be installed via pip.

MSVC Toolchain

Next, you'll need to setup the MSVC toolchain.

The best way I've found is provided as part of msvc-wine.

  • Follow only the first two steps of installation instructions in the repo. i.e. just these:
    • Run ./vsdownload.py --dest ${path_where_you_want_msvc}.
    • Run ./install.sh ${path_where_you_want_msvc}.

Make note of the VC version and the Windows SDK version that was installed.

  • The VC version will be the name of a folder in ${path_where_you_installed_msvc}/VC/Tools/MSVC/.
  • The Windows SDK version will be the name of a folder in ${path_where_you_installed_msvc}/Windows Kits/10/Include/.

Now your MSVC toolchian is ready to use.

CMake Toolchain

You'll need a CMake toolchain to enable linking against your VC toolchain.

LLVM provides just such a toolchain file.

  • I also use the ClangClCMakeCompileRules file, but it should only be required on Mac based on the comments.
  • I opted to set the required parameters in my copy of the toolchian, which may or may not fit with your workflow:
    set(HOST_ARCH "x86_64")
    set(LLVM_WINSYSROOT "${path_where_you_installed_msvc}")
    set(MSVC_VER "${VC_version_from_msvc_wine}")
    set(WINSDK_VER "${windows_SDK_version_from_msvc_wine}")
    set(LLVM_NATIVE_TOOLCHAIN "/usr")
    

Conan Configuration

I'm assuming your have a ConanFile which uses CMakeToolchain and CMakeDeps, similar to:

from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake, cmake_layout


class YourConanPackage(ConanFile):
    ...
    requires = {your requirements}
    
    def layout(self):    
        cmake_layout(self)

    def generate(self):
        cmake = CMakeDeps(self)
        cmake.generate()
        tc = CMakeToolchain(self)
        tc.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()

In order to get Conan to download MSVC compiled dependencies, we need a profile similar to this:

[settings]
os=Windows
arch=x86_64
compiler=msvc
build_type=Release
compiler.runtime=dynamic
compiler.runtime_type=Release
compiler.version=192
compiler.cppstd=17

You might need to set compiler.runtime to something else.

  • See the supported values here or here.

You can change compiler.cppstd to something else, but a value is required.

You'll also need to add the following CMake settings. I use the [conf] section of my profile, but it could probably also go in your Conanfile.

  • tools.cmake.cmaketoolchain:user_toolchain=\["/path/to/your/WinMsvc.cmake"\]
    • This will import our toolchain alongside Conan's generated toolchain.
  • tools.cmake.cmaketoolchain:generator="Unix Makefiles"
    • Ninja or other generators probably work. The important part is to change it from the default Visual Studio generator, which would otherwise cause Conan to add some variables that causes CMake to fail.
  • tools.microsoft.msbuild:installation_path=
    • This disables Conan from trying to check for an MSVC toolchain.

At this point, you can run: conan install -pr:h ${your-MSVC-profile} ${your_project_folder} And all your dependencies should get downloaded and can be linked against using find_package in CMake.

Now you can run CMake and the remaining steps to build, i.e:

cd build
cmake ../ -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=Release/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release ...(ther cmake options)...
make

Be sure to specify -DCMAKE_BUILD_TYPE=Release, otherwise your program will link against the debug MSVC libraries rather than the release versions.

Assuming everything finished and you didn't find any MSVC incompatibles in your code to fix, you should now a have binary of your program which can be run in Windows (or Wine) with only some version of the MSVC runtime installed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment