clangでPDBファイルが出力できるようになったりしたので、WSLのmake使ってWindowsの実行ファイルをビルドするMakefileを書いてみました。
方針としては、clang++.exeで依存関係ファイルの生成、clang-cl.exeでコンパイル、lld-link.exeでリンクを行います。
準備
次のものをインストールしておきます。
- Visual Studio
- LLVM/clang
- Windows Subsystem for Linux (WSL) 1
LLVM/clangのパスを通して、WSLのLinuxのclangではなくWindowsのclangを使用します。WSLならclang++.exe等を利用できるようにします。
今回の環境は以下のようにしています。
- ArchWSL
- GNU Make 4.2.1
- Visual Studio 2017 Community(14.13.26128)
- LLVM/clang 6.0.0
コード
ここでは、特にsrcフォルダなどを作らず、cppファイルと同じ場所にexeファイルが生成されるようにします。
CXXFLAGS := \
	-EHsc -W4 -utf-8 -std:c++17 \
	-fcolor-diagnostics -fansi-escape-codes \
	-DUNICODE -D_UNICODE
INCLUDE :=
LDFLAGS := \
	-libpath:"c:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.13.26128/lib/x64"
LIBS := \
	kernel32.lib user32.lib gdi32.lib
TARGET := example.exe
SRCS := $(shell ls | grep '.cpp')
OBJS := $(patsubst %.cpp, %.o, $(SRCS))
PDB := $(patsubst %.exe, %.pdb, $(TARGET))
DEPENDS := $(patsubst %.cpp, %.d, $(SRCS))
.PHONY: all clean debug release
debug: CXXFLAGS += -MTd -Zi -D_DEBUG -DDEBUG
debug: LDFLAGS += -debug
debug: $(TARGET)
release: CXXFLAGS += -MT -O2 -DNDEBUG
release: $(TARGET)
%.o: %.cpp
	clang++.exe -E -std=c++17 $(INCLUDE) -MMD $< > /dev/null
	sed -i 's/\\\([^\f\n\r\t]\)/\/\1/g' $(patsubst %.cpp, %.d, $<)
	clang-cl.exe -c $(CXXFLAGS) $(INCLUDE) $< -o $@
$(TARGET): $(OBJS)
	lld-link.exe $(LDFLAGS) $^ $(LIBS) -out:$@
all: debug
clean:
	-rm $(OBJS)
	-rm $(DEPENDS)
	-rm $(PDB)
	-rm $(TARGET)
-include $(DEPENDS)
コードはここに置いておきます。
https://github.com/LNSEAB/clang_cl_makefile
解説
CXXFLAGS
clang-cl.exeのオプションを設定しています。
以下にCXXFLAGSを抜粋します。
CXXFLAGS := \
	-EHsc -W4 -utf-8 -std:c++17 \
	-fcolor-diagnostics -fansi-escape-codes \
	-DUNICODE -D_UNICODE
オプションを/の代わりに-にしています。ただし、clと同じオプションで値や文字列を指定する時は-std:c++17のように=ではなく:を使います。
-fcolor-diagnostics -fansi-escape-codesはWSLでclangのメッセージに色をつけるためのオプションです。
-DUNICODE等の-Dはマクロを定義するオプションですが、ここで値を指定する時は-DWINNT=0x0A00のように=で指定します。
LDFLAGS
lld-link.exeのオプションを設定しています。
以下にLDFLAGSを抜粋します。
LDFLAGS := \
	-libpath:"c:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.13.26128/lib/x64"
lld-linkはVisual C++のlinkと同じオプションを使えます。
ライブラリのパスを通すのに-libpathを使っており、CXXFLAGSと同様に=の代わりに:を使っています。
また、上記のようにVisual Studioにあるlibcmt.libなどのCライブラリまでのパスを通す必要があります。
SRCSからDEPENDSまで
以下にSRCSからDEPENDSまでを抜粋します。
SRCS := $(shell ls | grep '.cpp')
OBJS := $(patsubst %.cpp, %.o, $(SRCS))
PDB := $(patsubst %.exe, %.pdb, $(TARGET))
DEPENDS := $(patsubst %.cpp, %.d, $(SRCS))
.cppファイルを自分で列挙したくないのでlsとgrepを使って列挙しています。さらに、それを元にmakeの組み込み関数patsubstを使ってcppファイルの拡張子をそれぞれoとdに変えて列挙しています。
debugとrelease
debugとreleaseをルールを分けて別々にオプションを指定したい時、以下のような書き方ができます。
debug: CXXFLAGS += -MTd -Zi -D_DEBUG -DDEBUG
debug: LDFLAGS += -debug
debug: $(TARGET)
release: CXXFLAGS += -MT -O2 -DNDEBUG
release: $(TARGET)
%.o: %.cpp
%.oのルールを以下に抜粋します。
%.o: %.cpp
    clang++.exe -E -std=c++17 $(INCLUDE) -MMD $< > /dev/null
    sed -i 's/\\\([^\f\n\r\t]\)/\/\1/g' $(patsubst %.cpp, %.d, $<)
    clang-cl.exe -c $(CXXFLAGS) $(INCLUDE) $< -o $@
clang++.exeの行について、-MMDでdファイルの生成し、コンパイルはしないように-Eと> /dev/nullで標準出力されたプリプロセス済のソースを読み捨てることで、dファイルだけを出力します。しかし、dファイル内の依存関係のパスに何故か\が混じるので、次の行でsedで/に置き換えてます。
あとは、clang-cl.exeでコンパイルを行います。
おわりに
ターミナルにWSLを使っているVSCodeでのデバッグもやりやすくなりました。
参考
サンプルコード
https://github.com/LNSEAB/clang_cl_makefile
- 
MSYS2とかのmakeのある他の環境で出来るかどうかは試してない。 ↩ 
