How to call clang-format over a cpp project folder?
C++ClangClang FormatC++ Problem Overview
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.
C++ Solutions
Solution 1 - C++
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.
Solution 2 - C++
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).
Solution 3 - C++
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 \|
.
Solution 4 - C++
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.
Solution 5 - C++
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
Solution 6 - C++
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.
Solution 7 - C++
The below script and process:
- works in Linux
- should work on MacOS
- 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:
#!/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. My .clang-format
file is there too.
References:
- My repository:
- 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"). - Official
clang-format
documentation, setup, instructions, etc! https://clang.llvm.org/docs/ClangFormat.html - Download the
clang-format
auto-formatter/linter executable for Windows, or other installers/executables here: https://llvm.org/builds/ - Clang-Format Style Options: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
- [my answer] https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself/60157372#60157372
Related:
See also:
Solution 8 - C++
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"
Solution 9 - C++
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
Solution 10 - C++
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.
Solution 11 - C++
As @sbarzowski touches on in a comment above, in bash you can enable globstar which causes **
to expand recursively.
If you just want it for this one command you can do something like the following to format all .h
, .cc
and .cpp
files.
(shopt -s globstar; clang-format -i **/*.{h,cc,cpp})
Or you can add shopt -s globstar
to your .bashrc
and have **
goodness all the time in bash.
As a side note, you may want to use --dry-run
with clang-format the first time to be sure it's what you want.
Solution 12 - C++
You can use this inside a Make file. It uses git ls-files --exclude-standard
to get the list of the files, so that means untracked files are automatically skipped. It assumes that you have a .clang-tidy
file at your project root.
format:
ifeq ($(OS), Windows_NT)
pwsh -c '$$files=(git ls-files --exclude-standard); foreach ($$file in $$files) { if ((get-item $$file).Extension -in ".cpp", ".hpp", ".c", ".cc", ".cxx", ".hxx", ".ixx") { clang-format -i -style=file $$file } }'
else
git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx)$$' | xargs clang-format -i -style=file
endif
Run with make format
Notice that I escaped $
using $$
for make.
If you use go-task instead of make, you will need this:
format:
- |
{{if eq OS "windows"}}
powershell -c '$files=(git ls-files --exclude-standard); foreach ($file in $files) { if ((get-item $file).Extension -in ".cpp", ".hpp", ".c", ".cc", ".cxx", ".hxx", ".ixx") { clang-format -i -style=file $file } }'
{{else}}
git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx)$' | xargs clang-format -i -style=file
{{end}}
Run with task format
If you want to run the individual scripts, then use these
# powershell
$files=(git ls-files --exclude-standard); foreach ($file in $files) { if ((get-item $file).Extension -in ".cpp", ".hpp", ".c", ".cc", ".cxx", ".hxx", ".ixx") { clang-format -i -style=file $file } }
# bash
git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx)$' | xargs clang-format -i -style=file