0c316ae32610ee66b87f804f54e350d9
xmake从入门到精通4:常用C/C++项目描述设置详解

xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。

本文主要详细讲解如何编写一些常用的基础xmake.lua描述配置,来实现一些简单的C/C++项目构建管理。
对于大部分小项目,这些配置已经完全足够使用,本系列后期进阶教程中,我会深入详细讲解如果使用一些高级特性来更加灵活定制化地配置项目。

先来一段最简短的

一行描述即可编译src目录下所有c源文件,然后生成一个名为demo的可执行文件。

target("demo", {kind = "binary", files = "src/*.c"})

上面的写法是精简写法,通常我们更推荐使用下面展开式写法:

target("demo")
    set_kind("binary")
    add_files("src/*.c")

这两者完全等价,如果配置很简短,可以完全精简成一行,而拆分成多行更加方便灵活配置。

如果没有特殊目的,下文我们都会采用第二段的写法。

配置项目目标类型

通常的C/C++项目生成的目标文件猪油三大类:可执行程序,静态库,动态库。

我们可以通过set_kind()配置来设置,分别对应:binary, static, shared

例如,我们想要编译动态库,只需要修改kind:

target("demo")
    set_kind("shared")
    add_files("src/*.c")

添加宏定义

编译宏的设置,大多数c/c++项目都会用到,一般如果我们设置编译flags传给gcc/clang,都是要配置:-DXXX

而在xmake里面,提供了add_defines()内置接口来配置:

target("demo")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("XXX")

条件配置

那如果我们想在不同编译平台,分别设置不同的宏开关呢?我们可以利用lua内置的if语句很方便的实现:

target("demo")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("XXX")
    if is_plat("linux", "macosx") then
        add_defines("YYY")
    end

我们通过is_plat()判断,如果当前编译目标平台是linux或者macosx,那么target会额外增加-DYYY宏定义。

全局配置

我们在target("demo")下面的所有配置,都属于demo这个target子域,并不是全局的,所以你会看到通常配置上都加了缩进,就是为了凸显作用域的影响范围。

通常如果多个target连续定义,下一个target定义就会自动结束上个target的作用域,每个target的配置完全独立,互不干扰:

target("test1")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("TEST1")

target("test2")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("TEST2")

例如,上面的配置两个target,各自拥有自己独立的宏定义:TEST1TEST2

那么,我们要对这两个target,设置共用的宏定义,应该如何配置呢?

每个target下面都配置一遍add_defines("TEST")? 当然可以,不过这样就有点冗余了,配置多了就会很难维护,其实我们只需要放置到全局根作用域就行了:

-- 全局设置
add_defines("TEST")
if is_arch("arm64", "armv7") then
    add_defines("ARM")
end

target("test1")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("TEST1")

target("test2")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("TEST2")

在target的外层的所有配置都属于全局配置,我们也可以调用target_end()强制结束target子域,切回全局作用域:

target("test1")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("TEST1")
target_end()

-- 全局设置
add_defines("TEST")
if is_arch("arm64", "armv7") then
    add_defines("ARM")
end

target("test2")
    set_kind("shared")
    add_files("src/*.c")
    add_defines("TEST2")
target_end()

添加编译选项

如果有些编译选项,xmake没有提供内置api设置,那么我们可以退化到add_cflags, add_cxflags, add_cxxflags来设置,
不过这就需要用户自己去判断编译平台了,因为并不是所有编译flags每个平台都支持。

比如:

add_cflags("-g", "-O2", "-DDEBUG")
if is_plat("windows") then
    add_cflags("/MT")
end

所有选项值都基于gcc的定义为标准,如果其他编译器不兼容(例如:vc),xmake会自动内部将其转换成对应编译器支持的选项值。
用户无需操心其兼容性,如果其他编译器没有对应的匹配值,那么xmake会自动忽略器设置。

我们也可以通过force参数来强制禁用flags的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置:

add_cflags("-g", "-O2", {force = true})

那如何知道,哪些flags检测失败给忽略了呢,带-v编译就可以看到,比如:

$ xmake -v
checking for the /usr/bin/xcrun -sdk macosx clang ... ok
checking for the flags (-Oz) ... ok
checking for the flags (-Wno-error=deprecated-declarations) ... ok
checking for the flags (-fno-strict-aliasing) ... ok
checking for the flags (-Wno-error=expansion-to-defined) ... no

最后备注下这三个api的区别:

  • add_cflags:仅添加C代码相关编译flags
  • add_cxflags:添加C/C++代码相关编译flags
  • add_cxxflags:仅添加C++代码相关编译flags

添加库相关设置

top Created with Sketch.