GameProgrammar's Night

ゲームプログラム系の覚え書き

ClangのWindowsネイティブ版clang-cl

ねんがんの ウインドウズClang をてにいれたぞ!

http://isocpp.org/blog/2014/01/llvm-3.4

clang3.4からWindowsネイティブを吐くバイナリが作れるようになりました。

ダウンロード

http://llvm.org/releases/download.html#3.4

Windowsインストーラーが用意されましたのでダウンロードしてインストールします。
 

clang-cl

 clang自体にWindows用のバイナリをはき出すモードがつきましたが、お手軽に使えるように「clang-cl」という専用の実行ファイルがありますのでそれを使います。
 「clang-cl」はVCのコンパイラである「cl.exe」と互換があるように作られていて、オプション・ABI互換があるそうです。
 その代わり、clang-clのC++11/14対応はVCと進度を合わせているようです(VCでの実装仕様公開待ちしてるということっぽい?)。そのため、clang3.4の目玉であるC++14への完全対応はclang-clでは行われていません。コンパイルオプションで「-std=C++1y」を指定してもはじかれます。(そもそも-stdオプションが使えません)

使い方

 VS2012のToolChainをバックグラウンドで使うので、VS2012がインストールされている必要があります。
 VS2012がインストールされていたら、お馴染み「vcvars32.bat」を叩いてパスを通すか、スタートメニューから「VS2012 の開発者コマンド プロンプト」を起動します。
 あとは、Clangへのパスが通ってれば、

clang-cl cppファイル <オプション>

コンパイルリンクされ実行ファイルができます。

コンパイルオプション

 基本的には、VCのコンパイルオプションを使います。まだすべてに対応しているわけではありませんが、良く使うものにはほぼ対応してるので、よほど特殊なことをしてない限りは困らないと思います。
 対応してるコンパイルオプションは、

clang-cl /?

で見るか、

http://llvm.org/releases/3.4/tools/clang/docs/UsersManual.html#clang-cl

に載ってます。
 他にもclangコアオプションに対応してるとあったので、-W系あたりはなんでも使えそうです。

WindowsSDKへのパス

 WindowsSDKへのパスはインクルードパスは通ってますので、windows.h も普通にincludeすれば使えます。しかし、ライブラリパスは通ってないので、ライブラリはフルパスで指定する必要があります。
 「/LIBPATH:」はまだ対応されてないので、

/link /I <ライブラリパス>

とかしてみたのですが、上手く動きませんでした。

/fallbackオプション

 少し特殊なオプションとして、fallbackというものがあります。clang-clでは対応してないcl独自拡張を使っているなどでコンパイルできなかった時に、clを使ってコンパイルしてくれるオプションです。
 こんなものが用意されてることからわかる通り、clでビルドしたバイナリとclang-clでビルドしたバイナリを混ぜて使うことも問題無いようです。

テストコード

実行すると左上にtestと表示されたウインドウが生成されるだけ

// a.cpp

#include <windows.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wc;
	::ZeroMemory(&wc, sizeof(wc));
	wc.cbSize = sizeof(wc);
	
	wc.hInstance	= hInstance;
	wc.lpfnWndProc	= &WndProc;
	wc.style	= CS_HREDRAW | CS_VREDRAW;
	wc.cbClsExtra	= 0;
	wc.cbWndExtra	= 0;
	wc.hIcon	= NULL;
	wc.hIconSm	= NULL;
	wc.hCursor	= ::LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = "clang-cl test"; 
	
	::RegisterClassEx(&wc);
	
	HWND hWnd = ::CreateWindowEx(0, wc.lpszClassName, wc.lpszClassName, 
                                     WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
				     100,100, 654, 494, NULL, NULL, hInstance, NULL);
	
	::ShowWindow(hWnd, SW_SHOWNORMAL);
	
	while(1)
	{
		MSG msg;
		LRESULT r = ::GetMessage(&msg, NULL, 0, 0);
		if(r==0||r==-1)
		{
			return 1;
		}
		else
		{
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
	}
	return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_PAINT:
	{
		HDC hDC = ::GetDC(hWnd);
		::TextOut(hDC, 0, 0, "test", 4);
		::ReleaseDC(hWnd, hDC);
	}
	return 0;
		
	case WM_DESTROY:
		PostQuitMessage( 0 );
	return 0;
	}

	return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

コンパイル

clang-cl a.cpp "<WindowsSDKへのパス>User32.lib"
               "<WindowsSDKへのパス>Gdi32.lib"

a.exe ができます。

プロジェクト

 さすがにプロジェクトファイルを渡してのビルドはできないようですので、makeの力を借りる必要があるようです。誰かがVS上でclang-clを叩けるようにするアドインを作ってることを期待

アドインなしでVSからclang-clを叩くようにするバッチが元々付いてたようです。
詳しくは↓の記事

http://blog.cnu.jp/2014/01/09/clang34-visualstudio/