昨天我們把 Conan 接上了 Xcode。今天是建置系統的最後一篇,輪到非常常見的 Makefile。
寫這篇範例時,我的實測環境:
前兩天一篇是 Windows (Visual Studio)、一篇是 macOS (Xcode),當然今天就輪到 Linux 了,不過這份 Makefile 在 macOS 上也能用。
請依照以下的目錄結構,自己手動建起來:
hello-conan-make/
├── conanfile.txt
├── Makefile
└── src/
└── main.cpp
全部就三個檔案。
開啟 conanfile.txt,填入以下內容。
[requires]
fmt/12.2.0
nlohmann_json/3.12.0
[generators]
MakeDeps
[requires] 區塊跟前兩篇一字不差,寫我們想導入的函式庫和版本號碼,一個函式庫一行。我們同樣使用業界廣為使用的 fmt + nlohmann JSON 函式庫作為範例。
[generators] 區塊填入 MakeDeps,告訴 Conan 這次我們要串接 Makefile。
注意:
MakeDeps在 Conan 2 官方文件上還標示為 experimental,以後可能會有改動。
開啟命令列,到專案的根目錄,執行以下指令:
conan install . --build=missing --output-folder build
--build=missing 是告訴 Conan 萬一沒辦法從伺服器上找到預編譯好的二進位檔,就抓原始碼下來現場編譯。
--output-folder build 指示 Conan 把產生的檔案放進 build/ 子目錄。
上面這行裝的是 Release 版套件 (Profile 裡預設的),如果你想要 Debug 版的套件,多跑一次conan install 指令加上 -s build_type=Debug 就可以了。
套件安裝完成之後, build/ 底下會多出七個檔案,我們只需關心一個檔案 conandeps.mk:
CONAN_DEPS = fmt nlohmann_json
CONAN_INCLUDE_DIRS = $(CONAN_INCLUDE_DIR_FLAG)/home/matt/.conan2/p/b/fmt.../p/include ...
CONAN_LIB_DIRS = $(CONAN_LIB_DIR_FLAG)/home/matt/.conan2/p/b/fmt.../p/lib ...
CONAN_LIBS = $(CONAN_LIB_FLAG)fmt $(CONAN_LIB_FLAG)fmt-c
CONAN_SYSTEM_LIBS = $(CONAN_SYSTEM_LIB_FLAG)m
裡面定義了多個變數,如 CONAN_INCLUDE_DIRS, CONAN_LIB_DIRS 等等,供我們之後使用。
到了重點步驟了,我們要撰寫 Mekfile 檔串接 Conan。以下就是 Makefile 的內容:
CONAN_INCLUDE_DIR_FLAG = -I
CONAN_LIB_DIR_FLAG = -L
CONAN_LIB_FLAG = -l
CONAN_DEFINE_FLAG = -D
CONAN_SYSTEM_LIB_FLAG = -l
include build/conandeps.mk
CXXFLAGS += -std=c++17
CPPFLAGS += $(CONAN_INCLUDE_DIRS) $(CONAN_DEFINES)
LDFLAGS += $(CONAN_LIB_DIRS)
LDLIBS += $(CONAN_LIBS) $(CONAN_SYSTEM_LIBS)
build/hello: src/main.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $< -o $@ $(LDFLAGS) $(LDLIBS)
.PHONY: clean
clean:
rm -f build/hello
我們用 include build/conandeps.mk 把 Conan 設定好的那些變數導入,然後依次設定 CPPFLAGS、LDFLAGS、LDLIBS。
然後這裡有個問題,為什麼 Makefile 檔案的開頭的五行,需要設定五個變數呢?
原因是因為啊, make 本身的機制並沒有綁死某個編譯器。比如設定 Include 目錄時,GCC 和 Clang 是用 -I ,而 MSVC 是寫 /I。Conan 無法預測 Makefile 最後會呼叫哪一個編譯器,只能把前綴留空給你填。
開頭那五行把 Conan 留的變數補上,之後就能直接用 $(CONAN_INCLUDE_DIRS) 這些彙總變數,直接一次導入 fmt & nlohmann JSON 的標頭檔目錄位置。
順帶一提,這五行寫在 include 前面或後面其實都可以,make 的變數是用到的時候才展開,放前面只是比較好讀。
另一個容易漏掉的地方就是 C++ 標準的版本。不同於前兩個建置系統,MakeDeps 不會把 profile 裡的 compiler.cppstd 設定值導入,所以 -std=c++17 要自己寫。
main.cpp 原始碼(一如我們前兩天用的同樣程式碼):
#include <fmt/core.h>
#include <nlohmann/json.hpp>
int main() {
nlohmann::json j = {
{"name", "Conan"},
{"language", "Python"},
{"stars", 9000}
};
fmt::print("Hello, {}!\n", j["name"].get<std::string>());
fmt::print("{}\n", j.dump(2));
return 0;
}
使用以下命令編譯執行:
make
./build/hello
你就會看到跟前兩篇完全一樣的輸出囉!
Hello, Conan!
{
"language": "Python",
"name": "Conan",
"stars": 9000
}
| generators | 要串接的檔案 | |
|---|---|---|
| Visual Studio | MSBuildDeps + MSBuildToolchain |
conandeps.props、conantoolchain.props |
| Xcode | XcodeDeps + XcodeToolchain |
conan_config.xcconfig |
| Makefile | MakeDeps |
conandeps.mk |
走過這三天,其實就幾個重點:
[requires] 這個區塊,就是條列函式庫的地方,不論哪個建構系統寫法都一樣。[generators] 替換不同的 generators 就能串接不同的建置系統好啦,建置系統的串接部分,到這裡先告一段落。
明天就來看稍微進階的用法: conanfile.py。