Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ take a look at this [page](https://github.com/IPPL-framework/ippl/blob/master/UN
# CI/CD and PR testing
Please see [Julich CI results](https://ippl-bc4558.pages.jsc.fz-juelich.de/) and [CSCS PR testing](ci/cscs/cscs-ci-cd.md) for further information.

# Code Formatting
Please see [Code Formatting Setup](doc/extras/CodeFormattingSetup.md) for further information and instructions.

# Citing IPPL

```
Expand Down
2 changes: 1 addition & 1 deletion ci/cscs/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ include:
- echo "CI_PROJECT_URL=$CI_PROJECT_URL"
- echo "CI_COMMIT_SHORT_SHA=$CI_COMMIT_SHORT_SHA"
- |
if [[ "$CI_COMMIT_REF_NAME" =~ gh-readonly-queue.*-pr-([0-9]+)- ]]; then
if [[ "$CI_COMMIT_REF_NAME" =~ gh-readonly-queue/.*/pr-([0-9]+)- ]]; then
export CDASH_LABEL="Merge-Queue-PR-${BASH_REMATCH[1]}"
elif [[ "$CI_COMMIT_REF_NAME" =~ pr([0-9]+)$ ]]; then
export CDASH_LABEL="PR-${BASH_REMATCH[1]}"
Expand Down
182 changes: 182 additions & 0 deletions doc/extras/CodeFormattingSetup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Code Formatting Setup Guide

This guide explains how to set up automatic code formatting for IPPL development using the pre-commit git hook.

## Overview

IPPL uses two automatic formatters:
- **clang-format-18**: For C/C++ source files (`.c`, `.cc`, `.cpp`, `.cxx`, `.h`, `.hpp`, etc.)
- **cmake-format**: For CMake configuration files (`CMakeLists.txt`, `*.cmake`)

The pre-commit hook runs these formatters automatically before each commit, ensuring all committed code adheres to the project's styling conventions.

## 1. Installing clang-format-18

### Ubuntu/Debian

Add the LLVM repository and install clang-format-18:

```bash
# Add LLVM repository (Ubuntu 20.04+, Debian 11+)
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-18 main"
sudo apt update

# Install clang-format-18
sudo apt install clang-format-18
```

Verify the installation:

```bash
clang-format-18 --version
```

Note: If you have `clang-format` installed but it doesn't use the full name `clang-format-18`, then just create a symlink in your `.local` path that redirects `clang-format-18` to your real `clang-format` executable

### Other Systems

For other operating systems, visit the [LLVM download page](https://releases.llvm.org/) or use your system's package manager to locate `clang-format` version 18 or later.

## 2. Installing cmake-format

cmake-format is a Python package available via pip:

```bash
# Install cmake-format (Python 3.x required)
pip install cmake-format
```

Verify the installation:

```bash
cmake-format --version
```

If you prefer to install it system-wide or in a virtual environment, refer to the [cmake-format documentation](https://cmake-format.readthedocs.io/).

## 3. Setting Up the Pre-Commit Hook

### Basic Setup

The repository includes the git hook script. From the repository root, setup a symlink to the git hook (in the .git/hooks dir):

```bash
cd /path/to/ippl
ln -s -f ../../scripts/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```
(note that the extra ../../ in the path is because git runs the hook from the .git/hooks directory, so the symlink has to be redirected)

Alternatively, create a symbolic link manually directly from the git hooks dir:

```bash
cd /path/to/ippl/.git/hooks
ln -s -f ../../scripts/pre-commit pre-commit
chmod +x pre-commit
```

### Verification

To verify the hook is installed, check that the symlink exists:

```bash
ls -la /path/to/ippl/.git/hooks/pre-commit
```

You should see output similar to:

```
lrwxrwxrwx 1 user group 23 Mar 4 12:00 pre-commit -> ../../scripts/pre-commit
```

## 4. How the Hook Works

When you run `git commit`:

1. The pre-commit hook identifies all staged C/C++ and CMake files
2. For each file, it checks if formatting is needed by comparing the staged content with the formatted version
3. If formatting is needed, the hook:
- Reports which files need reformatting
- Displays the command to fix each file
- Prevents the commit from proceeding
4. Run the suggested command and re-stage the files
5. Retry `git commit`

Note that we include the command-line parameter `-style=file` in all clang-format commands mentioned here, but this is actually the default and skipping it should not cause problems. Providing there is a `.clang-format` file present in the repository root, all should work as expected.

### Example Workflow

```bash
# Make changes and stage them
git add src/myfile.cpp

# Try to commit
git commit -m "Add feature"

# If formatting is needed, you'll see:
# clang-format-18 -style=file -i src/myfile.cpp
# Then re-stage and retry
git add src/myfile.cpp
git commit -m "Add feature"
```

## 5. Bypassing the Hook

If you need to commit without running the hook (not recommended), use:

```bash
git commit --no-verify
```

**Warning**: Code committed without formatting may not adhere to project standards.

## 6. Manual Formatting

To format files without committing:

```bash
# Format a single C/C++ file
clang-format-18 -style=file -i src/myfile.cpp

# Format all C/C++ files in a directory
find src -type f \( -name "*.cpp" -o -name "*.hpp" \) -exec clang-format-18 -style=file -i {} \;

# Format CMake files
cmake-format -c /path/to/ippl/.cmake-format.py -i CMakeLists.txt
```

## 7. Troubleshooting

### Hook not executing?

- Verify the script is executable: `chmod +x .git/hooks/pre-commit`
- Verify the symlink points to the correct location: `ls -la .git/hooks/pre-commit`
- Ensure `clang-format-18` and `cmake-format` are in your `$PATH`: `which clang-format-18 cmake-format`

### Formatting succeeds locally but hook still rejects?

This is typically due to:
- Different `clang-format-18` versions installed (ensure you have version 18)
- Nested `.clang-format` configuration files in subdirectories that override the root config

### Path issues on HPC systems?

If you're on a shared HPC system where multiple compiler modules are available:
- Load the correct module before working: `module load llvm/18` or similar
- Create a symlink to `clang-format` in `~/.local/bin` if module loading is inconvenient

## 8. Configuration Files

The formatting behavior is controlled by:

- **C/C++ formatting**: `.clang-format` (repository root and subdirectories)
- **CMake formatting**: `.cmake-format.py` (repository root)

These files define all style rules. For detailed customization, refer to:
- [clang-format documentation](https://clang.llvm.org/docs/ClangFormatStyleOptions.html)
- [cmake-format documentation](https://cmake-format.readthedocs.io/)

## Questions?

Refer to the main [WORKFLOW.md](../WORKFLOW.md) guide for additional context on IPPL's development practices.
73 changes: 73 additions & 0 deletions scripts/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/bin/bash

# ----------------------------------------------------------------
# simple pre commit hook script to check that C/C++ source files
# (*.c, *.cpp, *.hpp, *.h, *.tpp, etc.) are correctly clang-formatted
# and that CMakeLists.txt and *.cmake files are cmake-formatted

# To use this hook, you must have clang-format-18 and cmake-format
# installed on your system. See doc/extras/CodeFormattingSetup.md
# for setup instructions.

# To install this hook, symlink this hook to your git hooks as follows
# (note that the extra ../../ in the path is because git runs the hook
# from the .git/hooks directory, so the symlink has to be redirected)
# ln -s -f ../../scripts/pre-commit .git/hooks/pre-commit

CLANG_FORMAT_VERSION=clang-format-18

red=$(tput setaf 1)
green=$(tput setaf 2)
yellow=$(tput setaf 3)
blue=$(tput setaf 4)
normal=$(tput sgr0)

cxx_match="c|cc|cp|cxx|cpp|c\+\+|h|hh|hpp|hxx|h\+\+|ipp|tpp|tcc|txx|inl|inc"
cmake_match="CMakeLists\.txt|\.cmake"

cxxfiles=()
for file in `git diff --cached --name-only --diff-filter=ACMRT | grep -E "\.(${cxx_match})$"`; do
if ! cmp -s <(git show :${file}) <(git show :${file}|$CLANG_FORMAT_VERSION -style=file --assume-filename="${file}"); then
cxxfiles+=("${file}")
fi
done

cmakefiles=()
for file in `git diff --cached --name-only --diff-filter=ACMRT | grep -E "(${cmake_match})$"`; do
tmpfile=$(mktemp /tmp/cmake-check.XXXXXX)
git show :${file} > $tmpfile
cmake-format -c $(pwd)/.cmake-format.py -i $tmpfile
if ! cmp -s <(git show :${file}) <(cat $tmpfile); then
cmakefiles+=("${file}")
fi
rm $tmpfile
done

returncode=0
full_list=

if [ -n "${cxxfiles}" ]; then
printf "# ${blue}clang-format ${red}error pre-commit${normal} : To fix run the following (use git commit ${yellow}--no-verify${normal} to bypass)\n"
for f in "${cxxfiles[@]}" ; do
rel=$(realpath --relative-to "./$GIT_PREFIX" "$f")
printf "$CLANG_FORMAT_VERSION -style=file -i %s\n" "$rel"
full_list="${rel} ${full_list}"
done
returncode=1
fi

if [ -n "${cmakefiles}" ]; then
printf "# ${green}cmake-format ${red}error pre-commit${normal} : To fix run the following (use git commit ${yellow}--no-verify${normal} to bypass)\n"
for f in "${cmakefiles[@]}" ; do
rel=$(realpath --relative-to "./$GIT_PREFIX" "$f")
printf "cmake-format -i %s\n" "$rel"
full_list="${rel} ${full_list}"
done
returncode=1
fi

if [ ! -z "$full_list" ]; then
printf "\n# ${red}To commit the corrected files, run\n${normal}\ngit add ${full_list}\n"
fi

exit $returncode