web-dev-qa-db-ja.com

clang-formatは、フォーマットの変更が必要かどうかを教えてくれますか?

ファイルが指定された形式を満たしているかどうかを報告するモードでclang-formatを実行できる方法はありますか?変更が必要かどうかを報告するが、変更を行わない一種の予行モード。ファイルの変更が必要な場合、clang-formatがゼロ以外の終了コードを返すようにすることが理想です。または、さらに理想的には、ゼロ以外の終了コードと、標準出力の変更が必要なファイルのリスト。

より多くの人が答えられるように、私は質問を一般的なものにしようとしていますが、私がやろうとしているのは、予想される.clang-formatに一致しないコミットを拒否するgit pre-commitフックを書くことです。インデックス内のファイルのリストに対してclang-formatを実行するのは簡単です。しかし、clang-formatが実際に何かを変更したかどうかを知ることは困難です。

-output-replacements-xml(回答として投稿します)に基づいた1つの潜在的なソリューションがありますが、それはハックであり、これはもっと簡単なはずだと感じています。コメント/提案、編集、さまざまな回答/アプローチはすべて歓迎します。

61
David Ogren

run-clang-format は、フックまたは継続的統合スクリプトとして使用するために正確に設計されたclang-formatの単純なラッパーです。diffを出力し、適切なステータスで終了します。

ホームページにある例は、それ自体を物語っています:

run-clang-format example

7
Matthieu Moy

-output-replacements-xmlが基本的に必要な答えを提供してくれるだけでなく、使いやすい方法でそれを提供してくれるからです。ただし、置換が不要な場合の出力は非常に予測可能であるため、出力の解析はそれほど難しくありません。

私が今持っているのは

clang-format -style=file -output-replacements-xml | grep -c "<replacement " >/dev/null

Grepは何かが一致すると0を返し、何も一致しないと1を返すため、これは実際に必要な終了コードの逆を返します。しかし、それは対処するのに十分簡単です。

したがって、私のgit pre-commitフックの関連ビットは

git diff --cached --name-only --diff-filter=ACMRT |
  grep "\.[cmh]$" |
  xargs -n1 clang-format -style=file -output-replacements-xml |
  grep "<replacement " >/dev/null
if [ $? -ne 1 ]; then 
    echo "Commit did not match clang-format"
    exit 1
fi
  1. インデックス内のファイルの完全なファイル名を取得します(削除されているファイルや、ファイルを処理したくないかもしれないその他の異常なケースを除きます)
  2. フォーマットをチェックしたいもののファイル名のみを保持します(私の場合はc、m、hファイルのみ)
  3. Xargsを使用して結果を実行し、基本的に「for each」次のコマンドを実行します
  4. すべてのファイルで-output-replacements-xmlオプションを使用してclang-formatを実行します
  5. Clang-formatが作成したい置換を見つけたことを示す置換(置換ではなく)を検索します。 (すべての出力をXMLとして破棄することは、ユーザーにとって意味がありません。)
  6. 最後のコマンドは1を終了します(grepは何も見つからなかったと言います)完了し、問題はありません。
  7. そうでない場合は、メッセージを表示して1を終了し、コミットをキャンセルします。残念ながら、どのファイルが問題だったかをユーザーに伝える簡単な方法はありませんが、ユーザーは自分でclang-formatを実行して見ることができます。
40
David Ogren

あなたのユースケースが何であるかは完全にはわかりませんが、git-clang-format( https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/git- clang-format )。それは基本的にgitのclang形式の統合を提供し、おそらくあなたが探しているものです。

4
djasper

この投稿のphsからのコメント を少し調整しました:

find embedded/ -regex '.*\.\(ino\|cpp\|hpp\|cc\|cxx\|h\)' -exec cat {} \; | diff -u <(find embedded/ -regex '.*\.\(ino\|cpp\|hpp\|cc\|cxx\|h\)' -exec clang-format-3.9 -style=file {} \;) -

あれは..

  1. catすべてのcpp-ishファイルとdiffへのパイプ(最後に-を指定するため、diffstdinを受け入れます)
  2. プロセス置換(<( .. )構文)を使用して、それらの同じファイルでclang-formatを実行します。ここではインプレースフォーマットを使用しないでください。これは、diffに送信される残りの半分です。
  3. diffが出力なしで終了した場合、成功です! $?経由で終了コードを確認することもできます-ゼロでなければなりません。

CIサービス(travis)でbashスクリプトのこの行を実行して、適切にフォーマットされていることを確認します。実際にフォーマッタをインプレースで実行するための別のスクリプトがあります。これは、警告を思い出させます。サブ処理を実行できるシェルを使用する必要があります( posix Shellはそうではありません )。

2
Matt

git-clang-formatと、Mike Rhodesのブログの事前コミットスクリプトを使用します。

#!/bin/python

import subprocess
output = subprocess.check_output(["git", "clang-format", "--diff"])

if output not in ['no modified files to format\n', 'clang-format did not modify any files\n']:
    print "Run git clang-format, then commit.\n"
    exit(1)
else:
    exit(0)

このスクリプトには小さなエラーがあり、コミットがない場合は動作しません(HEADこれはまだ存在していません)。これをバイパスするには、-nまたは--no-verifyオプション。

-nを使用してpre-commitスクリプトをスキップすると、大規模なコードベースでは時間がかかるため、チェックをバイパスするときに役立ちます。

元の投稿はこちら: http://www.dx13.co.uk/articles/2015/4/3/Setting-up-git-clang-format.html

1
Daniel

David Ogrenの投稿 に触発されてから、段階的な変更に対応できるpre-commitフックを作成しました。これにより、pre-commitフックが実際にコミットの内容を構成するコードで動作し、ステージングされなかったclang-format実行にだまされないようになります。

#!/bin/bash

files=()
for file in `git diff --cached --name-only --diff-filter=ACMRT | grep -E "\.(cpp|hpp)$"`; do
  if ! cmp -s <(git show :${file}) <(git show :${file}|clang-format); then
    files+=("${file}")
  fi
done

if [ -n "${files}" ]; then
echo Format error within the following files:
printf "%s\n" "${files[@]}"
exit 1
fi
0
Martin