makefile 不会写,cmake 虽然常用,但文档太烂了,真不好用。前一段有同事说 xmake 不错,试试
安装
官方网站有脚本可以直接下载安装,这里是 github 下载的安装包
./xmake-v2.8.1.gz.run
测试
不会写makefile?没关系,直接在源码目录运行以下命令即可直接编译:
xmake
xmake -P .
xmake f -m release
xmake f -p cross --cross=arm-linux-gnueabihf- -m release
xmake build -j4
xmake install -o install/armhf
xmake uninstall
xmake clean
xmake会自动扫描在当前目录下的源码结构,生成一个xmake.lua工程描述文件,然后尝试直接编译。
想要直接运行编译后的可执行程序,简单,直接敲:
xmake run
创建项目
xmake create -l c++ -t qt.quickapp test
xmake create -l c -t test
xmake project -k vs2017 -m "debug,release"
xmake project -k makefile
静态库
xmake create -l c -t static test
动态库
xmake create -l c -t shared test
手工填写 xmake.lua
target("test")
set_kind("binary")
add_files("src/*.c")
target("test")
set_kind("static")
add_files("src/*.c")
add_files("lib/libxxx.a", "obj/bbb.o")
target("test")
set_kind("binary")
add_files("src/*.c")
if is_mode("debug") then
add_cxflags("-DDEBUG")
end
target("test")
set_kind("binary")
add_files("src/*.c")
after_build(function (target)
os.exec("file %s", target:targetfile())
end)
添加依赖
target("test")
set_kind("static")
add_files("src/test/*.c")
target("hello")
add_deps("test") --添加依赖
set_kind("binary")
add_files("src/hello/*.c")
直接运行
xmake run
如果有多个target目标,你可以指定需要运行的target名,例如:
xmake run test
想要快速调试程序?加上-d参数即可
xmake run -d test
界面
xmake f --menu
可以显示指定 target 配置信息,可以看到各种配置来源于哪个配置文件和具体的行数。
xmake show -t <target>
检查工程配置和代码。
xmake check
xrepo项目
xmake create -l c++ test
xmake.lua 文件
add_rules("mode.debug", "mode.release")
add_requires("cereal")
target("test")
set_kind("binary")
add_files("src/*.cpp")
add_packages("cereal")
github 无法下载,手工下载
mkdir lib && cd lib
wget -O cereal-1.3.2.tar.gz https://ghproxy.com/https://github.com/USCiLab/cereal/archive/refs/tags/v1.3.2.tar.gz
设置搜索库绝对路径
xmake --pkg_searchdirs="/mnt/d/Download/test/lib/"
编译
xmake
看起来可以,不过本地没有头文件,看起来也不太舒服
语义版本设置
Xmake 的依赖包管理是完全支持语义版本选择的,例如:”~1.6.1”,对于语义版本的具体描述见:https://semver.org/
比如下面一些语义版本写法:
add_requires("tbox 1.6.*", "pcre 1.3.x", "libpng ^1.18")
add_requires("libpng ~1.16", "zlib 1.1.2 || >=1.2.11 <1.3.0")
当然,如果我们对当前的依赖包的版本没有特殊要求,那么可以直接这么写:
add_requires("tbox", "libpng", "zlib")
这会使用已知的最新版本包,或者是master分支的源码编译的包,如果当前包有git repo地址,我们也能指定特定分支版本:
add_requires("tbox master")
add_requires("tbox dev")
Xmake 的语义版本支持,在几年前就已经很好的支持,而 vcpkg 也仅仅在最近一年才通过清单模式勉强支持它。
即使现在,vcpkg 对版本语义的支持也很受限,只能支持 >=1.0, 1.0 等几种版本模式,想要选择任意版本的包,比如 >=1.0 <1.5 等复杂版本条件的包,vcpkg 还是无法支持。
可选包设置
如果指定的依赖包当前平台不支持,或者编译安装失败了,那么 Xmake 会编译报错,这对于有些必须要依赖某些包才能工作的项目,这是合理的。 但是如果有些包是可选的依赖,即使没有也可以正常编译使用的话,可以设置为可选包:
add_requires("tbox", {optional = true})
使用系统库
默认的设置,Xmake 会去优先检测系统库是否存在(如果没设置版本要求),如果用户完全不想使用系统库以及第三方包管理提供的库,那么可以设置:
add_requires("tbox", {system = false})
而如果配置成:
add_requires("tbox", {system = true})
就是仅仅查找使用系统库,不会去远程下载安装它,这类似于 CMake 的 find_package,但是集成方式更加简单一致。
使用调试版本的包
如果我们想同时源码调试依赖包,那么可以设置为使用debug版本的包(当然前提是这个包支持debug编译):
add_requires("tbox", {debug = true})
启用包的可选特性
我们也可以安装带有指定特性的包,比如安装开启了 zlib 和 libx265 的 ffmpeg 包。
add_requires("ffmpeg", {configs = {zlib = true, libx265 = true}})
传递额外的编译选项
我们也可以传递额外的编译选项给包:
add_requires("spdlog", {configs = {cxflags = "-Dxxx"}})
独立的包管理命令 Xrepo
Xrepo 是一个基于 Xmake 的跨平台 C/C++ 包管理器。
它是一个独立于 Xmake 的命令程序,用于辅助用户去管理依赖包,类似 vcpkg/conan,但相比它们,有额外多了一些实用的特性,我们会简单介绍一些。
多仓库管理
除了可以直接从官方仓库:xmake-repo 检索安装包之外, 我们还可以添加任意多个自建的仓库,甚至可以完全隔离外网,仅仅在公司内部网络维护私有包的安装集成。
只需要通过下面的命令,添加上自己的仓库地址:
xrepo add-repo myrepo https://github.com/mygroup/myrepo
基本使用
xrepo install zlib tbox
安装指定版本包
完整支持 Semantic Versioning (语义版本)。
xrepo install "zlib 1.2.x"
xrepo install "zlib >=1.2.0"
安装指定平台包
xrepo install -p iphoneos -a arm64 zlib
xrepo install -p android [--ndk=/xxx] zlib
xrepo install -p mingw [--mingw=/xxx] zlib
xrepo install -p cross --sdk=/xxx/arm-linux-musleabi-cross zlib
安装调试版本包
xrepo install -m debug zlib
安装动态库版本包
xrepo install -k shared zlib
安装指定配置包
xrepo install -f "vs_runtime=MD" zlib
xrepo install -f "regex=true,thread=true" boost
安装第三方包管理器的包
xrepo install brew::zlib
xrepo install vcpkg::zlib
xrepo install conan::zlib/1.2.11
查看包的库使用信息
xrepo fetch pcre2 { { linkdirs = { "/usr/local/Cellar/pcre2/10.33/lib" }, links = { "pcre2-8" }, defines = { "PCRE2_CODE_UNIT_WIDTH=8" }, includedirs = "/usr/local/Cellar/pcre2/10.33/include" } }
导入导出安装后的包
xrepo 可以快速导出已经安装后的包,包括对应的库文件,头文件等等。
xrepo export -o /tmp/output zlib
也可以在其他机器上导入之前导出的安装包,实现包的迁移。
xrepo import -i /xxx/packagedir zlib
搜索支持的包
xrepo search zlib "pcr*"
zlib:
-> zlib: A Massively Spiffy Yet Delicately Unobtrusive Compression Library (in xmake-repo)
pcr*:
-> pcre2: A Perl Compatible Regular Expressions Library (in xmake-repo)
-> pcre: A Perl Compatible Regular Expressions Library (in xmake-repo)
另外,现在还可以从 vcpkg, conan, conda 以及 apt 等第三方包管理器中搜索它们的包,只需要加上对应的包命名空间就行,例如:
xrepo search vcpkg::pcre
The package names:
vcpkg::pcre:
-> vcpkg::pcre-8.44
#8: Perl Compatible Regular Expressions -> vcpkg::pcre2-10.35
#2: PCRE2 is a re-working of the original Perl Compatible Regular Expressions library
包虚拟环境管理
我们可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包 shell 环境。
add_requires("zlib 1.2.11")
add_requires("python 3.x", "luajit")
进行虚拟环境:
xrepo env shell
python --version
luajit --version
在 Xmake 中集成第三方构建系统
在 Xmake 中集成 Cmake 项目
Xmake 并不打算分裂 C/C++ 生态,它能很好和兼容复用现有 cmake/autoconf/meson 维护的项目,比如可以将一些其他使用 CMake 维护的代码库,直接本地集成进来,参与混合编译。
也就是说,Xmake 不会强制用户将所有的项目重新 port 到 xmake.lua,现有的 CMake 项目,一样可以快速集成到 Xmake 项目中去。
例如,我们有如下项目结构:
.
├── foo
│ ├── CMakeLists.txt
│ └── src
│ ├── foo.c
│ └── foo.h
├── src
│ └── main.c
├── test.lua
└── xmake.lua
foo 目录下是一个使用 CMake 维护的静态库,而根目录下使用了 Xmake 来维护,我们可以在 xmake.lua 中通过定义 package("foo") 包来描述如何构建 foo 代码库。
add_rules("mode.debug", "mode.release")
package("foo")
add_deps("cmake")
set_sourcedir(path.join(os.scriptdir(), "foo"))
on_install(function (package)
local configs = {}
table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release"))
table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF"))
import("package.tools.cmake").install(package, configs)
end)
on_test(function (package)
assert(package:has_cfuncs("add", {includes = "foo.h"}))
end)
package_end()
add_requires("foo")
target("demo")
set_kind("binary")
add_files("src/main.c")
add_packages("foo")
其中,我们通过 set_sourcedir() 来设置 foo 包的代码目录位置,然后通过 import 导入 package.tools.cmake 辅助模块来调用 cmake 构建代码,xmake 会自动获取生成的 libfoo.a 和对应的头文件。
如果仅仅本地源码集成,我们不需要额外设置 add_urls 和 add_versions。
定义完包后,我们就可以通过 add_requires("foo") 和 add_packages("foo") 来集成使用它了,就跟集成远程包一样的使用方式。
另外,on_test 是可选的,如果想要严格检测包的编译安装是否成功,可以在里面做一些测试。
在 Xmake 中集成 Autoconf 项目
我们也可以使用 package.tools.autoconf 来本地集成带有 autoconf 维护的第三方代码库。
package("libev")
set_sourcedir(path.join(os.scriptdir(), "3rd/libev"))
on_install(function (package)
import("package.tools.autoconf").install(package)
end)
package.tools.autoconf 和 package.tools.cmake 模块都是可以支持 mingw/cross/iphoneos/android 等交叉编译平台和工具链的,xmake 会自动传递对应的工具链进去,用户不需要做任何其他事情。
在 Xmake 中集成 Gn 项目
我们也可以使用 package.tools.gn 来本地集成带有 GN 维护的第三方代码库。
package("skia")
set_sourcedir(path.join(os.scriptdir(), "3rd/skia"))
add_deps("gn", "ninja")
on_install(function (package)
import("package.tools.gn").install(package)
end)
在 Xmake 中查找使用 CMake/C++ 包
现在 CMake 已经是事实上的标准,所以 CMake 提供的 find_package 已经可以查找大量的系统库和模块,我们也可以完全复用 CMake 的这部分生态来扩充 xmake 对包的集成。
只需要像集成 vcpkg/conan 包那样,将包命名空间改成 cmake:: 就可以了。
add_requires("cmake::ZLIB", {alias = "zlib", system = true})
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib")
我们指定 system = true 告诉 xmake 强制从系统中调用 cmake 查找包,如果找不到,不再走安装逻辑,因为 cmake 没有提供类似 vcpkg/conan 等包管理器的安装功能,只提供了包查找特性。
指定版本
add_requires("cmake::OpenCV 4.1.1", {system = true})
指定组件
add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}})}
预设开关
add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}})
相当于内部调用 find_package 查找包之前,在 CMakeLists.txt 中预定义一些配置,控制 find_package 的查找策略和状态。
set(Boost_USE_STATIC_LIB ON)
-- will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system)
设置环境变量
add_requires("cmake::OpenCV", {system = true, configs = {envs = {CMAKE_PREFIX_PATH = "xxx"}}})
指定自定义 FindFoo.cmake 模块脚本目录
mydir/cmake_modules/FindFoo.cmake
add_requires("cmake::Foo", {system = true, configs = {moduledirs = "mydir/cmake_modules"}})
在 Cmake 中集成 Xrepo 依赖包
除了可以在 Xmake 中集成 CMake 项目,我们也可以在 CMake 中直接集成 Xmake/Xrepo 提供的包,只需要使用 xrepo-cmake 提供的 CMake Wrapper。
例如:
cmake_minimum_required(VERSION 3.13.0)
project(foo)
# Download xrepo.cmake if not exists in build directory.
if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake")
message(STATUS "Downloading xrepo.cmake from https://github.com/xmake-io/xrepo-cmake/")
# mirror https://cdn.jsdelivr.net/gh/xmake-io/xrepo-cmake@main/xrepo.cmake
file(DOWNLOAD "https://raw.githubusercontent.com/xmake-io/xrepo-cmake/main/xrepo.cmake" "${CMAKE_BINARY_DIR}/xrepo.cmake" TLS_VERIFY ON)
endif()
# Include xrepo.cmake so we can use xrepo_package function.
include(${CMAKE_BINARY_DIR}/xrepo.cmake)
xrepo_package("zlib")
add_executable(example-bin "")
target_sources(example-bin PRIVATE src/main.cpp )
xrepo_target_packages(example-bin zlib)
添加带有配置的包
我们,也可以跟在 Xmake 中一样,定制包的可选特性。
xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true")
add_executable(example-bin "")
target_sources(example-bin PRIVATE src/main.cpp )
xrepo_target_packages(example-bin gflags)
使用来自第三个存储库的包
除了从 Xmake 官方维护的存储库安装软件包之外,我们也可以直接在 CMake 中使用它来安装来自第三方仓库的包,只需将仓库名称添加为命名空间即可。
例如:vcpkg::zlib, conan::pcre2。
xrepo_package("conan::gflags/2.2.2")
xrepo_package("conda::gflags 2.2.2")
xrepo_package("vcpkg::gflags")
xrepo_package("brew::gflags")
通过这种方式,我们将在 CMake 中集成使用 vcpkg/conan 包的方式进行了统一,并且额外提供了自动包安装特性,以及对 homebrew/conda 等其他包仓库的支持。
xmake-vscode 插件
我们之前的所有实验,都是使用 xmake 的命令行程序在终端下操作完成的,这对于一些初学者来说还是有不少门槛的,并且操作起来也不能够像其它 IDE 等带有可视化界面的开发环境那样顺手,尤其是代码的编辑、编译和断点调试都需要不停的切换各种终端、编辑器环境才能完成。
为了方便我们的日常开发,xmake 官方提供了可以快速无缝集成到 Visual Studio Code 编辑器的 xmake-vscode 插件,使用这个插件我们可以通过 vscode 编辑器环境来一站式进行 C/C++ 程序开发,内置 xmake 编译、断点调试、编译错误分析定位、编译配置的快速切换等各种实用功能。
而 Visual Studio Code 编辑器是微软推出的一款轻量级跨平台的编辑器,具有非常好的跨平台性、可扩展性,我们在实验环境的桌面上,就能找到带有 Visual Studio Code 字样的图标,双击运行就可以打开它。
安装 xmake-vscode 插件
首先在环境中安装 xmake,执行如下命令:
bash <(curl -kfsSL https://labfile.oss.aliyuncs.com/courses/2764/shget.text) v2.3.7
source ~/.xmake/profile
检查版本,验证安装成功
xmake --version
在使用 xmake-vscode 插件之前,我们需要安装它,安装过程很简单,只需要切换到 vscode 的插件扩展 tab 页,然后输入 xmake 搜索相关插件。
如果看到下图所示内容,说明我们已经成功找到了 xmake-vscode 插件,然后点击旁边的 Install 字样按钮就可以完成安装了。
创建和打开 C/C++ 工程
插件安装完成后,我们可以先来创建一个空的 C 工程,之前介绍过可以使用 xmake create 命令来创建工程。但是这里我们打算直接使用 xmake-vscode 插件提供的工程创建功能来完成 C/C++ 工程的创建。
在创建之前,需要先准备一个空目录用来存放工程文件,例如将工程放置在 ~/Code/vscode_test 目录下,如果还不存在此目录,可以先手动创建下。
然后,我们直接在 vscode 里面打开这个空目录,只需要通过点击下图红框位置的按钮。
点击后,我们进入刚刚创建的 vscode_test 目录,选中并打开它。
打开目录后,我们就可以开始创建工程文件了,继续进入菜单的 view 子菜单打开 vscode 里面的命令面板,如下图。
命令面板打开后,我们输入 xmake 字符串,就会看到一系列跟 xmake 插件相关的命令,然后从其中找到带 Create Project 字样的命令。
这个时候,由于当前还没有 xmake.lua 文件,会在底下弹出一个提示框,继续点击里面的蓝色按钮。
接着就会弹出编程语言的选择列表,这里选择第二项,也就是 C++ 语言。
选择完语言后,还会提示选择工程类型,这里选择 console 终端程序类型。
选择完成后,整个项目就创建好了,下图左边就是新创建的工程文件,根目录下有 xmake.lua,而图最底下的工具栏就是 xmake 插件的操作面板了,我们的大部分操作都可以通过这个面板快速完成,包括:编译、运行、调试以及配置切换等等。
工具栏面板介绍
创建完成工程后,我们会看到 vscode 底部工具链出现了一排跟 xmake 相关的操作面板,xmake 的大部分操作都可以通过这个面板上的工具按钮来快速完成。
另外,不仅仅是创建工程,如果安装 xmake-vscode 插件后,使用 vscode 打开一个带有 xmake.lua 文件的 C/C++ 工程根目录,那么 vscode 底部的 xmake 工具栏面板也会被自动激活。
通过上图,我们大概能知道每个按钮的具体功能,具体更进一步的使用方式,我们会接下来会挨个讲解。
编译 C/C++ 程序
刚刚我们通过 vscode 创建的一个 C++ 项目工程,其内部就是执行了 xmake 的 xmake create vscode_test 命令来完成的,整个工程文件结构如下。
.
├── src
│ └── main.cpp
└── xmake.lua
在编译之前,先调整下生成的 xmake.lua 文件,修改成如下配置。
add_rules("mode.debug", "mode.release")
target("test")
set_kind("binary")
add_files("src/*.cpp")
其实,也就是将程序目标名改成 test,然后点击 vscode 底下的 xmake 工具栏里面的 Build 按钮,来编译我们新创建的 C++ 工程。
运行程序
编译完成后就可以尝试运行程序了,还是使用底下的工具栏按钮,点击运行图标。
如果运行成功,就会看到实际的运行输出信息:Hello world!。
调试程序
接下来,我们重点讲解如何通过 vscode 配合 xmake-vscode 插件来实现断点调试 C/C++ 程序。
不过在调试前还需要做一些准备工作,由于默认 xmake 采用 release 模式编译的目标程序,是不带调试符号信息的,因此我们需要先将编译模式切换到 debug 调式编译模式去重新编译它,使其带上调试符号信息。
具体如何切换到 debug 模式,可以参考下图的操作,点击底下 release 文本所在的按钮,然后在上面列出的列表中,选择 debug 项即可。
完成切换后可以看到底下的 release 按钮已经变成了 debug 字样,然后点击 build 按钮,就可以重新编译带有调试符号的目标程序了。
调试版本程序编译完成后,还需要额外做一件事,那就是安装 vscode 的 C/C++ 插件,因为 xmake-vscode 插件的调试功能是基于这个插件的,我们仅仅只需要首次使用时安装它即可。
重新切到插件市场页面,搜索 C/C++,显示出来的第一项就是,点击 Install 安装即可。
安装好 C/C++ 插件,就可以开始调试操作了。首先点击 main.cpp 打开源文件,然后在需要下断点的代码行左侧位置点击下断点,如果出现小红点,说明已经成功下好了调试断点。
断点下好后,就可以点击底下的调试按钮,开启断点调试了,如果一切顺利,程序运行起来后就会命中刚刚设置的断点。
目标程序切换
接下来再来详细讲解编译,之前我们已经使用过 build 按钮来编译工程,但如果一个项目中存在多个目标程序又不想全部编译,我们就需要指定编译哪个目标程序。
在命令行中,我们可以通过 xmake build test 命令来显示的指定编译哪个目标,而在 vscode 中,xmake 插件也提供了很方便的目标切换操作,来快速切换编译。
首先修改 xmake.lua 文件,新增一个名为 test2 的目标程序,用于之后的目标切换测试,例如。
add_rules("mode.debug", "mode.release")
target("test")
set_kind("binary")
add_files("src/*.cpp")
target("test2")
set_kind("binary")
add_files("src/*.cpp")
然后点击底下 default 字样的按钮,之后会在顶部显示整个项目所有目标程序名的列表,我们点击其中的 test2 目标程序,就可以完成切换。
如果切换成功,底下的 default 文本就会变成 test2,由于默认编译 xmake 会自动编译所有目标程序,所以最初显示的是 default 文本。
这个时候,我们再执行编译,就能看到实际仅仅只编译了我们指定的 test2 目标程序。
编译错误信息
如果我们的工程代码没写对导致编译出错,可以直接从编译输出中看到错误信息。
而 xmake-vscode 插件还会自动解析编译错误输出信息,分类每个编译错误,并可通过双击指定错误,跳转定位到指定的错误代码位置,也就是下图所示位置。
查看编译详细信息
在命令行中,我们可以通过 xmake -v 来查看编译过程中的详细命令参数信息,而在 vscode 中同样可以通过配置开启详细输出。
首先打开菜单,点击 File -> Preferences -> Settings 子菜单。
然后在打开的 Setting 配置页面,输入 xmake 找到所有跟 xmake 插件相关的配置项,其中有一项是 BuildLevel,它就是用于设置编译过程中的输出信息级别。
默认是 warnings 级别,仅仅输出编译警告信息以及正常信息,我们把它改为 verbose 级别,就可以输出完整的编译命令行参数了。
至于 debug 级别对应的就是 xmake -vD 的诊断信息,还会进一步打印出错的栈信息。
我们这里将配置切换成 verbose 级别后,再重新构建下程序,看看实际的输出是怎样的。不过由于底部只提供了 build 按钮,没有 rebuild 按钮,为了执行重新编译,我们需要从菜单里面的命令面板中,找到 xmake 的 Rebuild 命令,点击执行才行。
从下图的红色箭头位置找到对应的命令面板。
然后再打开的命令面板中输入 xmake 过滤出所有跟 xmake 相关的命令列表,找到 Rebuild 命令后点击编译即可,如下图。
开启 verbose 级别后,我们就能看到编译输出中完整的命令参数了。
xmake.lua 编辑和自动补全
xmake-vscode 插件还内置了对 xmake.lua 文件编写时的自动提示和补全支持,我们只需要输入 add_、set_ 等字样的文本,就会自动列举出与其相关的所有 api 供我们使用,来方便快速配置 xmake.lua。
我们可以通过编辑 xmake.lua 文件对里面 test2 目标程序添加一个 add_defines("TEST2") 来体验下自动补全功能。
最终的完整配置内容如下。
add_rules("mode.debug", "mode.release")
target("test")
set_kind("binary")
add_files("src/*.cpp")
target("test2")
set_kind("binary")
add_files("src/*.cpp")
add_defines("TEST2")
而我们输入时的补全特性可以通过下面的图片体会到。
完成配置后,我们再来点击底下的 Build 按钮执行编译,看下详细命令输出,应该能够正常看到新加上的 -DTEST2 宏定义了。
简单使用还挺不错,以后尽量不用 cmake 了。
后面那些都是来自官网,我也没看完,备用吧。