Skip to content

Clang Format

clang-format is a set of tools to format your codebase based on given styles (or styles created by the user in a config file). It can be used for the following languages: C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C#. It is a project of LLVM. More in depth information about the actual documentation can be found here.

In this project, we have implemented two ways to format C++ code. One way is a manual implementation with a bash script, the other is automatically on git commits using git hooks.

Format using a bash script

This approach involves a "manual" formatting step. The root directory of this project contains a bash script clang-format.sh which contains commands designed to automatically format each .cpp and .h file located in the root directory of the repository. Since clang-format lacks inherent recursive capabilities, it becomes necessary to identify each file and apply formatting individually.

1. Finding the root directory

The first command in clang-format.sh finds the root directory and navigates to it using the following command:

cd -- "$(find ~/ -type d -name ReCoDE-SPH-solver-2D-NS | head -1)"

This command looks for a directory (-type d) with a name matching that of the repository (-name ReCoDE-SPH-solver-2D-NS). It is important to note that, should the repository have a different name, the value of this flag should be adjusted accordingly. Subsequently, we capture the output of the find command and pipe(|) it to head, which extracts the first(-1) line (a precaution in case the specified name appears multiple times on the host machine). The outcome of this combined effort is then passed to cd to navigate to the correct location.

2. Choosing the Style Guide

The style guide defines the rules clang-format will use to format your code. The default style guide is llvm, but we can choose any other pre-defined style guide, or we could define our own. Here we choose the pre-defined googlestyle.

if ! [ -f "$config_file" ]; then
    clang-format --style=google --dump-config > "$config_file"
fi

This command searches the current directory for the specified config file and, in the case it's not present, creates it on the spot to be used by clang-format.

3. Format CPP and header files

The final command performs the code formatting for every CPP and header file.

find . \( -name '*.cpp' -o -name '*.h' \) -exec clang-format -i {} +

This command finds every .cpp and .h file inside the current directory and passes them to clang-format to format them. Should the find command locate any CPP or header files, formatting will be applied to these files. In the absence of matching files, the command won't encounter an error, but it's essential to include the + symbol in the find command. This ensures that even if no files are found, an empty string is passed to clang-format as expected.

Usage

In order to use the formatter, you need to run the following command in the terminal in the root directory of the project:

./clang-format.sh

This runs the script. The formatter then proceeds to format all the detected files using the style rules provided in the clang configuration file. If the user wants to change the style of the formatting applied, they need to edit clang-format.sh to change the --style flag's value to the desired style guide for clang follow. Some suggestions can be found here.

Format on commit

Another approach we could take is to automate the running of clang. We do this by using the pre-commit Python package to automate the process of formatting our staged files on every git commit. This way of code formatting is independent of the manual way and you don't need to use both to format your code or for them to work properly.

There is a configuration file for pre-commit (.pre-commit-config.yaml), in which a pre-commit git hook is specified. To install this hook to a local git repository the following command is required:

pre-commit install

Following this, every time the git commit command is executed, any "bad formatted" staged files are detected and formatted by pre-commit, based on the clang-format configuration that has been specified. Moreover, the commit fails with a message that prompts the user to stage the files that were formatted by the package and make a new commit. In the case that all the staged files are formatted according to the specified style rules, the commit will succeed with an appropriate message.

More information on the pre-commit package can be found here.

Format Checking Pipeline

Furthermore, we have implemented a final, holistic check of our code's format. Using a Github Actions workflow (defined in format.yml), we have developed a pipeline that is triggered every time a git push happens or a Pull Request (PR) is opened.

The pipeline checks all the .cpp and .h files in the repository, using the clang-format Github Action defined here. If all the files are formatted based on the clang-format configuration that has been specified, the pipeline succeeds, else it fails. This pipeline, combined with the appropriate branch rules, guarantees that our Github repository is always well-formatted.