您好,登錄后才能下訂單哦!
本篇內容介紹了“xmake v2.5.2有哪些新特性”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
xmake 是一個基于 Lua 的輕量級跨平臺構建工具,使用 xmake.lua 維護項目構建,相比 makefile/CMakeLists.txt,配置語法更加簡潔直觀,對新手非常友好,短時間內就能快速入門,能夠讓用戶把更多的精力集中在實際的項目開發(fā)上。
在 2.5.2 版本中,我們增加了一個重量級的新特性:自動拉取遠程交叉編譯工具鏈
。
這是用來干什么的呢,做過交叉編譯以及有 C/C++ 項目移植經(jīng)驗的同學應該知道,折騰各種交叉編譯工具鏈,移植編譯項目是非常麻煩的一件事,需要自己下載對應工具鏈,并且配置工具鏈和編譯環(huán)境很容易出錯導致編譯失敗。
現(xiàn)在,xmake 已經(jīng)可以支持自動下載項目所需的工具鏈,然后使用對應工具鏈直接編譯項目,用戶不需要關心如何配置工具鏈,任何情況下只需要執(zhí)行 xmake
命令即可完成編譯。
甚至對于 C/C++ 依賴包的集成,也可以自動切換到對應工具鏈編譯安裝集成,一切完全自動化,完全不需要用戶操心。
除了交叉編譯工具鏈,我們也可以自動拉取工具鏈,比如特定版本的 llvm,llvm-mingw, zig 等各種工具鏈來參與編譯 C/C++/Zig 項目的編譯。
即使是 cmake 也還不支持工具鏈的自動拉取,頂多只能配合 vcpkg/conan 等第三方包管理對 C/C++ 依賴包進行集成,另外,即使對于 C/C++依賴包,xmake 也有自己原生內置的包管理工具,完全無任何依賴。
項目源碼
官方文檔
入門課程
從 2.5.2 版本開始,我們可以拉取指定的工具鏈來集成編譯項目,我們也支持將依賴包切換到對應的遠程工具鏈參與編譯后集成進來。
相關例子代碼見:Toolchain/Packages Examples
相關的 issue #1217
當前,我們已經(jīng)在 xmake-repo 倉庫收錄了以下工具鏈包,可以讓 xmake 遠程拉取集成:
llvm
llvm-mingw
gnu-rm
muslcc
zig
雖然現(xiàn)在支持的工具鏈包不多,當?shù)钦w架構已經(jīng)打通,后期我們只需要收錄更多的工具鏈進來就行,比如:gcc, tinyc, vs-buildtools 等工具鏈。
由于 xmake 的包支持語義版本,因此如果項目依賴特定版本的 gcc/clang 編譯器,就不要用戶去折騰安裝了,xmake 會自動檢測當前系統(tǒng)的 gcc/clang 版本是否滿足需求。
如果版本不滿足,那么 xmake 就會走遠程拉取,自動幫用戶安裝集成特定版本的 gcc/clang,然后再去編譯項目。
我們使用 llvm-10 中的 clang 來編譯項目。
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")
,后面的名字是需要被關聯(lián)工具鏈包名,也就是 package("llvm")
,不過如果設置了別名,那么會優(yōu)先使用別名:llvm-10
另外,我們后續(xù)還會增加 gcc 工具鏈包到 xmake-repo,使得用戶可以自由切換 gcc-10, gcc-11 等特定版本的 gcc 編譯器,而無需用戶去手動安裝。
我們也可以拉取指定的交叉編譯工具鏈來編譯項目。
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
切換到 aarch74-linux-musl-
目標平臺來進行交叉編譯。
我們也可以使用指定的muslcc交叉編譯工具鏈去編譯和集成所有的依賴包。
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 命令,就可以完成整個交叉編譯環(huán)境的集成以及架構切換。
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")
xmake 會先下載特定版本的 zig 工具鏈,然后使用此工具鏈編譯 zig 項目,當然用戶如果已經(jīng)自行安裝了 zig 工具鏈,xmake 也會自動檢測對應版本是否滿足,如果滿足需求,那么會直接使用它,無需重復下載安裝。
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 內置的 c/c++ 編譯器,可以完全獨立進行 c/c++ 代碼的編譯和鏈接,完全不依賴 gcc/clang/msvc,非常給力。
因此,我們完全可以使用它來編譯 c/c++ 項目,關鍵是 zig 的工具鏈還非常輕量,僅僅幾十M 。
我們只需要切換到 zig 工具鏈即可完成編譯:
$ 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,即可實現(xiàn)對 arm64 的交叉編譯,例如:
$ xmake f -a arm64 --toolchain=zig
$ xmake
[ 25%]: compiling.release src/main.c
"zig cc" -c -target aarch74-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 aarch74-macos-gnu -arch arm64 -stdlib=libc++ -Wl,-x -lz
[100%]: build ok!
即使你是在在 macOS,也可以用 zig cc
去交叉編譯 windows/x64 目標程序,相當于替代了 mingw 干的事情。
$ xmake f -p windows -a x64 --toolchain=zig
$ xmake
cmake 中有這樣一個功能:WINDOWS_EXPORT_ALL_SYMBOLS
,安裝 cmake 文檔中的說法:
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.
大體意思就是:
啟用此布爾屬性,可以自動創(chuàng)建一個模塊定義(.def)文件,其中包含在Windows上的共享庫(或使用ENABLE_EXPORTS的可執(zhí)行文件)的輸入.obj文件中找到的所有全局符號。
模塊定義文件將被傳遞給鏈接器,使所有符號從.dll中導出。對于全局數(shù)據(jù)符號,當對.dll中的代碼進行編譯時,仍然必須使用__declspec(dllimport)。
所有其它的函數(shù)符號將被調用者自動導出和導入。這就簡化了將項目移植到 Windows 的過程,減少了對顯式 dllexport 標記的需求,甚至在 C++ 類中也是如此。
現(xiàn)在,xmake 中也提供了類似的特性,可以快速全量導出 windows/dll 中的符號,來簡化對第三方項目移植過程中,對符號導出的處理。另外,如果項目中的符號太多,也可以用此來簡化代碼中的顯式導出需求。
我們只需在對應生成的 dll 目標上,配置 utils.symbols.export_all
規(guī)則即可。
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 實現(xiàn)快速全量導出。
這個特性也是參考自 CMAKE_GNUtoMS 功能,可以把MinGW生成的動態(tài)庫(xxx.dll & xxx.dll.a)轉換成Visual Studio可以識別的格式(xxx.dll & xxx.lib),從而實現(xiàn)混合編譯。
這個功能對Fortran & C++混合項目特別有幫助,因為VS不提供fortran編譯器,只能用MinGW的gfortran來編譯fortran部分,然后和VS的項目鏈接。
往往這樣的項目同時有一些其他的庫以vs格式提供,因此純用MinGW編譯也不行,只能使用cmake的這個功能來混合編譯。
因此,xmake 也提供了一個輔助模塊接口去支持它,使用方式如下:
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
為了簡化用戶自定義 rule 的配置,xmake 新提供了 on_buildcmd_file
, on_buildcmd_files
等自定義腳本入口,
我們可以通過 batchcmds 對象,構造一個批處理命令行任務,xmake 在實際執(zhí)行構建的時候,一次性執(zhí)行這些命令。
這對于 xmake project
此類工程生成器插件非常有用,因為生成器生成的第三方工程文件并不支持 on_build_files
此類內置腳本的執(zhí)行支持。
但是 on_buildcmd_file
構造的最終結果,就是一批原始的 cmd 命令行,可以直接給其他工程文件作為 custom commands 來執(zhí)行。
另外,相比 on_build_file
,它也簡化對擴展文件的編譯實現(xiàn),更加的可讀易配置,對用戶也更加友好。
rule("foo")
set_extensions(".xxx")
on_buildcmd_file(function (target, batchcmds, sourcefile, opt)
batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile})
end)
除了 batchcmds:vrunv
,我們還支持一些其他的批處理命令,例如:
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 = ""}})
同時,我們在里面也簡化對依賴執(zhí)行的配置,下面是一個完整例子:
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)
我們從上面的配置可以看到,整體執(zhí)行命令列表非常清晰,而如果我們用 on_build_file
來實現(xiàn),可以對比下之前這個規(guī)則的配置,就能直觀感受到新接口的配置方式確實簡化了不少:
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)
關于遠程依賴包定義這塊,我們也新增了 add_extsources
和 on_fetch
兩個配置接口,可以更好的配置 xmake 在安裝 C/C++ 包的過程中,對系統(tǒng)庫的查找過程。
至于具體背景,我們可以舉個例子,比如我們在 xmake-repo 倉庫新增了一個 package("libusb")
的包。
那么用戶就可以通過下面的方式,直接集成使用它:
add_requires("libusb")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("libusb")
如果用戶系統(tǒng)上確實沒有安裝 libusb,那么 xmake 會自動下載 libusb 庫源碼,自動編譯安裝集成,沒啥問題。
但如果用戶通過 apt install libusb-1.0
安裝了 libusb 庫到系統(tǒng),那么按理 xmake 應該會自動優(yōu)先查找用戶安裝到系統(tǒng)環(huán)境的 libusb 包,直接使用,避免額外的下載編譯安裝。
但是問題來了,xmake 內部通過 find_package("libusb")
并沒有找打它,這是為什么呢?因為通過 apt 安裝的 libusb 包名是 libusb-1.0
, 而不是 libusb。
我們只能通過 pkg-config --cflags libusb-1.0
才能找到它,但是 xmake 內部的默認 find_package 邏輯并不知道 libusb-1.0
的存在,所以找不到。
因此為了更好地適配不同系統(tǒng)環(huán)境下,系統(tǒng)庫的查找,我們可以通過 add_extsources("pkgconfig::libusb-1.0")
去讓 xmake 改進查找邏輯,例如:
package("libusb")
add_extsources("pkgconfig::libusb-1.0")
on_install(function (package)
-- ...
end)
另外,我們也可以通過這個方式,改進查找 homebrew/pacman 等其他包管理器安裝的包,例如:add_extsources("pacman::libusb-1.0")
。
如果不同系統(tǒng)下安裝的系統(tǒng)庫,僅僅只是包名不同,那么使用 add_extsources
改進系統(tǒng)庫查找已經(jīng)足夠,簡單方便。
但是如果有些安裝到系統(tǒng)的包,位置更加復雜,想要找到它們,也許需要一些額外的腳本才能實現(xiàn),例如:windows 下注冊表的訪問去查找包等等,這個時候,我們就可以通過 on_fetch
完全定制化查找系統(tǒng)庫邏輯。
還是以 libusb 為例,我們不用 add_extsources
,可以使用下面的方式,實現(xiàn)相同的效果,當然,我們可以在里面做更多的事情。
package("libusb")
on_fetch("linux", function(package, opt)
if opt.system then
return find_package("pkgconfig::libusb-1.0")
end
end)
在新版本中,我們還新增了對 windows .manifest
文件的支持,只需要通過 add_files
添加進來即可。
add_rules("mode.debug", "mode.release")
target("test")
set_kind("binary")
add_files("src/*.cpp")
add_files("src/*.manifest")
關于 xrepo 命令,我們也稍微改進了下,現(xiàn)在可以通過下面的命令,批量卸載刪除已經(jīng)安裝的包,支持模式匹配:
$ xrepo remove --all
$ xrepo remove --all zlib pcr*
我們也改進了 add_packages
,使其也支持 {public = true}
來導出包配置給父 target。
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 庫
至于具體導出哪些配置呢?
-- 默認私有,但是 links/linkdirs 還是會自動導出
add_packages("pcre2")
-- 全部導出。包括 includedirs, defines
add_packages("pcre2", {public = true})
#955: 支持 zig cc
和 zig c++
作為 c/c++ 編譯器
#955: 支持使用 zig 進行交叉編譯
#1177: 改進終端和 color codes 探測
#1216: 傳遞自定義 includes 腳本給 xrepo
添加 linuxos 內置模塊獲取 linux 系統(tǒng)信息
#1217: 支持當編譯項目時自動拉取工具鏈
#1123: 添加 rule("utils.symbols.export_all")
自動導出所有 windows/dll 中的符號
#1181: 添加 utils.platform.gnu2mslib(mslib, gnulib)
模塊接口去轉換 mingw/xxx.dll.a 到 msvc xxx.lib
#1246: 改進規(guī)則支持新的批處理命令去簡化自定義規(guī)則實現(xiàn)
#1239: 添加 add_extsources
去改進外部包的查找
#1241: 支持為 windows 程序添加 .manifest 文件參與鏈接
支持使用 xrepo remove --all
命令去移除所有的包,并且支持模式匹配
#1254: 支持導出包配置給父 target,實現(xiàn)包配置的依賴繼承
#1226: 添加缺失的 Qt 頭文件搜索路徑
#1183: 改進 C++ 語言標準,以便支持 Qt6
#1237: 為 vsxmake 插件添加 qt.ui 文件
改進 vs/vsxmake 插件去支持預編譯頭文件和智能提示
#1090: 簡化自定義規(guī)則
#1065: 改進 protobuf 規(guī)則,支持 compile_commands 生成器
#1249: 改進 vs/vsxmake 生成器去支持啟動工程設置
#605: 改進 add_deps 和 add_packages 直接的導出 links 順序
移除廢棄的 add_defines_h_if_ok
and add_defines_h
接口
#1219: 修復版本檢測和更新
#1235: 修復 includes 搜索路徑中帶有空格編譯不過問題
“xmake v2.5.2有哪些新特性”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。