VSBT (Visual Studio Build Tools) 環境で、Enterprise ツールを使わない場合の選択肢を整理します。
Windows 向けのオープンソースカバレッジツールです。gcov に最も近い使い勝手で、MSVC でビルドしたバイナリに対応しています。
特徴
導入方法
choco install opencppcoverage関連リンク: OpenCppCoverage GitHub
BullseyeCoverage
商用ツールですが、MSVC に対応しており、ソースコードのインストルメンテーション (コードに計測用の処理を埋め込むこと) を行ってカバレッジを取得します。ライセンス費用が発生します。
gcov + MinGW-w64
MSVC ではなく MinGW-w64 の GCC を使う選択肢もあります。この場合は gcov がそのまま使えますが、MSVC でのビルドが要件の場合は選択できません。
VSInstr.exe や VSPerfCmd.exe は Visual Studio に付属していますが、これらは主にプロファイリング用途であり、Code Coverage 機能自体は Enterprise エディション専用です。Build Tools だけでは完全なカバレッジレポート生成は難しいです。
OpenCppCoverage は名前に “Cpp” と入っていますが、C言語と C++ の両方に対応しています。MSVC でコンパイルされた C言語のプログラムでもカバレッジを取得できます。
cl.exe /Zi /Od your_program.c
OpenCppCoverage.exe --sources "C:\your\source\path" -- your_program.exe/Zi: デバッグ情報 (PDB ファイル) を生成する/Od: 最適化を無効にする (カバレッジの精度が上がる)最適化を有効にすると、コンパイラがコードを変形するため、カバレッジ結果が実際のソースコードと対応しにくくなることがあります。
OpenCppCoverage が対応している出力形式は次のとおりです。
gcov のようなシンプルなテキスト出力形式は標準では用意されていません。
OpenCppCoverage は PDB (Program Database) ファイルを使ってソースコードとバイナリの対応関係を取得します。この仕組みは C言語でも C++ でも同じように機能します。
OpenCppCoverage は gcov 形式のテキスト出力に直接対応していませんが、Cobertura XML 形式で出力し、それを変換することで gcov 互換のテキストファイルを生成できます。
Cobertura XML をパースして gcov 風のテキストを生成する Python スクリプトの実装例を示します。
import xml.etree.ElementTree as ET
import sys
from pathlib import Path
def convert_cobertura_to_gcov(xml_path, output_dir='.'):
"""Cobertura XMLをgcov風テキストに変換する"""
tree = ET.parse(xml_path)
root = tree.getroot()
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
# 各ソースファイルを処理
for package in root.findall('.//package'):
for cls in package.findall('.//class'):
filename = cls.get('filename')
if not filename:
continue
# ソースファイルを読み込む
try:
with open(filename, 'r', encoding='utf-8') as f:
source_lines = f.readlines()
except FileNotFoundError:
print(f"Warning: Source file not found: {filename}", file=sys.stderr)
continue
# 行ごとの実行回数を取得
line_hits = {}
for line in cls.findall('.//line'):
line_num = int(line.get('number'))
hits = int(line.get('hits'))
line_hits[line_num] = hits
# gcov形式で出力
gcov_filename = Path(filename).name + '.gcov'
gcov_path = output_path / gcov_filename
with open(gcov_path, 'w', encoding='utf-8') as f:
# ヘッダー情報
f.write(f" -: 0:Source:{filename}\n")
f.write(f" -: 0:Graph:(generated by cobertura_to_gcov.py)\n")
f.write(f" -: 0:Data:(generated by cobertura_to_gcov.py)\n")
# 各行を出力
for line_num, line_text in enumerate(source_lines, start=1):
if line_num in line_hits:
hits = line_hits[line_num]
if hits == 0:
prefix = " #####"
else:
prefix = f"{hits:9d}"
else:
prefix = " -"
f.write(f"{prefix}:{line_num:5d}:{line_text}")
print(f"Generated: {gcov_path}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python cobertura_to_gcov.py <coverage.xml> [output_dir]")
sys.exit(1)
xml_path = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else '.'
convert_cobertura_to_gcov(xml_path, output_dir)OpenCppCoverage.exe --export_type cobertura:coverage.xml --sources "C:\your\source" -- your_test.exe
python cobertura_to_gcov.py coverage.xml gcov_output生成されるファイルは次のような形式になります。
-: 0:Source:example.c
-: 0:Graph:(generated by cobertura_to_gcov.py)
-: 0:Data:(generated by cobertura_to_gcov.py)
-: 1:#include <stdio.h>
-: 2:
5: 3:int add(int a, int b) {
5: 4: return a + b;
5: 5:}
-: 6:
1: 7:int main() {
1: 8: printf("%d\n", add(2, 3));
#####: 9: printf("not executed\n");
1: 10: return 0;
1: 11:}
各行の先頭の数字は次の意味を持ちます。
#####: カバレッジ対象だが実行されなかった行-: カバレッジ対象外の行 (コメントや空行など)このスクリプトを基に、必要に応じてカスタマイズできます。
VSBT 環境で Enterprise ツールを使わない場合、OpenCppCoverage が最も現実的な選択肢です。Cobertura XML 形式で出力し、簡単な Python スクリプトで変換することで、gcov 互換のテキスト形式も生成できます。