Linux Development with C++ in Visual Studio 2019: WSL, ASan for Linux, Separation of Build and Debug

Erika Sweet

Erika

In Visual Studio 2019 you can target both Windows and Linux from the comfort of a single IDE. In Visual Studio 2019 version 16.1 Preview 3 we announced several new features specific to the Linux Workload: native support for the Windows Subsystem for Linux (WSL), AddressSanitizer integration, and the ability to separate build and debug targets. If you’re just getting started with cross-platform development, I recommend trying our native support for WSL.

Native support for the Windows Subsystem for Linux (WSL)

Visual Studio now provides native support for using C++ with WSL. WSL lets you run a lightweight Linux environment directly on Windows, including most command-line tools, utilities, and applications. In Visual Studio you no longer need to add a remote connection or configure SSH in order to build and debug on your local WSL installation. Check out our post on native support for WSL in Visual Studio to learn more and follow a step-by-step guide on getting started.

AddressSanitizer for the Linux Workload

In Visual Studio 2019 version 16.1 Preview 3 we have integrated AddressSanitizer (ASan) into Visual Studio for Linux projects. ASan is a runtime memory error detector for C/C++. You can enable ASan for MSBuild-based Linux projects and CMake projects that target a remote Linux machine or WSL. Check out our post on AddressSanitizer for the Linux Workload in Visual Studio for more information.

Separate build and debug targets for Linux projects

You can now separate your remote build machine from your remote debug machine for both MSBuild-based Linux projects and CMake projects that target a remote Linux machine. For example, you can now cross-compile on x64 and deploy to an ARM device when targeting IoT scenarios.

For a MSBuild-based Linux project, you can specify a new remote debug machine in the project’s Property Pages (Configuration Properties > Debugging > Remote Debug Machine). By default, this value is synchronized with your remote build machine (Configuration Properties > General > Remote Build Machine).

The drop-down menu is populated with all established remote connections. To add a new remote connection, navigate to Tools > Options > Cross Platform > Connection Manager or search for “Connection Manager” in the search bar at the top of your screen. You can also specify a new remote deploy directory in the project’s Property Pages (Configuration Properties > General > Remote Deploy Directory).

By default, only the files necessary for the process to debug will be deployed to the remote debug machine. You can view/configure which source files will be deployed via the Solution Explorer. When you click on a source file, you will see a preview of its File Properties directly below the Solution Explorer. You can also right-click on a source file and select “Properties.”

The “Content” property specifies whether the file will be deployed to the remote debug machine. You can also disable deployment entirely by navigating to Property Pages > Configuration Manager and unchecking “Deploy” for the desired configuration.

If you want complete control over your project’s deployment (e.g. some files you want to deploy are outside of your solution or you want to customize your remote deploy directory per file/directory), then you can append the following code block(s) to your .vcxproj file:

For CMake projects that target a remote Linux machine, you can specify a new remote debug machine via launch.vs.json. By default, the value of “remoteMachineName” will be synchronized with the “remoteMachineName” property in CMakeSettings.json, which corresponds to your remote build machine. These properties no longer need to match, and the value of “remoteMachineName” in launch.vs.json will dictate the remote machine used for deploy and debug.

IntelliSense will suggest all a list of all established remote connections, but you can add a new remote connection by navigating to Tools > Options > Cross Platform > Connection Manager or searching for “Connection Manager” in the search bar at the top of your screen.

If you want complete control over your deployment, you can append the following code block(s) to launch.vs.json:

Resolved issues

The best way to report a problem or suggest a feature to the C++ team is via Developer Community. The following feedback tickets related to C++ cross-platform development have been recently resolved in Visual Studio 2019 16.1 Preview 2 or Preview 3:

No configurations when using CppProperties.json

Unable to attach process of linux vm

cmake linux binary deployment fails with WSL

Infobar appears when open existing CMake cache fails

VS2017 crashes if SSH has connection error while building remote Linux CMake project

CTest timeout feature doesn’t work in test explorer

CMake: Any minor change to CMakeLists.txt triggers a full cache regeneration

CMake + Intellisense: Preprocessor definitions in CMakeLists do not work with quoted strings

Intellisense problem for Linux Makefile project

Talk to us!

Do you have feedback on our Linux tooling in Visual Studio? Pick a time to chat with the C++ cross-platform team and share your experiences – the good and the bad – to help us prioritize and build the right features for you! We can also be reached via the comments below, email (visualcpp@microsoft.com), and Twitter (@VisualC) and (@erikasweet_).

Erika Sweet
Erika Sweet

Follow Erika   

14 comments

  • Avatar
    Ron Even

    That is an absolutely great feature my team wanted for a very long time, however, I can’t make it copy the build output file to the remote debugging machine. My output file is obviously not a file in my project, so I can’t set it to content.
    When I set one of my project files Content to True, it is copied.
    I guess customly specifying the output file in the vcxproj will work, but I am probably missing something here…
    Also, I tried changing the MSBuild logging level but deployment part always shows:
    2>Connecting to remote systems.2>Resolving remote paths.2>Copying files.2>Deployment took 1.6778987 seconds.
    So I can’t know what’s wrong

  • Avatar
    Ron Even

    That is an absolutely great feature my team wanted for a very long time, however, I can’t make it copy the build output file to the remote debugging machine. My output file is obviously not a file in my project, so I can’t set it to content. When I set one of my project files Content to True, it is copied. I guess customly specifying the output file in the vcxproj will work, but I am probably missing something here… Also, I tried changing the MSBuild logging level but deployment part always shows:
    2>Connecting to remote systems.
    2>Resolving remote paths.
    2>Copying files.
    2>Deployment took 1.6778987 seconds.

    So I can’t know what’s wrong

    • Avatar
      Niklas Niebrügge

      Hi Ron, I ran into the same problem but could solve it. I postet a comment here on this page how you can solve this issue.

  • Avatar
    Jack Hansen

    I tried to download this and after a little while found a forum saying preview 3 is not available in community edition. This could be made clear in the writeup and save some folks time, if it’s true. Anyway, the feature set looks like something I’d like to try.

    • Erika Sweet
      Erika Sweet

      Hi Jack,

      All of these features are now available in the GA 16.1 release, available for download here.

      That said, the preview channel is available for all users (Community, Professional, and Enterprise). You can install the latest preview build here. The preview channel is now on version 16.2 Preview 1. 

  • André Nicolaysen
    André Nicolaysen

    Is it possible to get more information about how to set up cmake to have another debug target? 
    I’d like to develop with Visual Studio, build on WSL for Linux ARM and then deploy and debug on Linux ARM.
    It seems that this should be possible, but I can’t seem to wrap my head arround how cmake / visual studio should be configured.
    Any help or guidance is greatly appreciated.

    • Erika Sweet
      Erika Sweet

      Hi André, 

      Unfortunately this support does not apply to native WSL configurations yet, so you will need to connect to WSL via SSH and treat it as a remote connection or order to separate your build machine (WSL) from your deploy/debug machine (ARM device). We are in the process of creating more comprehensive documentation for these new features. For now, if the steps to edit your launch.vs.json file (described above) are not working please reach out to vcpplinux-support@microsoft.com. Thank you! 

  • Avatar
    Office Browser Sync

    Hi Erika, I understand that the documentation is still in-progress, but I wanted to know if this configuration is supported:
    1) Build in Linux-Machine-A (x64)
    2) Transfer the executable from Linux-Machine-A to Linux-Machine-B (ARM, gdbserver-only). –>  Currently, I don’t see a method to do this, other than to write a script and SSH into A to make the transfer.
    3) Debug on B. –> It seems like gdb needs to be in Windows. 
    However, my entire toolchain is in Linux, and machine B only has gdbserver.  I was hoping VS could start gdb on A to attach to the process in B, and relay the data.  It seems like miDebuggerPath is only looking in Windows. It’s fine it this configuration is not supported — I just need to know so I can move on. Thanks, Erika.

    • Erika Sweet
      Erika Sweet

      Hi, first of all I apologize for the late reply. Steps (1) and (2) you can accomplish using the separation of build and deploy feature. Your “build” machine would be machine A and your “debug” machine would be machine B. 

      To debug on B, you can debug directly from Visual Studio without starting gdb on A. This blog post describes how to debug using gdbserver on a microcontroller using CMake. 

  • Avatar
    Paul Lauzon

    Thanks for the article!   Is it possible to build (cross-compile) a linux executable on Windows using Clang/LLVM and then copy the executable to a remote Linux machine for running and debugging? 
    I tried creating a few projects using VisualStudio2019 without success so far.  When selecting a Linux target, it automatically tries to compile on the Linux target.  When selecting a Windows target, it uses the Windows source headers (win32) even when I force it to use Clang/LLVM in a CMakeSettings.json.  Perhaps there is a way to do this?

  • Avatar
    Cem PAYZIN

    Is there a way to use mutiple core to parallel compile like we do on windows? Projects are quite big and it takes a long time.

  • Avatar
    Niklas Niebrügge

    The feature to debug the project on a different target than it was built on is great, but it needs to be better documented and polished, otherwise you end up needing the old project structure with the makefile to copy binaries. The first problem is in the ProjectProperties->Linker->General, there are two fields named Output File now. But with a lot of experimenting I could identify each properties: The first OutputField is the relative output Path (including name + extionsion) of the linked file. This path is also used for copying to the windows project folder. On the remote target this path is appended to the remotedeploydirectory. So normally on all three devices the output file is located at bin\$(Platform)\$(Configuration)\$(TargetName)$(TargetExt). So the specified output directory is pretty much ignored… But you can live with the fact that the appended path is the same on all three platforms as long as you debug a single programm with no other custom shared libraries. But it would be better, if the copy out put is enabled, that the output file is copied to the specified local output directory. And also the remote build output directory and the remote deploy output directory should be choosable independently. The tricky part ist the second “OutputFile”. The entry is internally used under the name RemoteTargetPath and is the absolute path to the linker output file on the build target. The problem here are the default settings for the remote directories under ProjectProperties->General. By default the remote root folder is ~/projects. This way you have a flexible project, that can be used for different users in the remote build machines. But the problem here is that this leads to a bug when deploying on a target that has a different home folder. When the RemoteTargetPath is resolved and ~ is used for the home directory it already uses the home path from the deploy target. So it is possible that the output file cannot be copied to the debug target because Visual Studio is looking at a wrong path on the build machine. So ALWAYS use a hard coded path to the output file location on the remote build machine. Otherwise everything but the output file will be deployed on the debug target… So by default a project with a debug machine different to the build machine is setup wrong by default. Also if the debug target is an ARM device and it only has gdb-server, use the ARM configuration for building with a crosscompiler on an x64 machine and debugging on the ARM device. With the x64 configuration gdb cannot connect to the ARM gdb-server. Addendum: For better code navigation when using a cross compiler, you can explore the folder of the IntelliSense header Cache and remove the “unused” part from settings.xml.unused. Then you can specify the paths to the gcc / g++ compiler paths under CCompilerPathsSemicolonDelim and CppCompilerPathsSemicolonDelim. I kicked out the standard g++ compiler under compilerPath and replaced the includeDirs with thepathtosdk/usr/include  pathtosdk/usr/lib To be save you can resynch the header files after this and have a look at the new settings.xml.unused file. In the includeDirs section there should also be the paths added that were read from the crosscompilers. You can append those additional entries to the include directories in the settings.xml file. But be clear, this only says that those directories shall be downloaded. I also kept the useCompiler to true so IntelliSense nows all compiler standard include paths that you can optionally add to the settings.xml for code navigation. This works with with several different sdks at the same time. IntelliSense seems to lookup the paths according to the compiler you specify in the project properties. But to make  IntelliSense the other important paths you have to add a few include paths under ProjectProperties->VC++Directories->Include Directories: pathtosdk/usr/include pathtosdk/usr/include/c++/X.X.X/ pathtosdk/usr/include/c++/X.X.X/arm…gnueabi
    Edit: I do not know why the comment is always reformatted to this hard ro read block..

Leave a comment