104
votes

Is there a way to call something like clang-format --style=Webkit for an entire cpp project folder, rather than running it separately for each file?

I am using clang-format.py and vim to do this, but I assume there is a way to apply this once.

10

10 Answers

55
votes

What about:

clang-format -i -style=WebKit *.cpp *.h

in the project folder. The -i option makes it inplace (by default formatted output is written to stdout).

106
votes

Unfortunately, there is no way to apply clang-format recursively. *.cpp will only match files in the current directory, not subdirectories. Even **/* doesn't work.

Luckily, there is a solution: grab all the file names with the find command and pipe them in. For example, if you want to format all .h and .cpp files in the directory foo/bar/ recursively, you can do

find foo/bar/ -iname *.h -o -iname *.cpp | xargs clang-format -i

See here for additional discussion.

34
votes

First create a .clang-format file if it doesn't exist:

clang-format -style=WebKit -dump-config > .clang-format

Choose whichever predefined style you like, or edit the resulting .clang-format file.

clang-format configurator is helpful.

Then run:

find . -regex '.*\.\(cpp\|hpp\|cc\|cxx\)' -exec clang-format -style=file -i {} \;

Other file extensions than cpp, hpp, cc and cxx can be used in the regular expression, just make sure to separate them with \|.

13
votes

I recently found a bash-script which does exactly what you need:

https://github.com/eklitzke/clang-format-all

This is a bash script that will run clang-format -i on your code.

Features:

  • Finds the right path to clang-format on Ubuntu/Debian, which encode the LLVM version in the clang-format filename
  • Fixes files recursively
  • Detects the most common file extensions used by C/C++ projects

On Windows, I used it successfully in Git Bash and WSL.

5
votes

For the Windows users: If you have Powershell 3.0 support, you can do:

Get-ChildItem -Path . -Directory -Recurse |
    foreach {
        cd $_.FullName
        &clang-format -i -style=WebKit *.cpp
    }

Note1: Use pushd . and popd if you want to have the same current directory before and after the script

Note2: The script operates in the current working directory

Note3: This can probably be written in a single line if that was really important to you

3
votes

The below script and process:

  1. works in Linux
  2. should work on MacOS
  3. works in Windows inside Git For Windows terminal with clang-format downloaded and installed.

Here's how I do it:

I create a run_clang_format.sh script and place it in the root of my project directory, then I run it from anywhere. Here's what it looks like:

run_clang_format.sh

#!/bin/bash

THIS_PATH="$(realpath "$0")"
THIS_DIR="$(dirname "$THIS_PATH")"

# Find all files in THIS_DIR which end in .ino, .cpp, etc., as specified
# in the regular expression just below
FILE_LIST="$(find "$THIS_DIR" | grep -E ".*(\.ino|\.cpp|\.c|\.h|\.hpp|\.hh)$")"

echo -e "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\""

# Format each file.
# - NB: do NOT put quotes around `$FILE_LIST` below or else the `clang-format` command will 
#   mistakenly see the entire blob of newline-separated file names as a SINGLE file name instead 
#   of as a new-line separated list of *many* file names!
clang-format --verbose -i --style=file $FILE_LIST

Using --style=file means that I must also have a custom .clang-format clang-format specifier file at this same level, which I do.

Now, make your newly-created run_clang_format.sh file executable:

chmod +x run_clang_format.sh

...and run it:

./run_clang_format.sh

Here's a sample run and output for me:

~/GS/dev/eRCaGuy_PPM_Writer$ ./run_clang-format.sh 
Files found to format = 
"""
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo/PPM_Writer_demo.ino
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo2/PPM_Writer_demo2.ino
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.h
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.cpp
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/timers/eRCaGuy_TimerCounterTimers.h
"""
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo/PPM_Writer_demo.ino
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo2/PPM_Writer_demo2.ino
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.h
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.cpp
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/timers/eRCaGuy_TimerCounterTimers.h

You can find my run_clang_format.sh file in my eRCaGuy_PPM_Writer repository, and in my eRCaGuy_CodeFormatter repository too.

References:

  1. My repository:
    1. eRCaGuy_PPM_Writer repo
    2. run_clang_format.sh file
  2. My notes on how to use clang-format in my "git & Linux cmds, help, tips & tricks - Gabriel.txt" doc in my eRCaGuy_dotfiles repo (search the document for "clang-format").
  3. Official clang-format documentation, setup, instructions, etc! https://clang.llvm.org/docs/ClangFormat.html
  4. Download the clang-format auto-formatter/linter executable for Windows, or other installers/executables here: https://llvm.org/builds/
  5. Clang-Format Style Options: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
  6. [my answer] How can I get the source directory of a Bash script from within the script itself?

Related:

  1. [my answer] Indenting preprocessor directives with clang-format

See also:

  1. [my answer] Fixing a simple C code without the coments
2
votes

When you use Windows (CMD) but don't want to use the PowerShell cannon to shoot this fly, try this:

for /r %t in (*.cpp *.h) do clang-format -i -style=WebKit "%t"

Don't forget to duplicate the two %s if in a cmd script.

1
votes

I'm using the following command to format all objective-C files under the current folder recursively:

$ find . -name "*.m" -o -name "*.h" | sed 's| |\\ |g' | xargs clang-format -i

I've defined the following alias in my .bash_profile to make things easier:

# Format objC files (*.h and *.m) under the current folder, recursively
alias clang-format-all="find . -name \"*.m\" -o -name \"*.h\" | sed 's| |\\ |g' | xargs clang-format -i"
1
votes

Here is a solution that searches recursively and pipes all files to clang-format as a file list in one command. It also excludes the "build" directory (I use CMake), but you can just omit the "grep" step to remove that.

shopt -s globstar extglob failglob && ls **/*.@(h|hpp|hxx|c|cpp|cxx) | grep -v build | tr '\n' ' ' | xargs clang-format -i
1
votes

In modern bash you can recursively crawl the file tree

for file_name in ./src/**/*.{cpp,h,hpp}; do
    if [ -f "$file_name" ]; then
        printf '%s\n' "$file_name"
        clang-format -i $file_name
    fi
done

Here the source is assumed to be located in ./src and the .clang-format contains the formatting information.