{"id":23891,"date":"2018-11-27T12:12:01","date_gmt":"2018-11-27T12:12:01","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=23891"},"modified":"2019-02-22T12:18:29","modified_gmt":"2019-02-22T12:18:29","slug":"exploring-clang-tooling-using-build-tools-with-clang-tidy","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/exploring-clang-tooling-using-build-tools-with-clang-tidy\/","title":{"rendered":"Exploring Clang Tooling &#8211; Using Build Tools with clang-tidy"},"content":{"rendered":"<p><em>This post is part of a regular series of posts where the C++ product team and other guests answer questions we have received from customers. The questions can be about anything C++ related: MSVC toolset, the standard language and library, the C++ standards committee, isocpp.org, CppCon, etc.<\/em><\/p>\n<p><em>Today\u2019s post is by guest author Stephen Kelly, who is a developer at Havok, a contributor to Qt and CMake and <a href=\"https:\/\/steveire.wordpress.com\/\">a blogger<\/a>. This post is part of a series where he is sharing his experience using Clang tooling in his current team.<\/em><\/p>\n<p>The previous <a href=\"http:\/\/blogs.msdn.microsoft.com\/vcblog\/2018\/11\/06\/exploring-clang-tooling-part-3-rewriting-code-with-clang-tidy\">series about <tt>clang-tidy<\/tt><\/a> on this blog covered the basics of creating a <tt>clang-tidy<\/tt> extension and tooling to support that in the form of <tt>clang-query<\/tt>.<\/p>\n<p>While the series focused on single-file examples for simplicity, developers progressing in this direction will need to run the tooling on all of the files in their project at once, or on all files which match a specific pattern.<\/p>\n<h3>Delayed refactoring<\/h3>\n<p>The first problem with processing multiple files is that we can no longer change files as we process them and discover locations to refactor. Tools like <tt>clang-tidy<\/tt> only work if the source code compiles, so a process which changed a header file while processing the first source file would cause the next source file to not be compilable.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23892\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/cpp-files.png\" alt=\"\" width=\"833\" height=\"410\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/cpp-files.png 833w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/cpp-files-300x148.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/cpp-files-768x378.png 768w\" sizes=\"(max-width: 833px) 100vw, 833px\" \/><\/p>\n<p>To resolve this problem, <tt>clang-tidy<\/tt> has the ability to export refactoring changes to a <tt>.yaml<\/tt> file, instead of changing the files directly.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23893\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/replacements-db.png\" alt=\"\" width=\"869\" height=\"340\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/replacements-db.png 869w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/replacements-db-300x117.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/replacements-db-768x300.png 768w\" sizes=\"(max-width: 869px) 100vw, 869px\" \/><\/p>\n<p>The <tt>clang-apply-replacements<\/tt> tool can then be run on a directory of <tt>.yaml<\/tt> files in order to apply the changes to all of the files at once.<\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/2018\/11\/apply-replacements.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-23894\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/apply-replacements.png\" alt=\"\" width=\"906\" height=\"343\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/apply-replacements.png 906w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/apply-replacements-300x114.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/apply-replacements-768x291.png 768w\" sizes=\"(max-width: 906px) 100vw, 906px\" \/><\/a><\/p>\n<p>The <a href=\"https:\/\/github.com\/llvm-mirror\/clang-tools-extra\/blob\/c2e6f37\/clang-tidy\/tool\/run-clang-tidy.py\"><tt>run-clang-tidy<\/tt><\/a> script in the clang repository helps with these tasks. It accepts a pattern of files and processes all matching files in parallel, making use of all available cores.<\/p>\n<h3>Build tools<\/h3>\n<p>Consider the similarity between using a compiler with a <tt>.cpp<\/tt> file to produce an object file and using <tt>clang-tidy<\/tt> to produce a <tt>.yaml<\/tt> file.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23895\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/comparison-cpp.png\" alt=\"\" width=\"908\" height=\"279\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/comparison-cpp.png 908w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/comparison-cpp-300x92.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/comparison-cpp-768x236.png 768w\" sizes=\"(max-width: 908px) 100vw, 908px\" \/><\/p>\n<p>This similarity implies that we can use build tools with <tt>clang-tidy<\/tt>.<\/p>\n<p>We can use any tool to generate a Ninja buildsystem, but generally they are not currently optimized for generating commands which invoke clang-tidy instead of a compiler. Although <a href=\"https:\/\/cmake.org\/cmake\/help\/latest\/variable\/CMAKE_LANG_CLANG_TIDY.html\">CMake has <tt>clang-tidy<\/tt> support<\/a>, it doesn&#8217;t have direct support for delayed refactoring, so the CMake integration is currently more suitable to linting instead of refactoring tasks.<\/p>\n<p>For now, using some tricks, we can use CMake to <a href=\"http:\/\/github.com\/steveire\/codedive2018\/blob\/master\/0005-ninja-build\/CMakeLists.txt\">generate a buildsystem<\/a> from a <tt>compile_commands.json<\/tt> file. The generated &#8216;buildsystem&#8217; simply uses <tt>clang-tidy<\/tt> in place of the compiler, so that it outputs <tt>.yaml<\/tt> files instead of object files. The CMake script produces a &#8216;buildsystem&#8217; based on the content of a <tt>compile_commands.json<\/tt> file which you have already generated.<\/p>\n<p>We can instruct CMake to generate a Ninja &#8216;buildsystem&#8217; and run a &#8216;build&#8217; in the normal way to invoke the refactor:<\/p>\n<pre>cmake .. -G Ninja -DCMAKE_CXX_COMPILER=&lt;path_to_clang_tidy&gt;\r\ncmake --build .\r\n<\/pre>\n<p>Ninja processes the inputs in parallel, so this results in a collection of <tt>.yaml<\/tt> files in the <tt>fixes<\/tt> directory. We can use <tt>clang-apply-replacements<\/tt> to apply those fixes to the source code.<\/p>\n<p>Using CMake and Ninja brings advantages that the <tt>run-clang-tidy<\/tt> script doesn&#8217;t provide. Because we are modelling mechanical refactoring as a build task, we can use other build tools which work with Ninja and CMake. To start, we can <a href=\"https:\/\/github.com\/nico\/ninjatracing\">convert the log of Ninja<\/a> performing the refactor to a trace which is compatible with the Chrome <tt>about:tracing<\/tt> tool. This gives output showing the length of time taken for each translation unit:<\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/2018\/11\/tracing.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-23896\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/tracing.png\" alt=\"\" width=\"902\" height=\"577\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/tracing.png 902w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/tracing-300x192.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/tracing-768x491.png 768w\" sizes=\"(max-width: 902px) 100vw, 902px\" \/><\/a><\/p>\n<p>We can also take advantage of the fact that we are now using CMake to handle the refactoring. Using Visual Studio Code and the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=vector-of-bool.cmake-tools\">CMake Tools<\/a> plugin, we can simply open the folder containing the CMakeLists.txt and trigger the refactoring task from there.<\/p>\n<p>Add a <a href=\"https:\/\/vector-of-bool.github.io\/docs\/vscode-cmake-tools\/kits.html\">custom kit<\/a> to the <tt>CMake Tools<\/tt> for running <tt>clang-tidy<\/tt>:<\/p>\n<pre>{\r\n  \"name\": \"Clang tidy\",\r\n  \"compilers\": {\r\n    \"CXX\": \"C:\/dev\/prefix\/bin\/clang-tidy.exe\"\r\n  }\r\n}\r\n<\/pre>\n<p>Now, when we invoke <tt>Build<\/tt> in Visual Studio Code, the refactoring is started. Diagnostics are also collected with easy navigation to the source code.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23897\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/vsc-clang-tidy.gif\" alt=\"\" width=\"992\" height=\"652\" \/><\/p>\n<p>Because CMake can generate Visual Studio solutions, it is also possible to control the refactoring from within Visual Studio. As this requires creating a <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ff770576.aspx\">Toolset file<\/a> to replace the compiler with <tt>clang-tidy<\/tt>, it is slightly out of scope of this post but it follows the same pattern to achieve the result.<\/p>\n<h3>Distributing the refactor<\/h3>\n<p>Consider how we distribute our build tasks on the network.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23898\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-cpp.png\" alt=\"\" width=\"832\" height=\"641\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-cpp.png 832w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-cpp-300x231.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-cpp-768x592.png 768w\" sizes=\"(max-width: 832px) 100vw, 832px\" \/><\/p>\n<p>If we treat <tt>clang-tidy<\/tt> as a compiler, then we should be able to use a build-distribution tool to distribute our refactoring task on the network.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23899\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-tidy.png\" alt=\"\" width=\"884\" height=\"659\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-tidy.png 884w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-tidy-300x224.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/distribute-tidy-768x573.png 768w\" sizes=\"(max-width: 884px) 100vw, 884px\" \/><\/p>\n<p>One such build distribution tool is <a href=\"https:\/\/github.com\/icecc\/icecream\">Icecream<\/a>, which is popular on Linux systems and available under the GPL. Icecream works by sending an archive of the build tools to client machines so that the actual compilation is run on the remote machine and the resulting object file is sent back to the client.<\/p>\n<p>By <a href=\"http:\/\/github.com\/steveire\/codedive2018\/blob\/master\/0005-ninja-build\/script\">packaging the clang-tidy executable<\/a>, renamed to <tt>clang<\/tt> so that Icecream accepts it, we can refactor on remote machines and send resulting .obj files (named so that Icecream accepts them, but containing yaml content) to clients. The Icecream Monitor tool then shows a progress of the distributed task among the build nodes.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-23900\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/icecream2.png\" alt=\"\" width=\"612\" height=\"446\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/icecream2.png 612w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2019\/02\/icecream2-300x219.png 300w\" sizes=\"(max-width: 612px) 100vw, 612px\" \/><\/p>\n<p>This work distribution brings a significant increase in speed to the refactoring task. Using this technique I have been able to make mechanical changes to the LLVM\/Clang source (millions of lines of code) in minutes which would otherwise take hours if run only locally. Because there is no need to link libraries while refactoring, each refactor does not conflict with any other and the process can be embarrassingly parallel.<\/p>\n<h3>Conclusion<\/h3>\n<p>Mechanical refactoring with <tt>clang-tidy<\/tt> requires distribution over a network in order to complete in reasonable time on large codebases. What other build tools do you think would be adaptable for refactoring tasks? Let us know in the comments below or contact the author directly via e-mail at <a href=\"mailto:stkelly@microsoft.com\">stkelly@microsoft.com<\/a>, or on Twitter <a href=\"https:\/\/twitter.com\/steveire\">@steveire<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is part of a regular series of posts where the C++ product team and other guests answer questions we have received from customers. The questions can be about anything C++ related: MSVC toolset, the standard language and library, the C++ standards committee, isocpp.org, CppCon, etc. Today\u2019s post is by guest author Stephen Kelly, [&hellip;]<\/p>\n","protected":false},"author":890,"featured_media":23892,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-23891","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>This post is part of a regular series of posts where the C++ product team and other guests answer questions we have received from customers. The questions can be about anything C++ related: MSVC toolset, the standard language and library, the C++ standards committee, isocpp.org, CppCon, etc. Today\u2019s post is by guest author Stephen Kelly, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/23891","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\/890"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=23891"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/23891\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/23892"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=23891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=23891"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=23891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}