{"id":76,"date":"2021-10-29T20:38:38","date_gmt":"2021-10-29T19:38:38","guid":{"rendered":"https:\/\/mrzebra.co.uk\/code\/?p=76"},"modified":"2024-12-19T14:32:51","modified_gmt":"2024-12-19T14:32:51","slug":"how-to-use-clang-with-visual-studio-code-and-docker","status":"publish","type":"post","link":"https:\/\/zebra-north.com\/code\/2021\/10\/29\/how-to-use-clang-with-visual-studio-code-and-docker\/","title":{"rendered":"How To Use Clang With Visual Studio Code and Docker"},"content":{"rendered":"\n<p>Getting Clang to run inside a Visual Studio Code remote container on Docker is surprisingly difficult, but I&#8217;ll take you through all the steps you need.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Changes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>2024-12-19: Updated dependencies to include <code>llvm-gtest<\/code>.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Why Docker And VS Code?<\/h2>\n\n\n\n<p>If you&#8217;re reading this tutorial then you&#8217;ve probably already decided that this is a good combination for you, but just in case you are on the fence, here are some reasons that you might want to use this setup:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>A completely separate build environment.<\/strong>  Because your application and compiler live within a Docker container, they are kept completely separate from your main system.  This means no conflicts with libraries, no messing around with having multiple versions of compilers installed, and a build environment that can be recreated by other developers on your team with a single command.<\/li>\n\n\n\n<li><strong>Build and debug under any Linux distribution &#8211; from within Windows.<\/strong>  You get the convenience of your Windows desktop while seamlessly compiling, running, and debugging your application in Linux.<\/li>\n\n\n\n<li><strong>Less overhead than virtual machines.<\/strong>  Although Docker containers are similar in concept to a virtual machine, they don&#8217;t have the same resource requirements.  This can save a lot of RAM, especially when running several instances.<\/li>\n\n\n\n<li><strong>VS Code Remote Development.<\/strong>  VS Code is really two pieces of software: a &#8220;client&#8221; that displays the user interface, and a &#8220;server&#8221; that writes your files to disk, runs the debugger, and so on.  Because of this partitioning, the server and client can run on different machines.  This can be over an SSH connection, in WSL, or in a Docker container.  It allows you to use VS Code as if it was operating on your local machine, when actually it&#8217;s doing the real work elsewhere.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>You&#8217;ll need a few pieces of software installed before we get started:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/code.visualstudio.com\/\" data-type=\"URL\" data-id=\"https:\/\/code.visualstudio.com\/\">Visual Studio Code<\/a>, of course.<\/li>\n\n\n\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/wsl\/install\">WSL2<\/a>, Windows Subsystem for Linux.  If you have version 1 installed, you&#8217;ll need to upgrade.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.docker.com\/products\/docker-desktop\">Docker Desktop<\/a>.  Make sure it&#8217;s set in <a href=\"https:\/\/docs.docker.com\/desktop\/windows\/wsl\/\">WSL2 mode<\/a> in the settings.<\/li>\n<\/ul>\n\n\n\n<p>You&#8217;ll also need the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-vscode-remote.vscode-remote-extensionpack\">Remote Development<\/a> extension pack installed in VS Code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting Started<\/h2>\n\n\n\n<p>Load up a shell in your WSL.  You can do this by typing <code>wsl<\/code> in PowerShell or at a Command Prompt, or you can install the excellent and free <a href=\"https:\/\/www.microsoft.com\/en-us\/p\/windows-terminal\/9n0dx20hk701?activetab=pivot:overviewtab\">Windows Terminal<\/a> from the Microsoft Store.  If you&#8217;re using Windows Terminal it will default to PowerShell, so click the &#8220;v&#8221; at the top and select &#8220;Ubuntu&#8221; (or whichever distro you chose when installing).<\/p>\n\n\n\n<p>Create a directory for your project in WSL and launch Visual Studio Code.  This will launch VS Code in a split client\/server mode: the front-end user interface running in Windows, but the back-end running in WSL.  A little later we&#8217;ll switch to running the back end in a Docker container.<\/p>\n\n\n\n<p>I&#8217;ll call my project directory &#8220;clang-example&#8221; for this example.  You can call it whatever you like.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\">cd ~\nmkdir clang-example\ncd clang-example\ncode .<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create Your Dockerfile<\/h2>\n\n\n\n<p>In VS Code, create a new file and name it <code>Dockerfile<\/code> (exactly like that: uppercase D, no file extension).  The contents will be:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">FROM alpine:latest\nRUN apk --no-cache add clang llvm llvm-dev llvm-static llvm-gtest lldb lldb-dev g++ git gcompat cmake make py3-lldb<\/code><\/pre>\n\n\n\n<p>The <code>FROM<\/code> line tells it to start with a base image of Alpine Linux.  Alpine is a minimalistic distribution that will keep your Docker container file sizes small, and is used as a base for many Docker projects.<\/p>\n\n\n\n<p>The next line tells it to run <code>apk<\/code>, Alpine&#8217;s package manager (equivalent to <code>yum<\/code>, <code>apt<\/code>, etc.), and install several pages.  The <code>--no-cache<\/code> option prevents it from needlessly caching files and bloating the size of your container.  I&#8217;ll explain why each package is required.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>clang<\/code>: This should be obvious, it&#8217;s the C\/C++ compler.<\/li>\n\n\n\n<li><code>llvm<\/code>: This is the &#8220;back end&#8221; for the Clang compiler.<\/li>\n\n\n\n<li><code>llvm-dev<\/code>: This provides <code>LLVMConfig.cmake<\/code> so you can compile tools that integrate directly with LLVM, ie. LLDB-MI.<\/li>\n\n\n\n<li><code>llvm-static<\/code>: This provides <code>libLLVMDemangle.a<\/code>, needed for compiling LLDB-MI.<\/li>\n\n\n\n<li><code>llvm-gtest<\/code>: This provides <code>libLLVMTestingAnnotations.a<\/code>, needed for compiling LLDB-MI.<\/li>\n\n\n\n<li><code>lldb<\/code>: The debugger for LLVM.<\/li>\n\n\n\n<li><code>lldb-dev<\/code>: This provides <code>lib_lldb<\/code>, needed for compiling LLDB-MI.<\/li>\n\n\n\n<li><code>g++<\/code>: Clang doesn&#8217;t actually provide standard headers such as <code>iostream<\/code>, so you need to install <code>g++<\/code> for these.<\/li>\n\n\n\n<li><code>git<\/code>: This provides an easy way to get LLDB-MI, and it will integrate with VS Code in your project.<\/li>\n\n\n\n<li><code>gcompat<\/code>: The VS Code <code>cpptools<\/code> extension needs this in order to run &#8211; it expects GCC&#8217;s standard library to be installed, but Alpine Linux uses the alternative Musl standard library.  This package provides a compatibility shim between the two.<\/li>\n\n\n\n<li><code>cmake<\/code>: This is needed for compiling LLDB-MI, and probably your own project as well.<\/li>\n\n\n\n<li><code>make<\/code>: CMake will produce a <code>Makefile<\/code>, which <code>make<\/code> will then use to build the project.<\/li>\n\n\n\n<li><code>py3-lldb<\/code>: This is a Python module used in debugging.  Without it, you will get the error &#8220;No module named &#8216;lldb'&#8221;<\/li>\n<\/ul>\n\n\n\n<p>Phew, that was a lot of packages, and it took a few hours for me to discover all these dependencies&#8230;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Build &amp; Switch To the Container<\/h2>\n\n\n\n<p>In VS Code, press <code>Ctrl + Shift + P<\/code> to bring up the task list, and select &#8220;<code>Remote Containers: Open Folder in Container...<\/code>&#8220;, and press &#8220;<code>OK<\/code>&#8220;, then select &#8220;<code>From Dockerfile<\/code>&#8220;.  This will start building your Docker container, which will take a few minutes.  Click &#8220;<code>(show log)<\/code>&#8221; on the popup to keep an eye on the progress.  Between g++, llvm, and Clang, you can expect it to download a few hundred megabytes.  Docker caches the result of each command in the Dockerfile, so if you want to add more packages later you can add a separate <code>RUN<\/code> command instead of just appending it to the existing one, and then it won&#8217;t have to redownload all the compiler packages.<\/p>\n\n\n\n<p>If you make any changes to the Dockerfile later then you can rebuild your container then you can select &#8220;Remote Containers: Rebuild Container&#8221;.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install Extensions in the Container<\/h2>\n\n\n\n<p>Because the VS Code client is now running inside your Docker container, it needs to have extensions installed there as well.  During the setup process it created a file <code>.devcontainer\/devcontainer.json<\/code> &#8211; load that up and edit the <code>extensions<\/code> key to add Microsoft C\/C++ Tools, CMake Tools, and any others you require:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-json\">{\n\t&quot;extensions&quot;: [\n\t\t&quot;ms-vscode.cpptools&quot;,\n\t\t&quot;ms-vscode.cmake-tools&quot;\n\t],\n}<\/code><\/pre>\n\n\n\n<p>After editing the file, select &#8220;Remote Containers: Rebuild Container&#8221; from the task list.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Building LLDB-MI<\/h2>\n\n\n\n<p>LLDB-MI provides the interface between VS Code and the LLDB debugger, allowing you to step through code, set breakpoints, and so on.  This used to be part of LLDB but was spun off into a separate project.  There is currently no package available for it in Alpine, so you have to build it yourself.  Open a terminal window in VS Code (Ctrl + &#8216;).  Make sure you are in your project directory, for example <code>\/workspaces\/clang-example\/<\/code>, and execute the following commands to download and build the tool:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\">git clone https:\/\/github.com\/lldb-tools\/lldb-mi.git\ncd lldb-mi\ncmake .\ncmake --build .<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring the Debugger with launch.json<\/h2>\n\n\n\n<p>VS Code has to be told which debugger to use, and this is done in the <code>launch.json<\/code> configuration file.  Click on the debugging panel in VS Code (the play button with a bug on the left), and select &#8220;create a launch.json file&#8221;.  Select &#8220;C++ (GDB\/LLDB)&#8221; (if you do not see this, make sure the C++ extension is installed properly).  You may get an error saying it is unable to open the file, in which case just try again and it should work.  You&#8217;ll now need to make some edits to the <code>launch.json<\/code> file:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-json\">{\n    \/\/ Use IntelliSense to learn about possible attributes.\n    \/\/ Hover to view descriptions of existing attributes.\n    \/\/ For more information, visit: https:\/\/go.microsoft.com\/fwlink\/?linkid=830387\n    &quot;version&quot;: &quot;0.2.0&quot;,\n    &quot;configurations&quot;: [\n        {\n            &quot;name&quot;: &quot;LLDB Launch&quot;,\n            &quot;type&quot;: &quot;cppdbg&quot;,\n            &quot;request&quot;: &quot;launch&quot;,\n            &quot;program&quot;: &quot;${workspaceFolder}\/a.out&quot;,\n            &quot;args&quot;: [],\n            &quot;stopAtEntry&quot;: true,\n            &quot;cwd&quot;: &quot;${fileDirname}&quot;,\n            &quot;environment&quot;: [],\n            &quot;externalConsole&quot;: false,\n            &quot;MIMode&quot;: &quot;lldb&quot;,\n            &quot;miDebuggerPath&quot;: &quot;${workspaceFolder}\/lldb-mi\/src\/lldb-mi&quot;,\n            &quot;logging&quot;: {&quot;engineLogging&quot;: true, &quot;trace&quot;: true, &quot;traceResponse&quot;: true},\n            &quot;setupCommands&quot;: [\n                {\n                    &quot;text&quot;: &quot;setting set target.disable-aslr false&quot;,\n                    &quot;description&quot;: &quot;Fix packet returned error 8&quot;,\n                    &quot;ignoreFailures&quot;: false\n                }\n            ]\n        }\n    ]\n}<\/code><\/pre>\n\n\n\n<p>The things to change are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Change <code>program<\/code> to your program&#8217;s executable path.<\/li>\n\n\n\n<li>Change <code>MIMode<\/code> to <code>lldb<\/code>.<\/li>\n\n\n\n<li>Add <code>miDebuggerPath<\/code>.<\/li>\n\n\n\n<li>Modify the <code>setupCommands<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>After compiling your application, you can now use the &#8220;Play&#8221; button on the debugging tab to launch it under the debugger.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">That&#8217;s It!<\/h2>\n\n\n\n<p>That&#8217;s everything &#8211; you now have CMake, the Clang compiler, and the debugger &#8211; all in Docker and hooked up for remote developing with VS Code.<\/p>\n\n\n\n<p>If you have closed VS Code and want to resume development, go to WSL and navigate to your project directory, then run &#8220;<code>code .&quot;<\/code>  When VS Code launches, you will get a dialog with a button to &#8220;Reopen in Container&#8221;.  Press this and it will launch the Docker container and open the VS Code server within it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">CMake Fails to find Build Tools<\/h3>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>CMake was unable to find a build program corresponding to &#8220;Unix Makefiles&#8221;. CMAKE_MAKE_PROGRAM is not set. You probably need to select a different build tool.<\/p>\n<\/blockquote>\n\n\n\n<p>CMake requires that the <code>make<\/code> package is installed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Clang++ Fails to Find Standard Headers<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;fatal error: &#8216;iostream&#8217; file not found&#8221;<\/p>\n<\/blockquote>\n\n\n\n<p>Clang requires that the <code>g++<\/code> package is installed to provide standard headers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Couldn&#8217;t start client cpptools<\/h3>\n\n\n\n<p>You get the error &#8220;Couldn&#8217;t start client cpptools&#8221;, and the Output windows shows:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p> [Error &#8211; 6:15:14 PM] Starting client failed<br>Launching server using command \/root\/.vscode-server\/extensions\/ms-vscode.cpptools-1.7.1\/bin\/cpptools failed.<\/p>\n<\/blockquote>\n\n\n\n<p>Trying to run <code>cpptools<\/code> from the terminal says <code>File not found<\/code>.<\/p>\n\n\n\n<p>This occurs under Alpine Linux because <code>cpptools<\/code> requires the <code>g++<\/code> and <code>gcompat<\/code> packages to be installed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The LLDB Debugger Hangs When Launched<\/h3>\n\n\n\n<p>When you view the Debug Console in VS Code, you see that the debugger is hung at <code>Wait for connection completion.<\/code><\/p>\n\n\n\n<p>Load <code>launch.json<\/code> and set <code>&quot;externalConsole&quot;: false<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The LLDB Debugger Immediately Exits<\/h3>\n\n\n\n<p>When you view the Debug Console in VS Code, you see:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>ERROR: Unable to start debugging. Unexpected LLDB output from command &#8220;-exec-run&#8221;. &#8216;A&#8217; packet returned an error: 8<\/p>\n<\/blockquote>\n\n\n\n<p>This is because the debugger is trying to disable ASLR (Address Space Layout Randomization), but cannot due to Docker&#8217;s default security settings.  GDB ignores this failure, but it causes LLDB to exit.<\/p>\n\n\n\n<p>Load <code>launch.json<\/code> and add the <code>setupCommands<\/code> given in &#8220;Configuring the Debugger with launch.json&#8221;.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The LLDB Debugger Gives &#8220;No module named &#8216;lldb'&#8221; or &#8220;&#8216;run_one_line&#8217; is not defined&#8221;<\/h2>\n\n\n\n<p>When you view the Debug Console in VS Code, you see:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Traceback (most recent call last):<br>File &#8220;&lt;string>&#8221;, line 1, in &lt;module><br>ModuleNotFoundError: No module named &#8216;lldb&#8217;<br>Traceback (most recent call last):<br>File &#8220;&lt;string>&#8221;, line 1, in &lt;module><br>NameError: name &#8216;run_one_line&#8217; is not defined<\/p>\n<\/blockquote>\n\n\n\n<p>This is caused by the LLDB Python module not being installed.  Check the package list in the instructions above, and add <code>py3-lldb<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Getting Clang to run inside a Visual Studio Code remote container on Docker is surprisingly difficult, but I&#8217;ll take you through all the steps you need.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[12,13,15],"class_list":["post-76","post","type-post","status-publish","format-standard","hentry","category-cpp","tag-clang","tag-docker","tag-wsl"],"_links":{"self":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts\/76","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/comments?post=76"}],"version-history":[{"count":16,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts\/76\/revisions"}],"predecessor-version":[{"id":241,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/posts\/76\/revisions\/241"}],"wp:attachment":[{"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/media?parent=76"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/categories?post=76"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zebra-north.com\/code\/wp-json\/wp\/v2\/tags?post=76"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}