C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

Visual Studio 2019/C++: ファイル単位で設定された Configuration はどこにあるかのメモ

このメモを残した経緯

保守性の視点では使わない方がよいのですが、諸事情によりファイル単位で Configuration を施される事はしばしばあります。そして、プロジェクトの責任者が変わり、ドキュメントにも注意が残されず、ファイル単位で特殊な Configuration が行われている事が忘れ去られ、やがて誰かがプロジェクトを保守する際に不可解なエラーによりその存在に気が付く事になります。問題は、不可解なエラーの原因がどこにあるのか探り出すにはそれ相応の手間がかかる事です。

PCH の Use / Create の切り替え程度の一般性も高く、プロジェクトの構成やファイルツリーを見ただけでおおよそ想像がつくような場合は問題になりません。しかし、次のような場合は少々問題の対応に手間が必要です。

問題の例

  1. "昔々"、プロジェクトの黎明期に何らかの理由で Debug 用の Configuration でもいくつかの特定のソースファイルは /O1 で翻訳されるようファイル単位の設定を施しました(†1)
  2. それから何年もの時間が経過し、Visual Studio のバージョンアップやC++言語規格のバージョンアップも何回かあり、プロジェクトの近代化対応が求められるようになります
  3. "今"となっては謎多きプロジェクトの地下に広がる広大なダンジョンを安全に再探索するため、例えばプロジェクトの Congiguration に /RTCc を加えたとします:
1>cl : Command line error D8016 : '/O1' and '/RTCc' command-line options are incompatible
1>C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets(314,5): error MSB6006: "CL.exe" exited with code 2.

"今"の担当者はプロジェクトの Configuration を見直しますが、 C/C++ ⮕ Optimization を確認しても Disabled (/Od) が設定されています。ふぁっきゅー。

と、なるわけです。問題は Output を見ても一体どのファイルがわざわざ Debug ビルドで個別に /O1 を設定されているのかわからない事です。わっざへぇぅ。

  • (†1): 少なからずのプロジェクト黎明期の神話の時代の神々は諸事情によりプロジェクトチームへ小さな約束事を口伝で伝えてはくれますが、まともなドキュメントは残しません。

ファイル単位で Configuration が施されたファイルの探し方

  1. プロジェクトファイル .vcxprojテキストエディターで開きます (†1)
  2. /Project/ItemGroup (†2) を単位として "目grep" を開始します
    • おそらく先頭にある Label 属性を持った ItemGroup 要素はプロジェクト全体の Configuration です
    • <ItemGroup> はプロジェクトの設定のほか、ファイルの種類ごとにいくつか存在します
      • ソースファイル ( .cpp など) が対象の設定を探す場合は /Project/ItemGroup/ClCompile を含む <ItemGroup> を探します
      • 画像ファイル ( .ico など ) が対象の設定を探す場合は /Project/ItemGroup/Image を含む <ItemGroup を探します
      • リソースファイル ( .rc ) が対象の設定を探す場合は /Project/ItemGroup/Image を含む <ItemGroup を探します
    • たいてい後から施されたであろう設定はファイルの終端に近い方で発見できます
    • 探したい設定値やファイルの一部がわかっている場合はそれをヒントに <ItemGropu> から目的の Configuration を定義しているであろう部分を絞り込みます
      • 例えば /O1 を探す場合は MinSpace で探し、
      • /Ox を探す場合は MaxSpeed で探し…
      • つまり、通常ユーザーの見えるところに露出している設定値とは少し値の表現が異なるそれを探す必要があります

例えば↑の問題の例では、↓のような <ItemGroup><ClCompile> 子要素を持ち、その属性で対象となるファイルが指示され、さらにその子要素に目的の Configuration が埋もれています:

  <ItemGroup>
    <ClCompile Include="something.cpp">
      <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MinSpace</Optimization>
    </ClCompile>
  </ItemGroup>

探し方のコツさえわかれば、あとは目的に応じたファイル単位の Configuration を必要に応じて洗い出し、必要に応じた調整を施せます。もし、設定値の XML での値の表現の変化 ( /O1MinSpace のような ) がわからなければ、適当に小さな新規プロジェクトを作り、構成から x86 と Release を削除して .vcxproj を目grepしやすくした上で、残る唯一の Configuration へ目的の設定値を施した状態で .vcxproj をテキストエディターで開いて観察するとよいかもしれません。たぶん Microsoft もこの部分の詳細な仕様のドキュメントは公開していません。

ファイル1つだけ、構成も複雑ではなく該当の設定箇所を見つけやすい場合はこのように既存の設定をどうにかすることで問題を解決できます。しかし、あまりに構成が複雑怪奇な場合は諦めて完全にまっさらな Configuration あるいは .vcxproj を作り直してプロジェクトのソースを取り込むところから始めた方がかえって低コストになることもあるかもしれません。

  • (†1): ファイルフォーマットは XML に準拠しているのでテキストまたはXMLに特化した適当なエディターで開くとよいです
  • (†2): XPath です。