Exploring clang-format
Why use a formatting tool
My goal to forget about exactly what formatting I should be using, but still produce consistent easy to read code. Go achieves this by shipping each developer gofmt and providing good tooling to make it usable in all editors. My hope is to achieve something similar with clang-format (or maybe a different tool).
How sane an idea is this? Many parts of Chrome requires all new changes to be formatted with clang-format, and some have done a clang-format pass over their section of the tree. If you are curious you can see the Chrome developers debate the merits of clang-format here. clang-format was developed to auto-reformat xcode after large tool based refactorings.
Installation
I am still running on Ubuntu 12.04, so install is a little more difficult than 14.04, which ships Clang 3.5 in it’s repos. The upside is that this is the install process needed to install Clang 3.6 or Clang 3.7.
Installing libstdc++ 4.8
Clang uses libstdc++ for it’s C++ library on linux and we need a newer one than what ships with 12.04, so clang can provide support for the latest C++11 and C+14 library features. To do that we using the ubuntu tool chain team’s PPA:
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
Installing clang 3.5
These steps are based on the LLVM apt page install instructions.
First create an apt config file: /etc/apt/sources.list.d/llvm-clang.list
with these lines:
deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main
deb-src http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main
Then add the key so we can authenticate the packages (just ignore the key not being served over https):
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
Then we install the package. Because of a some weird package dependency issue we need to install all the dependencies manually in one go. This command did it for me:
sudo apt-get install clang-3.5 clang-3.5-doc libclang-common-3.5-dev libclang-3.5-dev libclang1-3.5 libclang1-3.5-dbg libllvm3.5 libllvm3.5-dbg llvm-3.5 llvm-3.5-dev llvm-3.5-doc llvm-3.5-examples llvm-3.5-runtime clang-modernize-3.5 clang-format-3.5 python-clang-3.5
This was about 400MB of archives, including all the gcc libraries. All I can say is: thank you FIOS, and it was faster then building clang & llvm from scratch.
Exploring the format options
The default mode of clang format is to just dump to stdout the newly formed code, so:
clang-format-3.5 file_name.cpp
Will produce what is hopefully beautifully formatted text on stdout. That will only be true if you like the LLVM coding style. The good news is that clang-format has pretty good suite of configuration options.
Since clang-format is not supported by UniversalIndentGUI I created a simple python script that uses meld to compare a file before and after formatting. This allows for faster iteration, especially if you are trying to match an existing style:
I started by tweaking the configuration options with the -style
flag. I
used the clang docs and this helpful
online website to learn about what settings were
available. Here is an example:
clang-format-3.5 file_name.cpp -style="{BasedOnStyle: Google, IndentWidth: 4, PointerAlignment: Right}"
After a little bit of tweaking it becomes more practical to use the YAML based .clant-format config file. If you set “-style=file” then clang-format starts in the directory containing your source file, and walks up the tree using the the first .clang-format it finds. To get started with that file, you can export your current style with the “-dump-config” option.
clang-format-3.5 -style="{BasedOnStyle: Google, IndentWidth: 4, PointerAlignment: Right}" -dump-config
Issues with clang-format
My biggest issue so far is that it appears BreakBeforeBinaryOperators doesn’t appear to work in every case you would want. If you have some code with a conditional check on each line:
bool test_string(const std::string& incoming_str)
{
return incoming_str == "Jim" ||
incoming_str == "Bob" ||
incoming_str == "Joe" ||
incoming_str == "Nancy" ||
incoming_str == "Jane" ||
incoming_str == "Jenny";
}
It wants to collapse this onto one line, which is both less readable and less maintainable.
bool test_string(const std::string &incoming_str)
{
return incoming_str == "Jim" || incoming_str == "Bob"
|| incoming_str == "Joe" || incoming_str == "Nancy"
|| incoming_str == "Jane" || incoming_str == "Jenny";
}
Hopefully this is fixed in a newer version of clang, but I have not had the time to look.
In editor integration
When I use gofmt (through goimports) I have emacs run it every time I save the file. It is a little magical, I can write code without futzing with exactly alignment of statements or brace placement, but in the end everything is consistent. On save everything is fixed, no extra command to run, or more importantly to forget to run.
So that is our goal, run a command which does a clang-format pass on a file every time you save, in as many editors as possible. I started by looking at the chromium clang format docs, but they were not complete.
emacs
Emacs is the my main editor, so it’s the first I got format on save working in. The first step in emacs is install clang-format.el, either manually or with MELPA:
M-x package-install clang-format
Then you need to install a hook that runs on save so you can call clang format. Here is the one I created, along with an example of it’s use:
;; Hook function
(defun clang-format-before-save ()
"Add this to .emacs to clang-format on save
(add-hook 'before-save-hook 'clang-format-before-save)."
(interactive)
(when (eq major-mode 'c++-mode) (clang-format-buffer)))
;; Install hook to use clang-format on save
(add-hook 'before-save-hook 'clang-format-before-save)
Vim
I am not a Vim user so have not tried any of this out, but hopefully this research will be helpful. The vim-clang-format plugin for Vim provides format on save support but it’s not widely used so I don’t know how well it works.
Another option is to roll something yourself. LLVM clang-format.py allows you to format a file when you execute a certain key stroke. According to stackoverflow you should be able to build on that to run to setup a command that runs on save. The result might look something like this totally untested line of vim script:
autocmd FileType c,cpp autocmd BufWritePre <buffer> :pyf clang-format.py
Eclipse
I not currently and Eclipse CDT user either, but I did install it to see if
I could get format on save working. There is no built in way to do this,
but you can use the CppStyle plugin. It supports both Ctrl-Shift-F
shortcut and formatting on save. My thanks to the author for implementing
format on save after I requested it.
In general the sluggishness of Eclipse is what turns me down. Even with the latest version, Luna, running on Oracle Java 8 is still slow. It was slow on my 1.6 GHz Pentium M laptop 8 years ago, and it’s still slow on my 3 GHz quad core desktop today.
CLion
CLion is the new C/C++ IDE from JetBrains makers of PyCharm and Intellij iDEA. This IDE shows promise because it’s cross platform and the JetBrains appears to produce more polished products than the people behind Eclipse. Eventually it will be able to available as a plugin so you can run it alongside their other IDE’s like PyCharm.
The most basic use case is just being able to run clang-format from inside
the IDE the current file. To do that you follow these docs
to set clang-format up as an external tool. When setting up the tool set
the parameters field to “-i -style=file $FilePath$
”. To bind a tool to
that keyboard shortcut you can use the keymap panel in the settings, see
the docs here for more details.
Getting all of this setup to work on save is slightly more complicated, so I will just link to Marc Esher’s blog post on how he did it for goimports. The gist of it is he creates a macro that replaces the save shortcut. That macro uses the external tool to format the code, then runs the normal save command.
VCS Integration
So once you have decided to use clang-format or similar tools, how do you “help” (ie. impose your will) other developers remember to use these tools? The best option I have seen is using a pre-commit hook that ensures all changed lines have been run through clang-format. LLVM includes a helpful script called git-clang-format. It will apply clang-format to only the changed lines of a git commit. Running it is as simple as:
git clang-format
On projects like webkit and Chrome in their submission workflow they have both tree wide and directory by directory ability to auto-reject changes that differ after running git-clang-format or other lint like tools. If you are curious you can learn more about the Chrome workflow scripts here. Their system allows owners of each section of the source tree to adapt checks as they see fit.
Webkit on the other hand takes a more holistic approach and forces all code to comply with their check-webkit-style tool. Under the hood is a ton of python code that does hand written checks for everything from C++ to CMake. This is quite similar to google’s cpplint.py tool, also used by the Chrome team.