So you want to write a Clang plugin but you’re not sure where to start? Well this is kind of the blog post for you. I say “kind of” because I can’t tell you exactly how to write the Clang plugin you want to write, but I can give you a nudge in the right direction. More importantly, I can show you a thing or two about getting your code built, linked with the Clang library, and loaded into the compiler without dealing with the abject horror, misery, and despair that is building all of LLVM and Clang from source.

Now, this kind of thing might be obvious for some people, but it wasn’t for me! When I was looking at the example plugins in the Clang repository, I saw that they all link with the in-tree build of Clang and LLVM. So, obviously, I spent the next four years of my life waiting for the LLVM and Clang source to build on my machine. I’m here to tell you that it doesn’t need to be like that.

Prerequisites

As of this writing, I’m using Ubuntu 18.10, which means I get all the hip, cutting edge, updated tools that the cool kids are using.

Required packages:

  • build-essential – You probably already have this! :-)
  • cmake – I’m using version 3.12. I know that 3.10 is too old for CMake to find the Clang development libraries on its own, I can’t speak to 3.11. There might be a FindClang.cmake script out there that works, but the one I found didn’t work for my friends running older an older version CMake. Your mileage may vary.
  • clang
  • libclang-dev – This is what we are going to link to for our plugin.

Setting up the project

Now, let’s adapt the PrintFunctionNames example plugin that lives in the Clang source tree. Go ahead and just download PrintFunctionNames.cpp into a nice “project” folder on your computer.

We need to build the plugin as a shared library since the Clang compiler will load the library at runtime.

Make a file called CMakeLists.txt in the same project folder that the plugin code resides in and get ready for some typing.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.12)

find_package(Clang REQUIRED)

add_library(PrintFunctionNames SHARED PrintFunctionNames.cpp)
target_include_directories(PrintFunctionNames PUBLIC ${CLANG_INCLUDE_DIRS})

set_target_properties(PrintFunctionNames PROPERTIES
    LINKER_LANGUAGE CXX)

But what does it mean?

cmake_minimum_required(VERSION 3.12) – Tells CMake that it needs to be at least version 3.12. Any earlier versions have to kick rocks because we don’t think they have the correct features to correctly generate a Makefile from this script.

find_package(Clang REQUIRED) – Instructs CMake to find libClang as our project depends on it.

add_library(PrintFunctionNames SHARED PrintFunctionNames.cpp) – Tells CMake that we will be compiling a shared library. PrintFunctionNames.cpp is our source code. If we had a larger plugin with multiple files, it is likely they would all be listed here.

target_include_directories(PrintFunctionNames PUBLIC ${CLANG_INCLUDE_DIRS}) – This tells CMake where to find Clang’s header files.

set_target_properties(PrintFunctionNames PROPERTIES LINKER_LANGUAGE CXX) – Reminds CMake (in case it didn’t already know) that our project is in C++.

Building the plugin

Let’s try building it! I always do an “out of tree” build (meaning our build files are placed in a different directory than our source code).

Inside our project directory which has CMakeLists.txt and PrintFunctionNames.cpp:

  1. mkdir build
  2. cd build
  3. cmake .. (If you get an error here, retrace your steps, make sure your CMake is a high enough version, make sure you have libclang-dev installed)
  4. make

Running the plugin

The plugin has been built and we’re ready to go! I created a small main.cpp in the project folder that just has a bunch of functions defined in it.

I ran the Clang compiler (with the plugin) like this:

clang -Xclang -load -Xclang build/libPrintFunctionNames.so -Xclang -add-plugin -Xclang print-fns main.cpp -o main

The output:

top-level-decl: "func1"
top-level-decl: "boolFunc"
top-level-decl: "main"

Conclusion

Yay! You’ll notice that you didn’t spend 3-7 hours generating 50GB of build artifacts while you waited for Clang and LLVM to build.

Remarks

The Clang Plugin API is allegedly unstable, which is why I presume the examples in the repositories are built directly on top of the up-to-date source (so that they are always up to date in the event of breaking changes).

Indeed, you might find articles such as Don’t write a clang plugin which warn you that breaking changes are abound. Or, you might notice that the word “stable” (and “unstable”, for that matter) are nowhere near the description of the Clang Plugin interface description.

I just want to share the fact that the git logs show that PrintFunctionNames.cpphasn’t been updated in 2 years which means that this plugin hasn’t experienced any breaking changes across Clang versions:

  • 3.9.1
  • 4.0.0
  • 4.0.1
  • 5.0.0
  • 5.0.1
  • 6.0.0
  • 7.0.0
  • 7.0.1

I don’t mean to belabor a point here, or to call out the author of an article (in fact, I don’t know for sure if they’re maintaining their own fork which may indeed carry many breaking changes). This was a point of discussion for my capstone team and I thought it deserved some notes here.