{"id":25287,"date":"2019-12-13T23:00:06","date_gmt":"2019-12-13T23:00:06","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=25287"},"modified":"2019-12-13T23:07:06","modified_gmt":"2019-12-13T23:07:06","slug":"build-c-applications-in-a-linux-docker-container-with-visual-studio","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/build-c-applications-in-a-linux-docker-container-with-visual-studio\/","title":{"rendered":"Build C++ Applications in a Linux Docker Container with Visual Studio"},"content":{"rendered":"<p>Docker containers provide a consistent development environment for building, testing, and deployment. The virtualized OS, file system, environment settings, libraries, and other dependencies are all encapsulated and shipped as one image that can be shared between developers and machines. This is especially useful for C++ cross-platform developers because you can target a container that runs a different operating system than the one on your development machine.<\/p>\n<p>In this blog post we\u2019re going to use Visual Studio\u2019s native CMake support to build a simple Linux application in a Linux docker container over SSH. This post focuses on creating your first docker container and building from Visual Studio. If you\u2019re interested in learning more about Docker as a tool to configure reproducible build environments, check out our post on <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/using-multi-stage-containers-for-c-development\/\">using multi-stage containers for C++ development.<\/a><\/p>\n<p>This workflow leverages Visual Studio\u2019s native support for CMake, but the same instructions can be used to build a <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/linux\/configure-a-linux-project?view=vs-2019\">MSBuild-based Linux project in Visual Studio<\/a>.<\/p>\n<h4>Set-up your first Linux docker container<\/h4>\n<p>First, we\u2019ll set-up a Linux docker container on Windows. You will need to download the <a href=\"https:\/\/www.docker.com\/products\/docker-desktop\">Docker Desktop Client for Windows<\/a> and create a docker account if you haven\u2019t already. See <a href=\"https:\/\/docs.docker.com\/docker-for-windows\/\">Install Docker Desktop on Windows<\/a> for download information, system requirements, and installation instructions.<\/p>\n<p>We\u2019ll get started by pulling down an image of the <a href=\"https:\/\/hub.docker.com\/_\/ubuntu\">Ubuntu OS<\/a> \u00a0and running a few commands. From the Windows command prompt run:<\/p>\n<pre class=\"\">&gt; docker pull ubuntu<\/pre>\n<p>This will download the latest image of Ubuntu from Docker. You can see a list of your docker images by running:<\/p>\n<pre class=\"\">&gt; docker images<\/pre>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-25288\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_images.png\" alt=\"\" width=\"958\" height=\"52\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_images.png 958w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_images-300x16.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_images-768x42.png 768w\" sizes=\"(max-width: 958px) 100vw, 958px\" \/><\/p>\n<p>Next, we\u2019ll use a Dockerfile to create a custom image based on our local image of Ubuntu. Dockerfiles contain the commands used to assemble an image and allow you to automatically reproduce the same build environment from any machine. See <a href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/\">Dockerfile reference<\/a> for more information on authoring your own Dockerfiles. The following Dockerfile can be used to install Visual Studio&#8217;s <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/linux\/download-install-and-setup-the-linux-development-workload?view=vs-2019#options-for-creating-a-linux-environment\">required build tools\u00a0<\/a>and configure SSH. CMake is also a required dependency but I will deploy <a href=\"https:\/\/github.com\/microsoft\/CMake\/releases\/tag\/v3.15.3150938\">statically linked binaries<\/a> directly from Visual Studio in a later step. Use your favorite text editor to create a file called \u2018Dockerfile\u2019 with the following content.<\/p>\n<pre class=\"lang:default highlight:0 decode:true\"># our local base image\r\nFROM ubuntu \r\n\r\nLABEL description=\"Container for use with Visual Studio\" \r\n\r\n# install build dependencies \r\nRUN apt-get update &amp;&amp; apt-get install -y g++ rsync zip openssh-server make \r\n\r\n# configure SSH for communication with Visual Studio \r\nRUN mkdir -p \/var\/run\/sshd\r\n\r\nRUN echo 'PasswordAuthentication yes' &gt;&gt; \/etc\/ssh\/sshd_config &amp;&amp; \\ \r\n   ssh-keygen -A \r\n\r\n# expose port 22 \r\nEXPOSE 22<\/pre>\n<p>We can then <a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/build\/\">build an image<\/a> based on our Dockerfile by running the following command from the directory where your Dockerfile is saved:<\/p>\n<pre class=\"\">&gt; docker build -t ubuntu-vs .<\/pre>\n<p>Next, we can <a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/run\/\">run a container<\/a> derived from our image:<\/p>\n<pre class=\"\">&gt; docker run -p 5000:22 -i -t ubuntu-vs \/bin\/bash<\/pre>\n<p>The -p flag is used to expose the container\u2019s internal port to the host. If this step was successful, then you should automatically attach to the running container. You can stop your docker container at any time and return to the command prompt using the <strong>exit <\/strong>command. To reattach, run <strong>docker ps -a<\/strong>, <strong>docker start &lt;container-ID&gt;<\/strong>, and <strong>docker attach &lt;container-ID&gt;<\/strong> from the command prompt.<\/p>\n<p>Lastly, we will interact with our docker container directly to start SSH and create a user account to use with our SSH connection. Note that you can also enable root login and start SSH from your Dockerfile if you want to avoid any manual and container-specific configuration. Replace &lt;user-name&gt; with the username you would like to use and run:<\/p>\n<pre class=\"\">&gt; service ssh start\r\n&gt; useradd -m -d \/home\/&lt;user-name&gt; -s \/bin\/bash -G sudo &lt;user-name&gt;\r\n&gt; passwd &lt;user-name&gt;<\/pre>\n<p>The -m and -d flags create a user with the specified home directory, and the -s flag sets the user\u2019s default shell.<\/p>\n<p>You are now ready to connect to your container from Visual Studio.<\/p>\n<h4>Connect to your docker container from Visual Studio<\/h4>\n<p>Make sure you have <a href=\"https:\/\/visualstudio.microsoft.com\/downloads\/\">Visual Studio 2019<\/a> and the <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/linux\/download-install-and-setup-the-linux-development-workload?view=vs-2019#visual-studio-setup\">Linux development with C++ workload<\/a> installed.<\/p>\n<p>Open Visual Studio 2019 a create a new CMake Project. CMake is cross-platform and allows you to configure an application to run on both Windows and Linux.<\/p>\n<p>Once the IDE has loaded, you can add a SSH connection to your Linux docker container the same way you would add any other <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/linux\/connect-to-your-remote-linux-computer?view=vs-2019\">remote connection<\/a>. Navigate to the Connection Manager (Tools &gt; Options &gt; Cross Platform &gt; Connection Manager) and select \u201cAdd\u201d to add a new remote connection.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-25289\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_newConnection.png\" alt=\"Add a new remote connection in Visual Studio, with input fields for host name, port, user name, authentication type, and password.\" width=\"500\" height=\"489\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_newConnection.png 659w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_newConnection-300x294.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/p>\n<p>Your host name should be \u201clocalhost\u201d, the port should be whatever you are using for your SSH connection (in this example we\u2019re using 5000), and your username and password should match the user account that you just created for your container.<\/p>\n<h4>Configure build in Visual Studio<\/h4>\n<p>At this point the project behaves like any other CMake project in Visual Studio. To configure and build the console application in our Linux container navigate to \u201cManage Configurations\u2026\u201d in the configuration drop-down.<img decoding=\"async\" class=\"aligncenter wp-image-25290\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_configurationDropDown.png\" alt=\"\" width=\"500\" height=\"147\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_configurationDropDown.png 654w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_configurationDropDown-300x88.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/p>\n<p>You can then select the green plus sign in the CMake Settings Editor to add a new \u201cLinux-Debug\u201d configuration. Make sure that the remote machine name of your Linux configuration matches the remote connection we created for our Linux docker container.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-25291\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_remoteMachineName.png\" alt=\"Remote machine name property in the CMake Settings Editor showing the local docker container I am connected to\" width=\"750\" height=\"103\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_remoteMachineName.png 1053w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_remoteMachineName-300x41.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_remoteMachineName-1024x140.png 1024w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/12\/docker_remoteMachineName-768x105.png 768w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/p>\n<p>Save the CMake Settings Editor (ctrl + s) and select your new Linux configuration from the configuration drop-down to kick off a CMake configuration. If you don\u2019t already have CMake installed on your docker container, then Visual Studio will prompt you to deploy <a href=\"https:\/\/github.com\/microsoft\/CMake\/releases\/tag\/v3.15.3150938\">statically linked binaries<\/a> directly to your remote connection as a part of the configure step.<\/p>\n<p>At this point you can build your application in your Linux docker container directly from Visual Studio. Additional build settings (including custom toolchain files, CMake variables, and environment variables) can be configured in the <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/customize-cmake-settings?view=vs-2019\">CMake Settings Editor<\/a>. The underlying <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/cmakesettings-reference?view=vs-2019\">CMakeSettings.json file<\/a> can store multiple build configurations and can be checked into source control and shared between team members.<\/p>\n<h4>Coming next<\/h4>\n<p>This post showed you how to build a C++ application in a Linux docker container with Visual Studio. Stay tuned for our next post, where will we show you how to copy the build artifacts back to your local Windows machine and debug using gdbserver on a second remote system.<\/p>\n<h4>Give us your feedback<\/h4>\n<p>Do you have feedback on our Linux tooling or CMake support in Visual Studio? We\u2019d love to hear from you to help us prioritize and build the right features for you. We can be reached via the comments below, <a href=\"https:\/\/developercommunity.visualstudio.com\/spaces\/8\/index.html\">Developer Community<\/a>, email (<a href=\"mailto:visualcpp@microsoft.com\">visualcpp@microsoft.com<\/a>), and Twitter (<a href=\"https:\/\/twitter.com\/visualc\">@VisualC<\/a>).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Docker containers provide a consistent development environment for building, testing, and deployment. The virtualized OS, file system, environment settings, libraries, and other dependencies are all encapsulated and shipped as one image that can be shared between developers and machines. This is especially useful for C++ cross-platform developers because you can target a container that runs [&hellip;]<\/p>\n","protected":false},"author":2953,"featured_media":25289,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,266,292,279],"tags":[],"class_list":["post-25287","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus","category-cmake","category-containers","category-linux"],"acf":[],"blog_post_summary":"<p>Docker containers provide a consistent development environment for building, testing, and deployment. The virtualized OS, file system, environment settings, libraries, and other dependencies are all encapsulated and shipped as one image that can be shared between developers and machines. This is especially useful for C++ cross-platform developers because you can target a container that runs [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/25287","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/2953"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=25287"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/25287\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/25289"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=25287"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=25287"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=25287"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}