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のある他の環境で出来るかどうかは試してない。 ↩