 Hey everyone, my name is Erica. I work on Microsoft's Visual C++ team and today I'm going to be chatting with you all about C++ cross-platform development with Visual Studio and the Windows subsystem for Linux. So I'm going to start by defining what we mean when we say native support for WSL and Visual Studio. I'm then going to introduce our CMake support in Visual Studio and then the bulk of my time is going to be spent in a demo where I configure a CMake project to build and debug both locally on Windows and on the Windows subsystem for Linux and then at the end I'm going to quickly cover our WSL2 story. So since Visual Studio 2017, we've had support for building and debugging C++ projects on remote systems over SSH. In Visual Studio 2019, we added native support for WSL and WSL or the Windows subsystem for Linux. If you haven't used it, let's develop or run a Linux environment directly on Windows. When we say our native support, what we mean is that instead of invoking commands on WSL as if it were our remote system over SSH, all commands are executed locally. This means there's just one single copy of your source tree and no need for Visual Studio to manage file copying or maintain two synchronous copies of your build tree, one on Windows and one on your Linux system. This leads to no dependency on SSH as well as performance improvements. If you're using Visual Studio 2019 version 16.4 or later, you can also leverage the integrated terminal to interact directly with WSL from Visual Studio. CMake is a cross-platform open-source meta-build system and it's really popular among C++ developers, especially among those who are doing cross-platform development. It is our recommendation for anything cross-platform or with an IDA open-source team. CMake support in Visual Studio means that you can open any folder containing a CMakeList.txt file and edit it, build it, and debug it locally on Windows, on our remote system, or on WSL without ever generating Visual Studio project and solution files. You can still leverage almost the same full suite of IntelliSense code navigation and debugging features that you might be used to when working with Windows-based solutions, as well as some CMake-specific features that will make it easier for you to make sense of, edit, and author your own CMake scripts in Visual Studio. All of this stuff I'm going to be showing and talking about more as a part of my demo. With that, I'm just going to hop in. Here I have a CMake project open in Visual Studio. It looks and behaves very similarly to a normal Visual Studio solution with a few key differences. The CMake Settings Editor is where you can specify what system CMake will be invoked on and where you'll be building. So again, that can be locally on Windows, that can be on our remote system connected over SSH, or that can be natively on WSL. So right now I'm building on WSL using the GCC toolset. The CMake Settings Editor is also where you can specify things like configuration type, any CMake variables or environment variables, and pick your compilers. The CMake Settings Editor is just an overlay on top of the CMakeSettings.json file, which can be checked in and shared between team members so that all of your project-specific CMake configuration only needs to be done once. The project that I'm working with right now is just a calculator that takes in any number of post-fix expressions and evaluates them. So I'll go ahead and run it and show you how it works. Note that because I'm building and running on WSL, whenever I start debugging, it's using the Frontend, the Visual Studio Debugger backed by GDB. So here I have the Linux Console window, which is a way for me to interact directly with my applications running on WSL or on a remote system from Visual Studio. It's interactive so I can give it some input. I'll read in an expression, x equals 2, read in another expression, x plus x, and then evaluate the second expression, or in this case, x plus x. And it looks like I've hit an address sanitizer error. So address sanitizer is a runtime memory error detector for C and C++ that used to be native to Linux and Mac that we have integrated directly with the Linux workload in Visual Studio. So that address sanitizer errors will surface in the IDE alongside your code whenever you are debugging on WSL or on our remote system. And I say used to because we actually recently ported ASAN over to be used on Windows with the MSVC tool set as well. But it looks like I have a heat buffer overflow issue here. So I'll dig around and see if I can see what's happening. So we'll jump over to the call stack and start stepping through. And here it looks like I have an array of expressions with two elements, but we're trying to access the second element and it's index at zero, so that's out of bounds. So I want to see if I can find where this m expression number is being set. So I will find all references. And it looks like these results returned at the top is where it's actually being set. So I'm going to jump to about, yeah, line 8090 ish. And yeah, here it looks like we're reading in all of the expressions. But then we're setting expression number equal to red number. And for a better user experience, this should probably be red number minus one. So that for example, if the user wants to evaluate their second expression, we're accessing the first element of the array. So I'll change that here and here, save the file. And then I'm also going to hop back over here and set a breakpoint where this is actually being evaluated. So I can make sure it's behaving as I want. I just said a normal breakpoint, but when you're debugging on WSL or our remote system with Visual Studio, you still have the full suite of debugging features available to you. So I could still make this, you know, conditional breakpoint or a trace point, which doesn't halt code execution. And with that, I will restart the debugger. All right, back to my Linux console window. I'll just feed it the exact same input. So I'll read in x equals two, read in x plus x, evaluate the second expression. And this time, the array still has two elements, but we are accessing the first element. So that looks a lot better. When I continue execution, it looks like two plus two does indeed equal four this time. So that's behaving as expected. The last debugging feature that I want to show off to you guys is that you can interact directly with the underlying debugger, in this case, GDB, and execute custom GDB commands. So if I hop on over to the command window, and I use the command debug dot mi debug exec, my engine is the open source engine that we used to interface with GDB, then I can execute any GDB commands. So let's say that I want to have you an assembler dump of this print post fix expression. Then if I pause the debugger, I can view that full sampler dump right in the command window. And hopefully this shows you that when you are debugging on WSL or remote system with Visual Studio, you can still leverage the same suite of debugging features that you might be used to when using an IDE like Visual Studio. So a really visual way to navigate the call stack or set breakpoints, as well as us bringing native Linux tooling to Visual Studio. So things like ASAN or the ability to execute custom GDB commands. So so far I've been debugging, but now I want to add a logging library in case anyone else who's using this runs into issues that they want to send on over to me. So I'll go over to my CMake list, which is where I set dependencies on third party libraries. And I'm going to use the find package command. You can see that I'm getting an IntelliSense suggestions and tool tips. And that's a part of the CMake and editor documentation that we added to surface surface official CMake docs directly in the IDE. So I'm going to add G log, which is the logging library, and I'll add a config required. So we'll save that to regenerate the cache. And it looks like CMake is yelling at me because I don't actually have G log installed on WSL. But I'm getting this quick action to install G log using VC package. So I'll start that and then kind of walk you through what's happening. So if you haven't had, haven't heard of it, VC package is a cross platform command line tool that can be used to bring down, build and install C and C plus libraries from source on Windows, Linux and Mac OS. So here you can see I'm installing the X X 64 Linux triplet of G log directly on WSL. And all of the output is being routed to the output windows so that I can keep an eye on it. Note that I only got this VC package quick action suggestion because I already had VC package installed on WSL. And I've included some links at the end of my deck that shows you how to get started with VC package if you don't already have it. If I hover over the library here, I can see that there's two commands I need to include to consume this library, the find package command, which I just added, and then the target link libraries command. I actually already have that as well. So I'm just going to add G log. I can see that I'm inheriting these default linker options, default compile options and default project options. If I didn't actually know what these default options were, we've added language services to CMake scripts so I can do things like peak definition and see those default linker options enumerated for me. Note that this is pulling from a separate file in a completely different sub directory. And this is really common when you're working with large CMake projects, they're oftentimes structured across multiple different sub directories with a lot of different CMake lists.txt. And so hopefully this is a way a tool that can make it easier for you to make sense of edit and author your own CMake scripts in Visual Studio. So it looks like this has been done for a while. It's telling me G log, installation of G log has exceeded, regenerate the CMake cache to detect the new package. That's why I've been getting this gold bar up here. So I can regenerate the cache and I should be good to go. Over here in the solution explorer, the layout of my files currently matches the layout of my files on disk. And that's fine for this project because it's pretty small. But if I was working with a larger project, there's something called CMake targets view, which is a more CMake centric way of viewing your code organized by target that can help you to more easily make sense of really large projects with more complex structures. We've recently also added CMake project editing support in Visual Studio. And so that is Visual Studio's attempt to help you easily add rename and remove file and target references to existing projects in Visual Studio. So let's say that I want to add a new source file, just call it source dot CPP. Instead of just dropping that file on disk, Visual Studio will try to guess where you can add a reference to that new source file in your CMake scripts so that it's actually picked up. This is what MS Build does automatically behind the scenes when you're working in VCX project. But because there's a million different ways that you can author your own CMake scripts, Visual Studio will make its best guess. And if it has, if there's any ambiguity, it'll suggest multiple different options as to where that reference can be added. And so you can view and preview them all and then only apply the changes that you want to see. So so far, even though CMake is cross that cross platform, I've only been building and debugging on WSL. So now I'm going to show you how easy it is to get started and retarget the same project for Windows. So go back to the CMake settings editor, add a new configuration. This time it's going to be an x64 debug configuration. There's nothing that I need to specify here, but again, this is where I can specify things like configuration type and my compilers. We have out of the box support for both Clang CL and MSBC when you're targeting Windows. So let me make this my active configuration and then select the same executable just so I can show you that it's the same application running both on WSL and on Windows. So here's my calculator. I can read in an expression x equals to x plus x, evaluate the second expression. Here I'm hitting the same breakpoint that I had set earlier when I was debugging on Linux. So you can see it's the same source code, same everything. So I'll stop debugging. In Visual Studio 2019 version 16.4 there was support added for an integrated terminal and that can be used to interface with the developer command prompt with CMD or with a local WSL installation. So here is my local WSL installation Ubuntu. It drops me right in my working directory, which you can see is the mounted C drive. So if I take a look at my file structure, I can again see that I only have one source directory, one root CMakeList.txt, one CMakeSettings.json. So I'm using the same source code to target both Windows and WSL. If I CD into my build folder, then you can see I have two set of directories, one for my WSL configuration that contains ELF binaries and one for my x64 debug configuration that contains my Windows executables. So two different executables can be generated from the same source code. Right now I'm just using this integrated terminal to navigate my file structure, but it can be used to run command line tools or whatever steps you normally need to take outside of Visual Studio directly from the command line. All right, so another thing that we hear from cross-platform developers is that they oftentimes only build for one platform locally and then they'll rely on their CI system to check build errors across their other target platforms after they've checked in. So we have a feature that will hopefully make it easier for you to check build errors across multiple platforms before checking in. So let's say that I add something or accidentally include something that is Windows specific, like including Windows.h. Then you can see I get these purple squiggles, which is basically telling me, hey, this is defined for your current active configuration, my x64 configuration, but it's not defined for one of your other configurations, in this case, WSL. If I make WSL my active configuration, then those squiggles should turn red because Windows.h is just not defined on Linux. But if I were to wrap this in an ifdef, so ifdef, win, three, two, then you'll see all these squiggles go away and I'm back to having platform agnostic code. So hopefully this will let you make it easier for you to check build errors across multiple platforms before checking in, especially if you're, you know, typically a Windows shop and you build for Windows, but then you also want to make sure that you compile on Linux. Adding a WSL configuration is a really quick and easy way to do that. Alright, so the last feature I want to show you guys is something we call the separation of build and debug, and that's the ability to separate your build system from the system that you are deploying to and debugging on. These two systems don't necessarily have to have the same architecture or instruction set. The example I'm going to be showing you today is pretty simple. I'm going to keep my same build configuration so I will continue building natively on WSL, but I'm going to deploy to and debug on a Linux Docker container that's running locally and connected over SSH. So I'm going to go ahead and add a new debug configuration. I want launch for Linux with GDB because I am going to be debugging on our remote host and this adds a new CPP GDB template. We very recently streamlined the debug templates that are used when debugging with GDB on WSL or remote system. So this might look a bit different than what you've seen with previous versions of VS. The only changes that I need to make here is one, I will add a more friendly name just so I can identify this configuration. I need to add a project target, which in this case, I can just add my calculator. It just needs to be one of your targets that's already defined in this dropdown. And then I'll need to add the key remote machine name. So by default, the value of this key is synchronized with your build system, which is set and same mixed settings.json. So by default when I'm debugging, I'm debugging where I'm building natively on WSL. Only when I want to separate these two systems do I need to specify this remote machine name in launch.vs.json. And this will specify the machine that I am deploying to and debugging on. So I'll go ahead and select my local host, which is my Linux Docker container running locally. And I will save that this launch.vs.json file is always where you will configure debugging sessions for CMake projects in Visual Studio. So let me just pull up that Docker container real quick. If I list the running processes, you can see that my only processes are SSH and bash. But when I select my custom debug configuration and start debugging, what's happening is that I am continuing to build locally on WSL, but I will be deploying to and start debugging on my Linux Docker container. So from Visual Studio's point of view, this looks exactly the same. I still have my Linux console window here to interact with the application. But if I jump back to my Docker container, then you can see that GDB is running and my Calculate executable is running. And so I am debugging on this system. You can also see here in my build directory that only the binary folder has been copied over. There's no intermediate build output because the build is still happening locally on WSL. And this is only the things I need to be able to debug. All right. So that kind of wraps up the demo session. Let me jump back to the slide deck. To quickly summarize, some things we talked about today are debugging features that let you leverage the full Visual Studio debugging experience with CMake projects when you're working on a remote system or WSL, as well as Linux specific tooling like the Linux console window ASAN or executing custom GDB commands. We covered cross-platform VC package integration for library acquisition. I only did this on WSL, but it works the same on Windows or on our remote system. We covered CMake language services and project editing that make it easier for you to make sense of, edit, and author your CMake scripts. We covered platform-specific IntelliSense or those purple squiggles, which will hopefully make it easier for you to check for build errors across multiple platforms before checking in. And we covered the ability to separate the system that you are building on from the system that you are debugging to and deploying on. One last note that I want to make is that this whole demo was done with a CMake project, but the same exact feature set minus the CMake specific features are available with MS Build-based Linux projects as well. And that can be an option for you if you're not writing cross-platform code, like you're only building and debugging on Linux from Windows as your host operating system or if you just don't want to use CMake. And then quickly, WSL 2 works in Visual Studio, except you cannot yet use our native support if your source files are stored in the Linux root file system and the C++ team blog is where you should go for updates on that. That's it for my demo. Thank you guys. The code base that I used is on GitHub. If you want to play around with anything, C++ team blog is the place to go for all announcements. And then if you want to take a screenshot of this, this is some relevant documentation to get started with WSL or CMake support debugging or configuring your launch file to debug in Visual Studio, that launch.bs.json file and VC package.