xmake v2.5.2 釋出, 支援自動拉取交叉工具鏈和依賴包整合
阿新 • • 發佈:2021-03-01
[xmake](https://github.com/xmake-io/xmake) 是一個基於 Lua 的輕量級跨平臺構建工具,使用 xmake.lua 維護專案構建,相比 makefile/CMakeLists.txt,配置語法更加簡潔直觀,對新手非常友好,短時間內就能快速入門,能夠讓使用者把更多的精力集中在實際的專案開發上。
在 2.5.2 版本中,我們增加了一個重量級的新特性:`自動拉取遠端交叉編譯工具鏈`。
這是用來幹什麼的呢,做過交叉編譯以及有 C/C++ 專案移植經驗的同學應該知道,折騰各種交叉編譯工具鏈,移植編譯專案是非常麻煩的一件事,需要自己下載對應工具鏈,並且配置工具鏈和編譯環境很容易出錯導致編譯失敗。
現在,xmake 已經可以支援自動下載專案所需的工具鏈,然後使用對應工具鏈直接編譯專案,使用者不需要關心如何配置工具鏈,任何情況下只需要執行 `xmake` 命令即可完成編譯。
![](https://tboox.org/static/img/xmake/muslcc.gif)
甚至對於 C/C++ 依賴包的整合,也可以自動切換到對應工具鏈編譯安裝整合,一切完全自動化,完全不需要使用者操心。
除了交叉編譯工具鏈,我們也可以自動拉取工具鏈,比如特定版本的 llvm,llvm-mingw, zig 等各種工具鏈來參與編譯 C/C++/Zig 專案的編譯。
即使是 cmake 也還不支援工具鏈的自動拉取,頂多只能配合 vcpkg/conan 等第三方包管理對 C/C++ 依賴包進行整合,另外,即使對於 C/C++依賴包,xmake 也有自己原生內建的包管理工具,完全無任何依賴。
* [專案原始碼](https://github.com/xmake-io/xmake)
* [官方文件](https://xmake.io/#/zh-cn/)
* [入門課程](https://xmake.io/#/zh-cn/about/course)
## 新特性介紹
### 自動拉取遠端交叉編譯工具鏈
從 2.5.2 版本開始,我們可以拉取指定的工具鏈來整合編譯專案,我們也支援將依賴包切換到對應的遠端工具鏈參與編譯後集成進來。
相關例子程式碼見:[Toolchain/Packages Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/package)
相關的 issue [#1217](https://github.com/xmake-io/xmake/issues/1217)
當前,我們已經在 [xmake-repo](https://github.com/xmake-io/xmake-repo) 倉庫收錄了以下工具鏈包,可以讓 xmake 遠端拉取整合:
* llvm
* llvm-mingw
* gnu-rm
* muslcc
* zig
雖然現在支援的工具鏈包不多,當但是整體架構已經打通,後期我們只需要收錄更多的工具鏈進來就行,比如:gcc, tinyc, vs-buildtools 等工具鏈。
由於 xmake 的包支援語義版本,因此如果專案依賴特定版本的 gcc/clang 編譯器,就不要使用者去折騰安裝了,xmake 會自動檢測當前系統的 gcc/clang 版本是否滿足需求。
如果版本不滿足,那麼 xmake 就會走遠端拉取,自動幫使用者安裝整合特定版本的 gcc/clang,然後再去編譯專案。
#### 拉取指定版本的 llvm 工具鏈
我們使用 llvm-10 中的 clang 來編譯專案。
```lua
add_requires("llvm 10.x", {alias = "llvm-10"})
target("test")
set_kind("binary")
add_files("src/*.c)
set_toolchains("llvm@llvm-10")
```
其中,`llvm@llvm-10` 的前半部分為工具鏈名,也就是 `toolchain("llvm")`,後面的名字是需要被關聯工具鏈包名,也就是 `package("llvm")`,不過如果設定了別名,那麼會優先使用別名:`llvm-10`
另外,我們後續還會增加 gcc 工具鏈包到 xmake-repo,使得使用者可以自由切換 gcc-10, gcc-11 等特定版本的 gcc 編譯器,而無需使用者去手動安裝。
### 拉取交叉編譯工具鏈
我們也可以拉取指定的交叉編譯工具鏈來編譯專案。
```lua
add_requires("muslcc")
target("test")
set_kind("binary")
add_files("src/*.c)
set_toolchains("@muslcc")
```
muslcc 是 https://musl.cc 提供的一款交叉編譯工具鏈,預設 xmake 會自動整合編譯 `x86_64-linux-musl-` 目標平臺。
當然,我們也可以通過 `xmake f -a arm64` 切換到 `aarch64-linux-musl-` 目標平臺來進行交叉編譯。
#### 拉取工具鏈並且整合對應工具鏈編譯的依賴包
我們也可以使用指定的muslcc交叉編譯工具鏈去編譯和整合所有的依賴包。
```lua
add_requires("muslcc")
add_requires("zlib", "libogg", {system = false})
set_toolchains("@muslcc")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib", "libogg")
```
這個時候,工程裡面配置的 zlib, libogg 等依賴包,也會切換使用 muslcc 工具鏈,自動下載編譯然後整合進來。
我們也可以通過 `set_plat/set_arch` 固定平臺,這樣只需要一個 xmake 命令,就可以完成整個交叉編譯環境的整合以及架構切換。
```lua
add_requires("muslcc")
add_requires("zlib", "libogg", {system = false})
set_plat("cross")
set_arch("arm64")
set_toolchains("@muslcc")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib", "libogg")
```
完整例子見:[Examples (muslcc)](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/toolchain_muslcc/xmake.lua)
#### 拉取整合 Zig 工具鏈
xmake 會先下載特定版本的 zig 工具鏈,然後使用此工具鏈編譯 zig 專案,當然使用者如果已經自行安裝了 zig 工具鏈,xmake 也會自動檢測對應版本是否滿足,如果滿足需求,那麼會直接使用它,無需重複下載安裝。
```lua
add_rules("mode.debug", "mode.release")
add_requires("zig 0.7.x")
target("test")
set_kind("binary")
add_files("src/*.zig")
set_toolchains("@zig")
```
### 增加對 zig cc 編譯器支援
`zig cc` 是 zig 內建的 c/c++ 編譯器,可以完全獨立進行 c/c++ 程式碼的編譯和連結,完全不依賴 gcc/clang/msvc,非常給力。
因此,我們完全可以使用它來編譯 c/c++ 專案,關鍵是 zig 的工具鏈還非常輕量,僅僅幾十M 。
我們只需要切換到 zig 工具鏈即可完成編譯:
```bash
$ xmake f --toolchain=zig
$ xmake
[ 25%]: compiling.release src/main.c
"zig cc" -c -arch x86_64 -fvisibility=hidden -O3 -DNDEBUG -o build/.objs/xmake_test/macosx/x86_64/release/src/main.c.o src/main.c
[ 50%]: linking.release test
"zig c++" -o build/macosx/x86_64/release/test build/.objs/xmake_test/macosx/x86_64/release/src/main.c.o -arch x86_64 -stdlib=libc++ -Wl,-x -lz
[100%]: build ok!
```
另外,`zig cc` 的另外一個強大之處在於,它還支援不同架構的交叉編譯,太 happy 了。
通過 xmake,我們也只需再額外切換下架構到 arm64,即可實現對 arm64 的交叉編譯,例如:
```bash
$ xmake f -a arm64 --toolchain=zig
$ xmake
[ 25%]: compiling.release src/main.c
"zig cc" -c -target aarch64-macos-gnu -arch arm64 -fvisibility=hidden -O3 -DNDEBUG -o build/.objs/xmake_test/macosx/arm64/release/src/main.c.o src/main.c
checking for flags (-MMD -MF) ... ok
checking for flags (-fdiagnostics-color=always) ... ok
[ 50%]: linking.release xmake_test
"zig c++" -o build/macosx/arm64/release/xmake_test build/.objs/xmake_test/macosx/arm64/release/src/main.c.o -target aarch64-macos-gnu -arch arm64 -stdlib=libc++ -Wl,-x -lz
[100%]: build ok!
```
即使你是在在 macOS,也可以用 `zig cc` 去交叉編譯 windows/x64 目標程式,相當於替代了 mingw 乾的事情。
```bash
$ xmake f -p windows -a x64 --toolchain=zig
$ xmake
```
### 自動匯出所有 windows/dll 中的符號
cmake 中有這樣一個功能:`WINDOWS_EXPORT_ALL_SYMBOLS`,安裝 cmake 文件中的說法:
[https://cmake.org/cmake/help/latest/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.html](https://cmake.org/cmake/help/latest/prop_tgt/WINDOWS_EXPORT_ALL_SYMBOLS.html)
> Enable this boolean property to automatically create a module definition (.def) file with all global symbols found
> in the input .obj files for a SHARED library (or executable with ENABLE_EXPORTS) on Windows.
> The module definition file will be passed to the linker causing all symbols to be exported from the .dll. For global data symbols,
> __declspec(dllimport) must still be used when compiling against the code in the .dll. All other function symbols will be automatically exported and imported by callers.
> This simplifies porting projects to Windows by reducing the need for explicit dllexport markup, even in C++ classes.
大體意思就是:
> 啟用此布林屬性,可以自動建立一個模組定義(.def)檔案,其中包含在Windows上的共享庫(或使用ENABLE_EXPORTS的可執行檔案)的輸入.obj檔案中找到的所有全域性符號。
> 模組定義檔案將被傳遞給連結器,使所有符號從.dll中匯出。對於全域性資料符號,當對.dll中的程式碼進行編譯時,仍然必須使用__declspec(dllimport)。
> 所有其它的函式符號將被呼叫者自動匯出和匯入。這就簡化了將專案移植到 Windows 的過程,減少了對顯式 dllexport 標記的需求,甚至在 C++ 類中也是如此。
現在,xmake 中也提供了類似的特性,可以快速全量匯出 windows/dll 中的符號,來簡化對第三方專案移植過程中,對符號匯出的處理。另外,如果專案中的符號太多,也可以用此來簡化程式碼中的顯式匯出需求。
我們只需在對應生成的 dll 目標上,配置 `utils.symbols.export_all` 規則即可。
```lua
target("foo")
set_kind("shared")
add_files("src/foo.c")
add_rules("utils.symbols.export_all")
target("test")
set_kind("binary")
add_deps("foo")
add_files("src/main.c")
```
xmake 會自動掃描所有的 obj 物件檔案,然後生成 def 符號匯出檔案,傳入 link.exe 實現快速全量匯出。
### 轉換 mingw/.dll.a 到 msvc/.lib
這個特性也是參考自 CMAKE_GNUtoMS 功能,可以把MinGW生成的動態庫(xxx.dll & xxx.dll.a)轉換成Visual Studio可以識別的格式(xxx.dll & xxx.lib),從而實現混合編譯。
這個功能對Fortran & C++混合專案特別有幫助,因為VS不提供fortran編譯器,只能用MinGW的gfortran來編譯fortran部分,然後和VS的專案連結。
往往這樣的專案同時有一些其他的庫以vs格式提供,因此純用MinGW編譯也不行,只能使用cmake的這個功能來混合編譯。
因此,xmake 也提供了一個輔助模組介面去支援它,使用方式如下:
```lua
import("utils.platform.gnu2mslib")
gnu2mslib("xxx.lib", "xxx.dll.a")
gnu2mslib("xxx.lib", "xxx.def")
gnu2mslib("xxx.lib", "xxx.dll.a", {dllname = "xxx.dll", arch = "x64"})
```
支援從 def 生成 xxx.lib ,也支援從 xxx.dll.a 自動匯出 .def ,然後再生成 xxx.lib
具體細節見:[issue #1181](https://github.com/xmake-io/xmake/issues/1181)
### 實現批處理命令來簡化自定義規則
為了簡化使用者自定義 rule 的配置,xmake 新提供了 `on_buildcmd_file`, `on_buildcmd_files` 等自定義指令碼入口,
我們可以通過 batchcmds 物件,構造一個批處理命令列任務,xmake 在實際執行構建的時候,一次性執行這些命令。
這對於 `xmake project` 此類工程生成器外掛非常有用,因為生成器生成的第三方工程檔案並不支援 `on_build_files` 此類內建指令碼的執行支援。
但是 `on_buildcmd_file` 構造的最終結果,就是一批原始的 cmd 命令列,可以直接給其他工程檔案作為 custom commands 來執行。
另外,相比 `on_build_file`,它也簡化對擴充套件檔案的編譯實現,更加的可讀易配置,對使用者也更加友好。
```lua
rule("foo")
set_extensions(".xxx")
on_buildcmd_file(function (target, batchcmds, sourcefile, opt)
batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile})
end)
```
除了 `batchcmds:vrunv`,我們還支援一些其他的批處理命令,例如:
```lua
batchcmds:show("hello %s", "xmake")
batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}, {envs = {LD_LIBRARY_PATH="/xxx"}})
batchcmds:mkdir("/xxx") -- and cp, mv, rm, ln ..
batchcmds:compile(sourcefile_cx, objectfile, {configs = {includedirs = sourcefile_dir, languages = (sourcekind == "cxx" and "c++11")}})
batchcmds:link(objectfiles, targetfile, {configs = {linkdirs = ""}})
```
同時,我們在裡面也簡化對依賴執行的配置,下面是一個完整例子:
```lua
rule("lex")
set_extensions(".l", ".ll")
on_buildcmd_file(function (target, batchcmds, sourcefile_lex, opt)
-- imports
import("lib.detect.find_tool")
-- get lex
local lex = assert(find_tool("flex") or find_tool("lex"), "lex not found!")
-- get c/c++ source file for lex
local extension = path.extension(sourcefile_lex)
local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c"))
-- add objectfile
local objectfile = target:objectfile(sourcefile_cx)
table.insert(target:objectfiles(), objectfile)
-- add commands
batchcmds:show_progress(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex)
batchcmds:mkdir(path.directory(sourcefile_cx))
batchcmds:vrunv(lex.program, {"-o", sourcefile_cx, sourcefile_lex})
batchcmds:compile(sourcefile_cx, objectfile)
-- add deps
batchcmds:add_depfiles(sourcefile_lex)
batchcmds:set_depmtime(os.mtime(objectfile))
batchcmds:set_depcache(target:dependfile(objectfile))
end)
```
我們從上面的配置可以看到,整體執行命令列表非常清晰,而如果我們用 `on_build_file` 來實現,可以對比下之前這個規則的配置,就能直觀感受到新介面的配置方式確實簡化了不少:
```lua
rule("lex")
-- set extension
set_extensions(".l", ".ll")
-- load lex/flex
before_load(function (target)
import("core.project.config")
import("lib.detect.find_tool")
local lex = config.get("__lex")
if not lex then
lex = find_tool("flex") or find_tool("lex")
if lex and lex.program then
config.set("__lex", lex.program)
cprint("checking for Lex ... ${color.success}%s", lex.program)
else
cprint("checking for Lex ... ${color.nothing}${text.nothing}")
raise("lex/flex not found!")
end
end
end)
-- build lex file
on_build_file(function (target, sourcefile_lex, opt)
-- imports
import("core.base.option")
import("core.theme.theme")
import("core.project.config")
import("core.project.depend")
import("core.tool.compiler")
import("private.utils.progress")
-- get lex
local lex = assert(config.get("__lex"), "lex not found!")
-- get extension: .l/.ll
local extension = path.extension(sourcefile_lex)
-- get c/c++ source file for lex
local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c"))
local sourcefile_dir = path.directory(sourcefile_cx)
-- get object file
local objectfile = target:objectfile(sourcefile_cx)
-- load compiler
local compinst = compiler.load((extension == ".ll" and "cxx" or "cc"), {target = target})
-- get compile flags
local compflags = compinst:compflags({target = target, sourcefile = sourcefile_cx})
-- add objectfile
table.insert(target:objectfiles(), objectfile)
-- load dependent info
local dependfile = target:dependfile(objectfile)
local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {})
-- need build this object?
local depvalues = {compinst:program(), compflags}
if not depend.is_changed(dependinfo, {lastmtime = os.mtime(objectfile), values = depvalues}) then
return
end
-- trace progress info
progress.show(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex)
-- ensure the source file directory
if not os.isdir(sourcefile_dir) then
os.mkdir(sourcefile_dir)
end
-- compile lex
os.vrunv(lex, {"-o", sourcefile_cx, sourcefile_lex})
-- trace
if option.get("verbose") then
print(compinst:compcmd(sourcefile_cx, objectfile, {compflags = compflags}))
end
-- compile c/c++ source file for lex
dependinfo.files = {}
assert(compinst:compile(sourcefile_cx, objectfile, {dependinfo = dependinfo, compflags = compflags}))
-- update files and values to the dependent file
dependinfo.values = depvalues
table.insert(dependinfo.files, sourcefile_lex)
depend.save(dependinfo, dependfile)
end)
```
關於這個的詳細說明和背景,見:[issue 1246](https://github.com/xmake-io/xmake/issues/1246)
### 依賴包配置改進
#### 使用 add_extsources 改進包名查詢
關於遠端依賴包定義這塊,我們也新增了 `add_extsources` 和 `on_fetch` 兩個配置介面,可以更好的配置 xmake 在安裝 C/C++ 包的過程中,對系統庫的查詢過程。
至於具體背景,我們可以舉個例子,比如我們在 [xmake-repo](https://github.com/xmake-io/xmake-repo) 倉庫新增了一個 `package("libusb")` 的包。
那麼使用者就可以通過下面的方式,直接整合使用它:
```lua
add_requires("libusb")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("libusb")
```
如果使用者系統上確實沒有安裝 libusb,那麼 xmake 會自動下載 libusb 庫原始碼,自動編譯安裝整合,沒啥問題。
但如果使用者通過 `apt install libusb-1.0` 安裝了 libusb 庫到系統,那麼按理 xmake 應該會自動優先查詢使用者安裝到系統環境的 libusb 包,直接使用,避免額外的下載編譯安裝。
但是問題來了,xmake 內部通過 `find_package("libusb")` 並沒有找打它,這是為什麼呢?因為通過 apt 安裝的 libusb 包名是 `libusb-1.0`, 而不是 libusb。
我們只能通過 `pkg-config --cflags libusb-1.0` 才能找到它,但是 xmake 內部的預設 find_package 邏輯並不知道 `libusb-1.0` 的存在,所以找不到。
因此為了更好地適配不同系統環境下,系統庫的查詢,我們可以通過 `add_extsources("pkgconfig::libusb-1.0")` 去讓 xmake 改進查詢邏輯,例如:
```lua
package("libusb")
add_extsources("pkgconfig::libusb-1.0")
on_install(function (package)
-- ...
end)
```
另外,我們也可以通過這個方式,改進查詢 homebrew/pacman 等其他包管理器安裝的包,例如:`add_extsources("pacman::libusb-1.0")`。
#### 使用 on_fetch 完全定製系統庫查詢
如果不同系統下安裝的系統庫,僅僅只是包名不同,那麼使用 `add_extsources` 改進系統庫查詢已經足夠,簡單方便。
但是如果有些安裝到系統的包,位置更加複雜,想要找到它們,也許需要一些額外的指令碼才能實現,例如:windows 下注冊表的訪問去查詢包等等,這個時候,我們就可以通過 `on_fetch` 完全定製化查詢系統庫邏輯。
還是以 libusb 為例,我們不用 `add_extsources`,可以使用下面的方式,實現相同的效果,當然,我們可以在裡面做更多的事情。
```
package("libusb")
on_fetch("linux", function(package, opt)
if opt.system then
return find_package("pkgconfig::libusb-1.0")
end
end)
```
### manifest 檔案支援
在新版本中,我們還新增了對 windows `.manifest` 檔案的支援,只需要通過 `add_files` 新增進來即可。
```lua
add_rules("mode.debug", "mode.release")
target("test")
set_kind("binary")
add_files("src/*.cpp")
add_files("src/*.manifest")
```
### xrepo 命令改進
關於 xrepo 命令,我們也稍微改進了下,現在可以通過下面的命令,批量解除安裝刪除已經安裝的包,支援模式匹配:
```bash
$ xrepo remove --all
$ xrepo remove --all zlib pcr*
```
### 包的依賴匯出支援
我們也改進了 `add_packages`,使其也支援 `{public = true}` 來匯出包配置給父 target。
```lua
add_rules("mode.debug", "mode.release")
add_requires("pcre2")
target("test")
set_kind("shared")
add_packages("pcre2", {public = true})
add_files("src/test.cpp")
target("demo")
add_deps("test")
set_kind("binary")
add_files("src/main.cpp") -- 我們可以在這裡使用被 test 目標匯出 pcre2 庫
```
至於具體匯出哪些配置呢?
```lua
-- 預設私有,但是 links/linkdirs 還是會自動匯出
add_packages("pcre2")
-- 全部匯出。包括 includedirs, defines
add_packages("pcre2", {public = true})
```
## 更新內容
### 新特性
* [#955](https://github.com/xmake-io/xmake/issues/955#issuecomment-766481512): 支援 `zig cc` 和 `zig c++` 作為 c/c++ 編譯器
* [#955](https://github.com/xmake-io/xmake/issues/955#issuecomment-768193083): 支援使用 zig 進行交叉編譯
* [#1177](https://github.com/xmake-io/xmake/issues/1177): 改進終端和 color codes 探測
* [#1216](https://github.com/xmake-io/xmake/issues/1216): 傳遞自定義 includes 指令碼給 xrepo
* 新增 linuxos 內建模組獲取 linux 系統資訊
* [#1217](https://github.com/xmake-io/xmake/issues/1217): 支援當編譯專案時自動拉取工具鏈
* [#1123](https://github.com/xmake-io/xmake/issues/1123): 新增 `rule("utils.symbols.export_all")` 自動匯出所有 windows/dll 中的符號
* [#1181](https://github.com/xmake-io/xmake/issues/1181): 新增 `utils.platform.gnu2mslib(mslib, gnulib)` 模組介面去轉換 mingw/xxx.dll.a 到 msvc xxx.lib
* [#1246](https://github.com/xmake-io/xmake/issues/1246): 改進規則支援新的批處理命令去簡化自定義規則實現
* [#1239](https://github.com/xmake-io/xmake/issues/1239): 新增 `add_extsources` 去改進外部包的查詢
* [#1241](https://github.com/xmake-io/xmake/issues/1241): 支援為 windows 程式新增 .manifest 檔案參與連結
* 支援使用 `xrepo remove --all` 命令去移除所有的包,並且支援模式匹配
* [#1254](https://github.com/xmake-io/xmake/issues/1254): 支援匯出包配置給父 target,實現包配置的依賴繼承
### 改進
* [#1226](https://github.com/xmake-io/xmake/issues/1226): 新增缺失的 Qt 標頭檔案搜尋路徑
* [#1183](https://github.com/xmake-io/xmake/issues/1183): 改進 C++ 語言標準,以便支援 Qt6
* [#1237](https://github.com/xmake-io/xmake/issues/1237): 為 vsxmake 外掛新增 qt.ui 檔案
* 改進 vs/vsxmake 外掛去支援預編譯標頭檔案和智慧提示
* [#1090](https://github.com/xmake-io/xmake/issues/1090): 簡化自定義規則
* [#1065](https://github.com/xmake-io/xmake/issues/1065): 改進 protobuf 規則,支援 compile_commands 生成器
* [#1249](https://github.com/xmake-io/xmake/issues/1249): 改進 vs/vsxmake 生成器去支援啟動工程設定
* [#605](https://github.com/xmake-io/xmake/issues/605): 改進 add_deps 和 add_packages 直接的匯出 links 順序
* 移除廢棄的 `add_defines_h_if_ok` and `add_defines_h` 介面
### Bugs 修復
* [#1219](https://github.com/xmake-io/xmake/issues/1219): 修復版本檢測和更新
* [#1235](https://github.com/xmake-io/xmake/issues/1235): 修復 includes 搜尋路徑中帶有空格編譯不