LoginSignup
2
3

More than 3 years have passed since last update.

vcprojファイルからcompile_commands.jsonを作成する

Last updated at Posted at 2019-10-08

vcprojファイルからcompile_commands.jsonを作成する

vcprojファイルからcompile_commands.jsonを作成するツールを作りました。
この記事は、作成中に解決していった際のメモです。

大きく2つの問題が有りました。

  • vcprojファイルの解析
  • ソースコードのエンコーディング

一応動くようになったと思いますが、まだ完成度は低いです。

モチベーション

なんの業かわかりませんが、2019年現在でVisual Studio 2008(VC++ 2008)で開発が必要になっていることがあります。
最近はclangを中心としたエコシステム(clang-tidyやclang-doc)が充実しその恩恵にあやかりたいところですが、clangおよび周辺ツールで使用するcompile_commands.jsonを使用する必要があります。
そのため、何らかの方法で、Visual C++ 2008のプロジェクトファイル(.vcproj)からcompile_commands.jsonを作成する必要があります。

Visual Studio 2008(VC++ 2008)からcmakeに移行し、CMakefile.txtからcompile_commands.jsonを作成すればいい話ではありますが、日々変わっていく既存の大量のプロジェクトを前にすると、とても移行できる気がしません。

いくつかのcompile_commands.jsonを作成するツールがありますが、なかなか容易にできるツールが無く、さらにWindows CE(!)のプロジェクトファイルをサポートしているものは絶望的だったので、ツールを作成しました。

vcprojファイルの解析

vcprojファイルはXMLファイルなので、一瞬変換できるか?と思ってしまいますが、一筋縄で行きません。

  1. vcprojのスキーマがわからない。
  2. システムインクルードディレクトリがわからない。
  3. マクロが展開できない。

という問題があります。
スキーマは一応あることはあるのですが、一番欲しいところのコンパイラの設定(<Tool Name="VCCLCompilerTool>"の部分)は含まれてません。

というわけで、自力での解析は諦めていりいろ調べているうちに、Visual Studio 2008 に同梱されているVCProjectEngine.dll(Windows CEだとVCProjectWCEPlatform.dll)というCOMライブラリの存在を知りました。

devenv.exeで参照できる情報は大方このライブラリのVCCLCompilerToolインターフェースで参照できるようなので、これを使うことに決めました。

COMライブラリなので、VC++でツールの実装はできるのですが、文字列処理が面倒そうなので、C#で実装しました。
あとはVCCLCompilerToolインターフェースをひたすらclangのコマンドラインオプションに変換するコードを書いていきます。

clangでコンパイルしたときの問題

ツールで作成したjsonですんなりclangが動作すればいいのですが、手元のコードやSDKは古い、かつポータビリティを考慮していないものが多いので問題が多発します。

_Complex でエラーになる場合がある

SDKによっては、_cabs関数の宣言が、引数名がCのキーワード_Complexになっており、エラーとなってしまうことが有りました。

double    __cdecl _cabs(struct _complex _Complex);

ローカル環境のSDKのヘッダーを直すか、clangのコマンドラインで-D _Complex=_Complex_valueと置き換えることになります。

ツールのデフォルト設定で、clangのコマンドラインに-D _Complex=_Complex_valueを指定するようにしておきました。

ms-compatibility-versionで指定するバージョンは_MSC_VERを指定しなければならない

当初、VARIANT構造体の_VARIANT_BOOL boolがエラーになることが有りました。
「あれ、MSVCって、boolを変数名にできるの?」と思いながら_VARIANT_BOOLの定義を覗くと、下記のようになっています。

#if !__STDC__ && (_MSC_VER <= 1000)
/* For backward compatibility */
typedef VARIANT_BOOL _VARIANT_BOOL;

#else
/* ANSI C/C++ reserve bool as keyword */
#define _VARIANT_BOOL    /##/
#endif

どうやら、_MSC_VER <= 1000 になっているようです。
-fms-compatibility-version=に、VC++のバージョンを設定していましたが、ドキュメントを見ると_MSC_VERの方にしないと行けないようでした。
それにしても、MSVCのバージョン体系はクソ。

_MoveFromCoprocessor/_MoveFromCoprocessor2でオーバーロードエラーになる

Windows CE(ARM)のintrinsicsヘッダーファイル armintr.h を使用すると、エラーになる。

C:\Program Files (x86)\Windows Mobile 5.0 SDK R2\PocketPC\include\ARMV4I\armintr.h(59,5) : error: functions that differ only in their return type cannot be
overloaded
int _MoveFromCoprocessor(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); //mrc
~~~ ^
C:\Program Files (x86)\Windows Mobile 5.0 SDK R2\PocketPC\include\ARMV4I\armintr.h(59,5) : note: previous implicit declaration is here

previous implicit declaration is hereに続いて何も出力されておらず、
clangのBuiltinを見ると、戻り値はunsigned intのようなので、おそらくこれが問題なのであろう。
https://github.com/llvm-mirror/clang/blob/master/include/clang/Basic/BuiltinsARM.def

LANGBUILTIN(_MoveFromCoprocessor, "UiIUiIUiIUiIUiIUi", "", ALL_MS_LANGUAGES)
LANGBUILTIN(_MoveFromCoprocessor2, "UiIUiIUiIUiIUiIUi", "", ALL_MS_LANGUAGES)

これはシステムのヘッダーファイルを修正しました。
但し、MSVCで警告が出るようになります。

armintr.h(59) : warning C4391: 'unsigned int _MoveFromCoprocessor(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int)' : 組み込み関数に対して戻り値の型が無効です。'int' であるべきです。

MSVC拡張

clangでは積極的にMSVC拡張に対応してくれているが、それでも対応していないものがある。
手元では、下記の2点が対応していなかった。
(clangが悪いわけではない)

  1. class Foo abstact {};
  2. virtual修飾のない、void x() override {}

こんなコードを修正するべきです。

ソースコードのエンコーディング

clangは今の所、ソースコードのエンコーディングはUTF-8しか受け入れません。
(有志がShift-JIS対応を出されたりしてますが。)

こればっかりは仕方が無いので、ツールに簡易エンコーディング変換機能をつけました。
(手動でやっても良いのですが。)

compile_commands.jsonを作成しながらUTF-8に変換する --autoutf8と、
プロジェクトに含まれるファイルをUTF-8に変換するだけ(jsonを作成しない)--utf8です。

jsonを作成しながらUTF-8に変換するだけでも良い気がしそうですが、このツールではヘッダーファイルの依存関係がわからないので、予め全部変換してするのが通常の使い方になるかと思います。

心苦しいですが、UTF-8にはBOMを付けるので、一応はVisual Studioでは文字化けせずに開けます。

ツールのスコープ外の使い方

vcprojに沿ったcompile_commands.jsonに変換するようにしたつもりですが、clangおよび周辺ツールで使用することはあくまで「参考程度」と考えています。
クロスコンパイルのように、clangでオブジェクトが作れるレベルでの変換は目的にしてません。

参考にしたページ

Clang Compiler User’s Manual

Clang command line argument reference

Diagnostic flags in Clang
警告が出まくるので、システムヘッダーで出るような警告は無効にした。

GCC と Clang のオプション概要
Clang command line argument referenceだと説明が省かれていることがあるので、こちらで補いました。

Compilation database

JSON Compilation Database Format Specification

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3