XMake 是一個基於 Lua 的 現代化 C/C++ 構建系統。html
它的語法簡潔易上手,對新手友好,即便徹底不會 lua 也可以快速入門,而且徹底無任何依賴,輕量,跨平臺。java
同時,它也是一個自知足的構建系統,擁有強大的包管理系統,快速的構建引擎。python
相比 Ninja/Scons/Make 做爲 Build backend,CMake/Meson 做爲 Project Generator,那麼 XMake 就是這二者外加一個包管理。linux
xmake = Build backend + Project Generator + Package Manager
複製代碼
所以,只須要安裝一個不到 3M 的 XMake 安裝包,你就能夠不用再安裝其餘各類工具,甚至連 make 都不須要安裝,也不須要安裝 Python、Java 等重量級的運行時環境,就能夠開始您的 C/C++ 開發之旅。android
也許,有人會說,編譯器總須要安裝的吧。這也不是必須的,由於 XMake 的包管理也支持自動遠程拉取須要的各類編譯工具鏈,好比:llvm, Mingw, Android NDK 或者交叉編譯工具鏈。ios
每當在 Reddit 社區跟別人討論起 XMake,你們老是會拿下面這張圖來吐槽。c++
儘管有些無奈,也被吐槽的有些麻木了,不過我仍是想說明下,作 XMake 的初衷,並非爲了分裂 C/C++ 生態,相反,XMake 儘量地複用了現有生態。git
同時也讓用戶在開發 C/C++ 項目的時候,擁有與其餘語言同樣的良好體驗,好比:Rust/Cargo,Nodejs/Npm, Dlang/Dub,再也不爲處處找第三包,研究如何移植編譯而折騰。github
所以,若是您還不瞭解 XMake,請不要過早下定論,能夠先嚐試使用下,或者花點時間看完下文的詳細介紹。sql
常常有人問我 XMake 有什麼特別之處,相比現有 CMake、Meson 此類構建工具備什麼優點,我爲何要使用 XMake 而不是 CMake?
先說特色和優點,XMake 有如下幾點:
至於 CMake,畢竟已成事實上的標準,生態完善,功能強大。
我從沒想過讓 XMake 去替代它,也替代不了,徹底不是一個量級的,可是 CMake 也有許多爲人所詬病的短板,好比:語法複雜難懂,包管理支持不完善等等。
所以使用 XMake 能夠做爲一種補充,對於那些想要簡單快速入門 C/C++ 開發的新手,或者想要更加方便易用的包管理,或者想臨時快速寫一些短小的測試項目。
XMake 均可以幫他們提高開發效率,讓其更加關注 C/C++ 項目自己,而不是花更多的時間在構建工具和開發環境上。
下面,我來具體介紹 XMake 的這些主要特性。
CMake 本身設計一門 DSL 語言用來作項目配置,這對用戶來說提升了學習成本,並且它的語法可讀性不是很直觀,很容易寫出過於複雜的配置腳本,也提升了維護成本。
而 XMake 複用現有知名的 Lua 語言做爲基礎,而且其上提供了更加簡單直接的配置語法。
Lua 自己就是一門簡單輕量的膠水語言,關鍵字和內置類型就那麼幾種,看個一篇文章,就能基本入門了,而且相比 DSL,可以從網上更方便的獲取到大量相關資料和教程。
不過,仍是有人會吐槽:那不是還得學習 Lua 麼?
其實也不用,XMake 採用了 描述域
和 腳本域
分離的方式,使得初學者用戶在 80% 的狀況下,只須要在描述域以更簡單直接的方式來配置,徹底能夠不把它當成 Lua 腳本,例如:
target("test")
set_kind("binary")
add_files("src/*.c")
add_files("test/*.c", "example/**.cpp")
複製代碼
若是由於,看着有括號,仍是像腳本語言的函數調用,那咱們也能夠這麼寫(是否帶括號看我的喜愛,不過我我的仍是建議使用上面的方式)
target "test"
set_kind "binary"
add_files "src/*.c"
add_files "test/*.c"
add_files "example/**.cpp"
複製代碼
咱們只須要知道經常使用配置接口,即便不徹底不會 Lua 也能快速配置了。
咱們能夠對比下 CMake 的配置:
add_executable(test "")
file(GLOB SRC_FILES "src/*.c")
file(GLOB TEST_FILES "test/*.c")
file(GLOB_RECURSE EXAMPLE_FILES "example/*.cpp")
target_sources(test PRIVATE
${SRC_FILES}
${TEST_FILES}
${EXAMPLE_FILES}
)
複製代碼
哪一個更直觀可讀,一目瞭然。
若是,你已經初步瞭解了一些 Lua 等基礎知識,好比 if then
等條件判斷,那麼能夠進一步作一些條件配置。
target("test")
set_kind("binary")
add_files("src/main.c")
if is_plat("macosx", "linux") then
add_defines("TEST1", "TEST2")
end
if is_plat("windows") and is_mode("release") then
add_cxflags("-Ox", "-fp:fast")
end
複製代碼
繼續對比下 CMake 版本配置:
add_executable(test "")
if (APPLE OR LINUX)
target_compile_definitions(test PRIVATE TEST1 TEST2)
endif()
if (WIN32)
target_compile_options(test PRIVATE $<$<CONFIG:Release>:-Ox -fp:fast>)
endif()
target_sources(test PRIVATE
src/main.c
)
複製代碼
若是你已經晉升爲 XMake 的高端玩家,Lua 語法瞭然於胸,想要更加靈活的定製化配置須要,而且描述域的幾行簡單配置已經知足不了你的需求。
那麼 XMake 也提供了更加完整的 Lua 腳本定製化能力,你能夠寫任何複雜的腳本。
好比在構建以前,對全部源文件進行一些預處理,在構建以後,執行外部 gradle 命令進行後期打包,甚至咱們還能夠重寫內部連接規則,實現深度定製編譯,咱們能夠經過import 接口,導入內置的 linker 擴展模塊,實現複雜靈活的連接過程。
target("test")
set_kind("binary")
add_files("src/*.c")
before_build_file(function (target, sourcefile)
io.replace(sourcefile, "#define HAVE_XXX 1", "#define HAVE_XXX 0")
end)
on_link(function (target)
import("core.tool.linker")
linker.link("binary", "cc", target:objectfiles(), target:targetfile(), {target = target})
end)
after_build(function (target)
if is_plat("android" then
os.cd("android/app")
os.exec("./gradlew app:assembleDebug")
end
end)
複製代碼
若是換成 CMake,也能夠 add_custom_command 裏面實現,不過裏面彷佛只能簡單的執行一些批處理命令,無法作各類複雜的邏輯判斷,模塊加載,自定義配置腳本等等。
固然,使用 cmake 確定也能實現上面描述的功能,但絕對不會那麼簡單。
若是有熟悉 cmake 的人,也能夠嘗試幫忙完成下面的配置:
add_executable(test "")
file(GLOB SRC_FILES "src/*.c")
add_custom_command(TARGET test PRE_BUILD
-- TODO
COMMAND echo hello
)
add_custom_command(TARGET test POST_BUILD
COMMAND cd android/app
COMMAND ./gradlew app:assembleDebug
)
-- How can we override link stage?
target_sources(test PRIVATE
${SRC_FILES}
)
複製代碼
衆所周知,作 C/C++ 相關項目開發,最頭大的就是各類依賴包的集成,因爲沒有像 Rust/Cargo 那樣完善的包管理系統。
所以,咱們每次想使用一個第三方庫,都須要各類找,研究各類平臺的移植編譯,還常常遇到各類編譯問題,極大耽誤了開發者時間,沒法集中精力去投入到實際的項目開發中去。
好不容易當前平臺搞定了,換到其餘平臺,有須要從新折騰一遍依賴包,爲了解決這個問題,出現了一些第三方的包管理器,好比 vcpkg/conan/conda等等,但有些不支持語義版本,有些支持的平臺有限,但無論怎樣,總算是爲解決 C/C++ 庫的依賴管理邁進了很大一步。
可是,光有包管理器,C/C++ 項目中使用它們仍是比較麻煩,由於還須要對應構建工具可以很好的對其進行集成支持才行。
咱們先來看下 CMake 和 Vcpkg 的集成支持:
cmake_minimum_required(VERSION 3.0)
project(test)
find_package(unofficial-sqlite3 CONFIG REQUIRED)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE unofficial::sqlite3::sqlite3)
複製代碼
缺點:
-DCMAKE_TOOLCHAIN_FILE=<vcpkg_dir>/scripts/buildsystems/vcpkg.cmake"
vcpkg install xxx
命令安裝```cmake
cmake_minimum_required(VERSION 2.8.12)
project(Hello)
add_definitions("-std=c++11")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(hello hello.cpp)
target_link_libraries(hello gtest)
複製代碼
conanfile.txt
[requires]
gtest/1.10.0
[generators]
cmake
複製代碼
缺點:
conan install ..
來安裝包我沒找到如何在 Meson 中去使用 vcpkg 包,僅僅找到一篇相關的 Issue #3500 討論。
Meson 彷佛尚未對 Conan 進行支持,可是 Conan 官方文檔上有解決方案,對齊進行支持,可是很複雜,我是沒看會,你們能夠自行研究:docs.conan.io/en/latest/r…
前面講了這麼多,其餘構建工具和包管理的集成,我的感受用起來很麻煩,並且不一樣的包管理器,集成方式差異很大,用戶想要快速從 Vcpkg 切換到 Conan 包,改動量很是大。
接下來,咱們來看看 XMake 中集成使用 Vcpkg 提供的包:
add_requires("vcpkg::zlib", {alias = "zlib"})
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib")
複製代碼
咱們只須要經過 add_requires
配置上對應的包名,以及 vcpkg::
包命名空間,就能直接集成使用 vcpkg 提供的 zlib 包。
而後,咱們只須要執行 xmake 命令,既可完成整個編譯過程,包括 zlib 包的自動安裝,無需額外手動執行 vcpkg install zlib
。
$ xmake
note: try installing these packages (pass -y to skip confirm)?
-> vcpkg::zlib
please input: y (y/n)
=> install vcpkg::zlib .. ok
[ 25%]: compiling.release src\main.cpp
[ 50%]: linking.release test
[100%]: build ok!
複製代碼
接下來是集成 Conan 的包,徹底同樣的方式,僅僅執行換個包管理器名字。
add_requires("conan::zlib", {alias = "zlib"})
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib")
複製代碼
XMake 一樣會自動安裝 conan 中的 zlib 包,而後自動集成編譯。
XMake 跟 CMake 還有其餘構建系統,最大的不一樣點,也就是最大的優點之一,就是它有徹底自建的包管理系統,咱們徹底能夠不依賴 vcpkg/conan,也能夠快速集成依賴包,好比:
add_requires("zlib 1.2.x", "tbox >= 1.6.0")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib", "tbox")
複製代碼
並且,它還支持完整的語義版本選擇,多平臺的包集成,交叉編譯工具鏈的包集成,甚至編譯工具鏈包的自動拉取使用。
不只如此,咱們開能夠對定製化配置對自建包的依賴,例如:
咱們可使用 debug 版本庫,實現對依賴庫的斷點調試。
add_requires("zlib", {debug = true})
複製代碼
add_requires("zlib", {configs = {vs_runtime = "MD"}})
複製代碼
默認集成的是靜態庫,咱們也能夠切換到動態庫。
add_requires("zlib", {configs = {shared = true}})
複製代碼
XMake 的自建包集成支持完整的版本語義規範。
add_requires("zlib 1.2.x")
add_requires("zlib >=1.2.10")
add_requires("zlib ~1.2.0")
複製代碼
默認狀況下,若是版本匹配,XMake 會優先查找使用系統上用戶已經安裝的庫,固然咱們也能夠強制禁止查找使用系統庫,僅僅從自建包倉庫中下載安裝包。
add_requires("zlib", {system = true})
複製代碼
若是依賴包集成失敗,XMake 會自動報錯,中斷編譯,提示用戶:zlib not found
,可是咱們也能夠設置爲可選包集成,這樣的話,即便庫最終沒安裝成功,也不影響項目的編譯,僅僅只是跳過這個依賴。
add_requires("zlib", {optional = true})
複製代碼
好比,集成使用開啓了 context/coroutine 模塊配置的 boost 庫。
add_requires("boost", {configs = {context = true, coroutine = true}})
複製代碼
XMake 除了支持 vcpkg/conan 還有自建倉庫的包集成支持,還支持其餘的包管理倉庫,例如:Conda/Homebrew/Apt/Pacman/Clib/Dub 等等,並且集成方式徹底一致。
用戶可與快速切換使用其餘的倉庫包,而不須要花太多時間去研究如何集成它們。
所以,XMake 並無破壞 C/C++ 生態,而是極大的複用現有 C/C++ 生態的基礎上,努力改進用戶對 C/C++ 依賴包的使用體驗,提升開發效率,讓用戶可以擁有更多的時間去關注項目自己。
爲了方便 XMake 的自建倉庫中的包管理,以及第三方包的管理使用,咱們也提供了獨立的 Xrepo cli 命令工具,來方便的管理咱們的依賴包
咱們可使用這個工具,快速方便的完成下面的管理操做:
xrepo install zlib
xrepo remove zlib
xrepo info zlib
xrepo fetch zlib
xrepo env shell
(這是一個很強大的特性)咱們能夠到 Xrepo 項目主頁 查看更多的介紹和使用方式。
使用 Meson/Scons 須要先安裝 python/pip,使用 Bazel 須要先安裝 java 等運行時環境,而 XMake 不須要額外安裝任何依賴庫和環境,自身安裝包僅僅2-3M,很是的輕量。
儘管 XMake 是基於 lua,可是藉助於 lua 膠水語言的輕量級特性,xmake 已將其徹底內置,所以安裝完 XMake 等同於擁有了一個完整的 lua vm。
有人會說,編譯工具鏈總仍是須要的吧,也不徹底是,Windows 上,咱們提供了預編譯安裝包,能夠直接下載安裝編譯,地址見:Releases
另外,XMake 還支持遠程拉取編譯工具鏈,所以即便你的系統環境,尚未安裝任何編譯器,也不要緊,用戶徹底不用考慮如何折騰編譯環境,只須要在 xmake.lua 裏面配置上須要的工具鏈便可。
好比,咱們在 Windows 上使用 mingw-w64 工具鏈來編譯 C/C++ 工程,只須要作以下配置便可。
add_requires("mingw-w64")
target("test")
set_kind("binary")
add_files("src/*.c")
set_toolchains("mingw@mingw-w64")
複製代碼
經過 set_toolchains
配置綁定 mingw-w64 工具鏈包後,XMake 就會自動檢測當前系統是否存在 mingw-64,若是還沒安裝,它會自動下載安裝,而後完成項目編譯,整個過程,用戶僅僅只須要執行 xmake
這個命令就能完成。
$ xmake
note: try installing these packages (pass -y to skip confirm)?
in xmake-repo:
-> mingw-w64 8.1.0 [vs_runtime:MT]
please input: y (y/n)
=> download https://jaist.dl.sourceforge.net/project/mingw-w64/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z .. ok
checking for mingw directory ... C:\Users\ruki\AppData\Local\.xmake\packages\m\mingw-w64\8.1.0\aad6257977e0449595004d7441358fc5
[ 25%]: compiling.release src\main.cpp
[ 50%]: linking.release test.exe
[100%]: build ok!
複製代碼
除了 mingw-w64,咱們還能夠配置遠程拉取使用其餘的工具鏈,甚至交叉編譯工具鏈,例如:llvm-mingw, llvm, tinycc, muslcc, gnu-rm, zig 等等。
若是你們還想進一步瞭解遠程工具鏈的拉取集成,能夠看下文檔:自動拉取遠程工具鏈。
你們都知道 Ninja 構建很是快,所以不少人都喜歡用 CMake/Meson 生成 build.ninja 後,使用 Ninja 來知足極速構建的需求。
儘管 Ninja 很快,可是咱們仍是須要先經過 meson.build 和 CMakelist.txt 文件生成 build.ninja 才行,這個生成過程也會佔用幾秒甚至十幾秒的時間。
而 XMake 不只僅擁有和 Ninja 近乎相同的構建速度,並且不須要額外再生成其餘構建文件,直接內置構建系統,任何狀況下,只須要一個 xmake
命令就能夠實現極速編譯。
咱們也作過一些對比測試數據,供你們參考:
構建系統 | Termux (8core/-j12) | 構建系統 | MacOS (8core/-j12) |
---|---|---|---|
xmake | 24.890s | xmake | 12.264s |
ninja | 25.682s | ninja | 11.327s |
cmake(gen+make) | 5.416s+28.473s | cmake(gen+make) | 1.203s+14.030s |
cmake(gen+ninja) | 4.458s+24.842s | cmake(gen+ninja) | 0.988s+11.644s |
構建系統 | Termux (-j1) | 構建系統 | MacOS (-j1) |
---|---|---|---|
xmake | 1m57.707s | xmake | 39.937s |
ninja | 1m52.845s | ninja | 38.995s |
cmake(gen+make) | 5.416s+2m10.539s | cmake(gen+make) | 1.203s+41.737s |
cmake(gen+ninja) | 4.458s+1m54.868s | cmake(gen+ninja) | 0.988s+38.022s |
XMake 的另一個特色,就是高效簡單的多平臺編譯,無論你是編譯 windows/linux/macOS 下的程序,仍是編譯 iphoneos/android 又或者是交叉編譯。
編譯的配置方式大同小異,沒必要讓用戶去這折騰研究各個平臺下如何去編譯。
當前本機程序編譯,咱們僅僅只須要執行:
$ xmake
複製代碼
對比 CMake
$ mkdir build
$ cd build
$ cmake --build ..
複製代碼
$ xmake f -p android --ndk=~/android-ndk-r21e
$ xmake
複製代碼
對比 CMake
$ mkdir build
$ cd build
$ cmake -DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r21e/build/cmake/android.toolchain.cmake ..
$ make
複製代碼
$ xmake f -p iphoneos
$ xmake
複製代碼
對比 CMake
$ mkdir build
$ cd build
$ wget https://raw.githubusercontent.com/leetal/ios-cmake/master/ios.toolchain.cmake
$ cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/ios.toolchain.cmake ..
$ make
複製代碼
我沒有找到很方便的方式去配置編譯 ios 程序,僅僅只能從其餘地方找到一個第三方的 ios 工具鏈去配置編譯。
咱們一般只須要設置交叉編譯工具鏈根目錄,XMake 會自動檢測工具鏈結構,提取裏面的編譯器參與編譯,不須要額外配置什麼。
$ xmake f -p cross --sdk=~/aarch64-linux-musl-cross
$ xmake
複製代碼
對比 CMake
咱們須要先額外寫一個 cross-toolchain.cmake 的交叉工具鏈配置文件。
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(TOOL_CHAIN_DIR ~/aarch64-linux-musl)
set(TOOL_CHAIN_INCLUDE ${TOOL_CHAIN_DIR}/aarch64-linux-musl/include)
set(TOOL_CHAIN_LIB ${TOOL_CHAIN_DIR}/aarch64-linux-musl/lib)
set(CMAKE_C_COMPILER "aarch64-linux-gcc")
set(CMAKE_CXX_COMPILER "aarch64-linux-g++")
set(CMAKE_FIND_ROOT_PATH ${TOOL_CHAIN_DIR}/aarch64-linux-musl)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
include_directories(${TOOL_CHAIN_DIR}/aarch64-linux-musl/include)
set(CMAKE_INCLUDE_PATH ${TOOL_CHAIN_INCLUDE})
set(CMAKE_LIBRARY_PATH ${TOOL_CHAIN_LIB})
複製代碼
$ mkdir build
$ cd build
$ cmake -DCMAKE_TOOLCHAIN_FILE=../cross-toolchain.cmake ..
$ make
複製代碼
若是你是 C/C++ 開發的新手,能夠經過 XMake 快速上手入門 C/C++ 編譯構建。
若是你想開發維護跨平臺 C/C++ 項目,也能夠考慮使用 XMake 來維護構建,提升開發效率,讓你更加專一於項目自己,再也不爲折騰移植依賴庫而煩惱。
歡迎關注 XMake 項目: