自動ソースコードフォーマット

Python のソースコードは、Python 3.9.3 以降で Black を使用することで、自動的にフォーマットできます。

C++ のソースコードは、GROMACS 2020 以降、clang-format を使用して自動的にフォーマットできます。これにより、:doc: コードのフォーマットに関するガイドライン:doc: #include ディレクティブに関するガイドライン に記載されているガイドラインが自動的に適用されます。さらに、いくつかの他の自動フォーマット/チェックタスクには、別の Python スクリプトが使用されます。このページでは、clang-formatclang-tidy、および著作権関連スクリプトに関する詳細情報を提供します。

当社のCIでは、これらのスクリプト(特に``clang-format.sh``、copyright.sh``clang-tidy.sh``および``check-source``ターゲット)を使用して、コードが指定された形式で一貫性を保つようにしています。

clang-format の設定

GROMACS コードのフォーマットは、clang-format 18.1.8を使用して強制されます。`:command:`clang-format`は、主要な*clang*ツールの一つです。お好みのパッケージングシステムから*clang*または*llvm*パッケージに含まれている場合や、スタンドアロンの*clang-format*パッケージを見つけることができる場合がありますが、提供されているコマンドが正しいことを確認してください(他の18.1.xバージョンも使用できる場合があります)。例:

$ clang-format --version
clang-format version 18.1.8

もし、異なるバージョンの clang-format を使用した場合、GROMACS の継続的インテグレーションテストシステムとは異なるフォーマット結果が得られ、プッシュしたコミットが自動テストに失敗する可能性があります。

注釈

ソースとバイナリのダウンロードについては、`LLVM <http://releases.llvm.org/download.html#18.1.8>`を参照してください。ソースをダウンロードする場合は、*LLVMのソースコード*と*Clangのソースコード*の両方をダウンロードする必要があります。Clangの`INSTALL.txt <https://github.com/llvm/llvm-project/blob/release/18.x/clang/INSTALL.txt>`によると、展開したClangのソースコードを、展開したllvmアーカイブ内の`tools/clang`というサブディレクトリに配置し、その後、llvmソースディレクトリに対してCMakeを実行してください。

clang-format.sh および、事前コミットフックで使用するために、インストールされている clang-format を使用するには、以下のコマンドを各 GROMACS リポジトリで実行する必要があります。

git config hooks.clangformatpath /path/to/clang-format

alternativに、clang-format.sh を使用したい場合は、CLANG_FORMAT 環境変数を /path/to/clang-format に設定することで、設定できます。

プレコミットフックまたはGitフィルタを使用するには、追加の設定が必要です。詳細は、以下のセクションを参照してください。

clang-format は、プロジェクトディレクトリにある .clang-format 設定ファイルから、適用するフォーマットルールを自動的に検出します。これらの設定ファイルは、git pull コマンドを実行すると(必要な場合)、GROMACS リポジトリから自動的に更新されます。このツールと .clang-format 設定ファイルに関する詳細については、https://releases.llvm.org/18.1.8/tools/clang/docs/ClangFormat.html を参照してください。

自動的にフォーマットされるものは何ですか?

自動フォーマット対象のファイルを確認するために、スクリプトは .gitattributes ファイルに指定された Git フィルタを使用します。 以下の値のいずれかの属性 filter が設定されているファイルのみが処理されます。

  • filter=complete_formatting: 全てのフォーマットを実行します。コードのフォーマットには、clang-formatを使用します。

    ここに記載されているファイルも、clang-tidyによるコードチェックに渡されます。

  • filter=clangformat: clang-format が実行されます。また、clang-tidy も実行されます。

  • filter=includesort: ソート順が強制され、著作権ヘッダーがチェックされます。

  • filter=copyright: 著作権ヘッダーのみがチェックされます。

以下のスクリプト(clang-tidy.shclang-format.shcopyright.sh、および reformat_all.sh)は、その他のファイルを無視します(詳細は後述)。

clang-tidy の設定

GROMACS ソースコードの整理に関するチェックは、*clang*コンパイラバージョン18のclang-tidyを使用して行われます。`:command:`clang-tidy`は、*clang*の主要なツールの一つです。これは、お好みのパッケージングシステムから提供される*clang*または*llvm*パッケージに含まれているか、またはスタンドアロンの*clang-tidy*または*clang-tools*パッケージが見つかる場合があります。ただし、提供されているコマンドがバージョン18であることを確認する必要があります。例:

$ clang-tidy --version
  LLVM (http://llvm.org/):
    LLVM version 18.1.8

もし、異なるバージョンの clang-tidy を使用した場合、GROMACS の継続的インテグレーションテストシステムとは異なる結果が得られ、プッシュしたコミットが自動テストに失敗する可能性があります。

注釈

ソースとバイナリのダウンロードについては、`LLVM <https://releases.llvm.org/download.html#18.1.8>`を参照してください。ソースをダウンロードする場合、*LLVMのソースコード*と*Clangのソースコード*の両方をダウンロードする必要があります。Clangの`INSTALL.txt <https://github.com/llvm/llvm-project/blob/release/18.x/clang/INSTALL.txt>`によると、展開したClangのソースコードを、展開したLLVMアーカイブ内の`tools/clang`というサブディレクトリに配置し、その後、LLVMソースディレクトリに対してCMakeを実行してください。

clang-tidy.sh および事前コミットフックで使用するために、インストールされている clang-tidy のバージョンを使用するには、以下のコマンドを各 GROMACS リポジトリで実行する必要があります。

git config hooks.runclangtidypath /path/to/run-clang-tidy.py

あるいは、clang-tidy.sh を使用したいだけであれば、RUN_CLANG_TIDY 環境変数を /path/to/run-clang-tidy.py に設定することができます。

上記と同様に、事前コミットフックまたはGitフィルタを使用する方法については、以下のセクションを参照してください。

clang-tidy は、プロジェクトディレクトリにある .clang-tidy の構成ファイルから、適用するフォーマットルールを自動的に検出します。これらのルールは、git pull を実行したときに自動的に更新されます (必要な場合)。clang-tidy および .clang-tidy の構成ファイルに関する詳細は、https://releases.llvm.org/18.1.8/tools/clang/tools/extra/docs/clang-tidy/index.html を参照してください。

ツール

clang-format.sh

このスクリプトは、変更されたファイルに対して clang-format を実行し、結果を報告/適用します。 デフォルトでは、ソースブランチの現在の HEAD コミットがワークツリーと比較され、ファイルが

  1. これら2つのツリーの間には違いがあります。

  2. clang-format の設定を変更

は報告されます。この動作は、以下の方法で変更できます。

  1. --rev=REV オプションを指定すると、比較の基準として REV を使用します(通常は HEAD ではなく)。一般的な使用例としては、--rev=HEAD^ を指定して、HEAD のコミットを確認することです。

  2. アクションの指定:

    • check-*: clang-format によって変更されたファイルに関する情報を表示します

    • diff-*: 実際に変更される内容の差分を表示します

    • update-*: リポジトリへの変更を適用します

    • *-workdir: ワークディレクトリ(ディスク上のファイル)に対して操作を実行します

    • *-index: リポジトリのインデックスを操作します

    利便性のために、workdir/index というサフィックスを省略した場合、workdir がデフォルトで想定されます(つまり、diffdiff-workdir と同じ意味になります)。

  3. --format=off を指定すると、clang-format は実行されません。

デフォルトでは、update-* コマンドは、ディスクとインデックスで異なる(つまり、変更された)ファイルを更新しません。これにより、変更を簡単に取り消すことができます。この動作は、-f/--force オプションを追加することで上書きできます。

clang-format の動作は、同じオプションを使用した場合でも、バージョン間で異なる可能性があるため、Clang 18 の clang-format のみで正しい結果が得られます。 正しい clang-format 実行可能ファイルのパスは、 CLANG_FORMAT 環境変数または、リポジトリのルートで git config hooks.clangformatpath /path/to/clang-format-18 を実行することで指定できます。

clang-tidy.sh

このスクリプトは、変更されたファイルに対して clang-tidy ソースコードチェッカーを実行し、結果として発生した変更を報告するか、適用します。 デフォルトでは、ソースブランチの現在のHEADコミットがワークツリーと比較され、ファイルが

  1. これら2つのツリーの間には違いがあります。

  2. clang-tidy を適用する際に変更する点

は報告されます。この動作は、以下の方法で変更できます。

  1. --rev=REV オプションを指定すると、比較の基準として REV を使用します(通常は HEAD ではなく)。一般的な使用例としては、--rev=HEAD^ を指定して、HEAD のコミットを確認することです。

  2. アクションの指定:

    • check-*: clang-format によって変更されたファイルに関する情報を表示します

    • diff-*: 実際に変更される内容の差分を表示します

    • update-*: リポジトリへの変更を適用します

    • *-workdir: ワークディレクトリ(ディスク上のファイル)に対して操作を実行します

    • *-index: リポジトリのインデックスを操作します

    利便性のために、workdir/index というサフィックスを省略した場合、workdir がデフォルトで想定されます(つまり、diffdiff-workdir と同じ意味になります)。

  3. --tidy=off を指定すると、clang-tidy は実行されません。

デフォルトでは、update-* コマンドは、ディスクとインデックスで異なる(つまり、変更された)ファイルを更新しません。これにより、変更を簡単に取り消すことができます。この動作は、-f/--force オプションを追加することで上書きできます。

Black は、デフォルトで Python ファイルをその場で再フォーマットするツールです。リポジトリ全体を確認および更新するには、リポジトリのルートディレクトリにある .black.toml の構成ファイルを使用します。

pip install black
black --config .black.toml .

git の pre-commit フック

もし、変更を適用した際に copyright.shclang-tidy.sh、および/または clang-format.sh を自動的に実行したい場合は、 admin/git-pre-commit を使用して、コミット前のフックを設定できます。

  1. git-pre-commit スクリプトを .git/hooks/pre-commit にコピーします。

  2. もしすでに設定していない場合は、フックで run-clang-tidyclang-format のパスを指定してください:

    git config hooks.runclangtidypath /path/to/run-clang-tidy.py
    git config hooks.clangformatpath /path/to/clang-format
    
  3. 設定: フックの動作モードを設定する:

    git config hooks.clangtidymode check
    git config hooks.clangformatmode check
    git config hooks.copyrightmode  update
    

この設定では、コミットで変更されたすべてのソースファイルは、コードフォーマットツール、clang-tidy、および正しい著作権ヘッダーのチェックをパスします。 clang-tidy.shclang-format.sh、または copyright.sh によって変更されたファイルがある場合、そのファイル名は報告され、コミットは中止されます。 これらの問題は、スクリプトを手動で実行することで修正できます。

「pre-commit」ファイルを取り外さずに、フックを無効化するには、以下の設定を行います。

git config hooks.clangtidymode off
git config hooks.copyrightmode off
git config hooks.clangformatmode off

一時的にコミットに対して無効にするには、NO_FORMAT_CHECK 環境変数を設定します。たとえば:

NO_FORMAT_CHECK=1 git commit -a

また、git commit --no-verify を実行することも可能ですが、これにより他のフックも無効になります。

注意: git commit --amend を実行する際、フックは修正される変更のみに対して実行されます。つまり、全体のコミットに対しては実行されません。また、リベースの際にはフックは実行されません。

実際の作業は、admin/clang-tidy.shadmin/clang-format.sh、および admin/copyright.sh スクリプトによって行われ、これらは check-index アクションと --copyright および --format オプションが git config の設定に基づいて設定された状態で実行されます。

reformat_all.sh

このスクリプトは、ソースツリー内のすべての関連ファイルに対して、clang-formatcopyright.py、またはインクルードソーターを実行します。 実行方法については、reformat_all.sh -h を参照してください。

このスクリプトは、これらのコマンドが実行されるファイルのリストも生成できます。 これを行うには、コマンドラインで list-files を指定し、 --filter=<type> を使用して、どのコマンドのファイルリストを取得するかを指定します。 これを、例えば xargs と組み合わせて使用することで、同じセットのファイルに対して他のスクリプトを実行できます。

すべての操作において、さまざまなGitコマンドが受け入れる(例:src/*.cppsrc/ 以下のすべての .cpp ファイルを再帰的に一致させる)同じスタイルで定義されたパターンを適用することも可能です。 パターンは --pattern= オプションで指定し、複数の --pattern オプションを指定できます。

-f または --force を指定する必要があるのは、ワーキングツリーと Git インデックスが一致しない場合にのみです。

使用するGitフィルタ

変更時に自動的にclang-formatを適用するために、事前コミットフックを使用する代わりに、Gitフィルターを使用することもできます(どちらのスクリプトも不要で、``.gitattributes``ファイルのみが必要です)。 次を実行できます:

git config filter.clangformat.clean \
    "/path/to/clang-format -i"

すべてのファイルに対して、filter=complete_formatting 属性を指定し、すべてのフォーマット手順を実行するように設定するには、フィルタを設定します。

事前コミットフックとスクリプトを手動で実行することで、より良い/直感的な制御が可能になります(フィルターを使用すると、HEADとは異なるワークツリーを持ちながら、空の git diff を取得することも可能です)。また、多数のファイルを変更する変更に対して、より高いパフォーマンスを提供します。現在、この方法のみが著作権ヘッダーのチェックも行います。

このフィルターを使用すると、ソースチェッカーを通過していないブランチを、透明な形でマージできます。また、より一貫性を持って適用できます(例えば、リベースの際に、コミットごとに事前コミットフックが実行されません)。

git blame からフォーマットの変更を隠す

大規模なコードの再フォーマット、例えば、clang-formatの新しいバージョンに移行する場合、git blame / git praise の出力が解析しにくくなる可能性があります。なぜなら、多くの行が再フォーマットによって変更され、機能的な変更は発生しないからです。

「.git-blame-ignore-revs」ファイルには、このようなフォーマットのみのコミットを手動で管理するためのリストが保存されています。 リポジトリのルートで、次のコマンドを実行して、Gitに指定されたコミットを「スキップ」させ、代わりにその行の元のコミットを表示するように指示してください:

git config blame.ignoreRevsFile .git-blame-ignore-revs

このオプションを一時的に無効にするには、「git blame --ignore-revs-file=」を使用します(引数を指定しないでください)。