Cross-platform without complexity: remote Linux

Spread the love

Previously, we showed how to enable the Visual Studio IDE to work with an ostensibly cross-platform CMake project. Today we will prove that the project indeed can work on a non-Windows platform — at least as much as is possible when using the Windows Subsystem for Linux (WSL).

Let’s start by observing that Visual Studio has direct support for WSL. While I do want to use WSL for this demo, I actually don’t want VS to specifically target WSL. Instead, let’s use the generic remote Linux functionality instead. After appropriately configuring my WSL instance using simple password auth (for use in local scenarios only, of course!), we can add a remote connection for the WSL instance (e.g. using ::1 as the IPv6 localhost name and whichever port was chosen for SSH).

Now we just need to instruct VS to create CMake configurations for Linux-Clang-Debug and Linux-Clang-Release (GCC is also supported, but Clang is somewhat better integrated with VS). The CMakeSettings.json file now looks like this:

{
  "configurations": [
    {
      "name": "x64-Debug",
      "generator": "Ninja",
      "configurationType": "Debug",
      "inheritEnvironments": [ "msvc_x64_x64" ],
      "buildRoot": "${projectDir}\\out\\build\\${name}",
      "installRoot": "${projectDir}\\out\\install\\${name}",
      "cmakeCommandArgs": "",
      "buildCommandArgs": "",
      "ctestCommandArgs": "",
      "variables": []
    },
    {
      "name": "x64-Release",
      "generator": "Ninja",
      "configurationType": "RelWithDebInfo",
      "buildRoot": "${projectDir}\\out\\build\\${name}",
      "installRoot": "${projectDir}\\out\\install\\${name}",
      "cmakeCommandArgs": "",
      "buildCommandArgs": "",
      "ctestCommandArgs": "",
      "inheritEnvironments": [ "msvc_x64_x64" ],
      "variables": []
    },
    {
      "name": "Linux-Clang-Debug",
      "generator": "Ninja",
      "configurationType": "Debug",
      "cmakeExecutable": "/usr/bin/cmake",
      "remoteCopySourcesExclusionList": [ ".vs", ".git", "out" ],
      "cmakeCommandArgs": "",
      "buildCommandArgs": "",
      "ctestCommandArgs": "",
      "inheritEnvironments": [ "linux_clang_x64" ],
      "remoteMachineName": "${defaultRemoteMachineName}",
      "remoteCMakeListsRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/src",
      "remoteBuildRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/out/build/${name}",
      "remoteInstallRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/out/install/${name}",
      "remoteCopySources": true,
      "rsyncCommandArgs": "-t --delete --delete-excluded",
      "remoteCopyBuildOutput": false,
      "remoteCopySourcesMethod": "rsync",
      "addressSanitizerRuntimeFlags": "detect_leaks=0",
      "variables": []
    },
    {
      "name": "Linux-Clang-Release",
      "generator": "Ninja",
      "configurationType": "RelWithDebInfo",
      "cmakeExecutable": "/usr/bin/cmake",
      "remoteCopySourcesExclusionList": [ ".vs", ".git", "out" ],
      "cmakeCommandArgs": "",
      "buildCommandArgs": "",
      "ctestCommandArgs": "",
      "inheritEnvironments": [ "linux_clang_x64" ],
      "remoteMachineName": "${defaultRemoteMachineName}",
      "remoteCMakeListsRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/src",
      "remoteBuildRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/out/build/${name}",
      "remoteInstallRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/out/install/${name}",
      "remoteCopySources": true,
      "rsyncCommandArgs": "-t --delete --delete-excluded",
      "remoteCopyBuildOutput": false,
      "remoteCopySourcesMethod": "rsync",
      "addressSanitizerRuntimeFlags": "detect_leaks=0",
      "variables": []
    }
  ]
}

If your WSL instance has all the right packages installed (which could include clang, gcc, gdb, libgtest-dev, make, ninja-build, rsync, and zip), the remote build target should properly produce a debug or release build with ELF binaries.

To verify this, we can login to the WSL instance and locate the temporary output path that VS used to produce the build:

user@MACHINE:~/.vs/CMakeSampleVS/7f954387-c6bd-4590-b386-6f2e8cdbc5b2/out/build/Linux-Clang-Debug$ ./sample-app Linux
Hello, Linux!
user@MACHINE:~/.vs/CMakeSampleVS/7f954387-c6bd-4590-b386-6f2e8cdbc5b2/out/build/Linux-Clang-Debug$ ./sample-test
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from SampleTest
[ RUN      ] SampleTest.GetName
[       OK ] SampleTest.GetName (0 ms)
[----------] 1 test from SampleTest (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (4 ms total)
[  PASSED  ] 1 test.

Success!

Here is the GitHub repo with the changes up to this point: CMakeSampleVS

It appears that everything works on Linux. The final frontier is the command line — how can we replicate the above build and test steps from the comfort of the console? We’ll see next time.

Leave a Reply

Your email address will not be published. Required fields are marked *