之前的版本对编译控制粒度,只能到target这一级:
-- 全局根配置,所有target都会被影响
add_defines("ROOT")
target("test")
-- target目标配置,只对test目标下的所有源文件编译生效
add_defines("TEST")
add_files("src/*.c")
最近给2.1.6开发版本中的add_files
进行了改进,支持基于files更细粒度的编译选项控制,例如:
target("test")
add_defines("TEST1")
add_files("src/*.c")
add_files("test/*.c", "test2/test2.c", {defines = "TEST2", languages = "c99", includedirs = ".", cflags = "-O0"})
可以在add_files
的最后一个参数,传入一个配置table,去控制指定files的编译选项,里面的配置参数跟target的一致,并且这些文件还会继承target的通用配置-DTEST1
。
针对add_files
的更多描述,见手册文档,大家可以下载master版本来预先体验这一新特性。
如果我们要写跨平台的c/c++代码,很多时候需要处理由于不同编译器对c/c++各个标准支持力度不同导致的兼容性问题,一般通常的解决办法是:自己在代码中通过宏去判断各个编译器的版本、内置宏、标准库宏、__has_feature
等来检测处理。
自己如果在代码中按上述的方式检测,会很繁琐,尤其是像c++这种存在大量语法特性,如果一一检测过来,工作量是非常大的。
另外比较省事的方式,就是依赖构建工具提前做好检测,然后把检测结果作为宏添加到编译中去,这样代码只需要判断对应的特性宏是否存在,就可以进行处理了。
在cmake中就有类似的检测机制,非常强大,因此xmake也对其进行了支持,提供更加灵活强大的编译器特性预先检测支持:
target("test")
on_load(function (target)
import("core.tool.compiler")
if compiler.has_features("cxx_constexpr") then
target:add("defines", "HAS_CXX_CONSTEXPR=1")
end
end)
通过core.tool.compiler
模块的compiler.has_features接口,在xmake.lua
中预先判断当前编译期支持的语言特性,实现条件编译。
此处也是参考了cmake的设计,具体详情见:issues#83。
上述代码,在加载target的时候,判断当前编译器是否支持c++的常量表达式语法特性,如果支持则添加宏定义:HAS_CXX_CONSTEXPR=1
。
我们也可以在判断时候,追加一些参数控制编译选项,例如上述特性需要c++11
支持,我们可以启用它:
if compiler.has_features({"c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then
-- ok
end
通过上面的代码可以看到,此接口是可以同时检测多个特性的,返回值为实际支持的特性列表。
此版本带来了大量新特性更新,具体详见:xmake v2.1.5版本新特性介绍。
更多使用说明,请阅读:文档手册。
add_csnippet
,add_cxxsnippet
到option
来检测一些编译器特性find_program
, find_file
, find_library
, find_tool
和find_package
等模块接口net.*
和devel.*
扩展模块val()
接口去获取内置变量,例如:val("host")
, val("env PATH")
, val("shell echo hello")
and val("reg HKEY_LOCAL_MACHINE\\XX;Value")
has_flags
, features
和has_features
等探测模块接口option.on_check
, option.after_check
和 option.before_check
接口target.on_load
接口add_frameworkdirs
接口lib.detect.has_xxx
和lib.detect.find_xxx
接口add_moduledirs
接口在工程中定义和加载扩展模块includes
接口替换add_subdirs
和add_subfiles
xmake project -k compile_commands
来导出compile_commands.json
set_pcheader
和set_pcxxheader
去支持跨编译器预编译头文件,支持gcc
, clang
和msvc
xmake f -p cross
平台用于交叉编译,并且支持自定义平台名includes
和 links
import
接口,去加载用户扩展模块xmake lua
,支持运行单行命令和模块print
接口去更好些显示table数据--root
通用选项去临时支持作为root运行xxx_script
工程描述api,支持多平台模式选择, 例如:on_build("iphoneos|arm*", function (target) end)
*.asm
文件的支持add_bindings
和add_rbindings
为废弃接口xmake rebuild
在windows上的构建速度core.project.task
模块迁移至core.base.task
echo
和 app2ipa
插件迁移到 xmake-plugins 仓库set_config_header("config.h", {prefix = ""})
代替 set_config_h
和 set_config_h_prefix
try-catch-finally
get.sh
的路径问题import()
导入接口的缓存问题最近为了给xmake实现预编译头文件的支持,研究了下各大主流编译器处理预编译头的机制以及之间的一些差异。
现在的大部分c/c++编译器都是支持预编译头的,例如:gcc,clang,msvc等,用于优化c++代码的编译速度,毕竟c++的头文件如果包含了模板定义的话,编译速度是很慢的,
如果能够吧大部分通用的头文件放置在一个header.h
中,在其他源码编译之前预先对其进行编译,之后的代码都能重用这部分预编译头,就可以极大程度上减少频繁的头文件冗余编译。
但是不同编译器对它的支持力度和处理方式,还是有很大差异的,并不是非常通用,在xmake中封装成统一的接口和使用方式,还是费了很大的功夫才搞定。
预编译头在msvc的项目中很常见,经常会看到类似stdafx.cpp
, stdafx.h
的文件,就是用于此目的,而msvc编译器是通过编译stdafx.cpp
来生成预编译头文件stdafx.pch
的。
创建预编译头的命令行如下:
$ cl.exe -c -Yc -Fpstdafx.pch -Fostdafx.obj stdafx.cpp
其中,-Yc
就是创建预编译头stdafx.pch
的意思,通过-Fp
来指定*.pch
的输出文件路径,用-Fo
指定编译stdafx.cpp
生成对象文件。
其他源码是如何使用这个stdafx.pch
的呢,通过将stdafx.h
传入-Yu
来告诉编译器,编译当前代码,忽略#include "stdafx.h"
,直接使用已经编译好的stdafx.pch
文件。
$ cl.exe -c -Yustdafx.h -Fpstdafx.pch -Fotest.obj test.cpp
最后链接的时候,需要把:stdafx.obj
, test.obj
都连接上才行,这个也是和gcc, clang编译器不同的地方。
$ link.exe -out:test test.obj stdafx.obj
注:一定要吧stdafx.obj
也链接上哦,虽然stdafx.cpp
仅用于生成stdafx.pch
,但是对象文件也是需要。
还有个跟gcc, clang有区别的地方是,msvc的-Yu
指定stdafx.h
必须是#include "stdafx.h"
中的头文件名字,不是文件路径哦。
2.1.5版本现已进入收尾阶段,此版本加入了一大波新特性,目前正在进行稳定性测试和修复,在这里,先来介绍下新版本中引入了哪些些新特性和改进。
1. 提供类似cmake的find_*系列接口,实现各种查找,例如:find_package, find_library, find_file, ...
2. 提供模块接口,实现编译器的各种检测,例如:has_features, has_flags, has_cincludes, has_cfuncs, ...
3. 实现大量扩展模块,提供文件下载、解压缩、git操作等接口
4. 支持预编译头文件支持,改进c++编译效率
5. 支持在工程中自定义模块进行扩展
6. 提供代码片段检测接口,实现更加灵活定制化的检测需求
7. 改进option和target,提供更加动态化的配置
8. 通过find_package实现包依赖管理2.0版本
9. 改进root权限问题,实现更加安全的root下运行
10. 提供compile_commands.json导出插件
11. 改进vs201x工程生成插件,支持多模式、多架构同时构建和自由切换不干扰
此接口参考了cmake对于find_*
系列接口的设计,实现在项目中动态的查找和添加包依赖。
target("test")
set_kind("binary")
add_files("*.c")
on_load(function (target)
import("lib.detect.find_package")
target:add(find_package("zlib"))
end)
上述描述代码,通过lib.detect.find_package来查找包,如果找到zlib
包,则将links
, includedirs
和linkdirs
等信息添加到target中去。
2.1.4版本之前,xmake对于包管理,是通过在项目内置pkg/zlib.pkg
方式,来检测链接的,虽然也支持自动检测,但是查找功能有限,并且内置的各个架构的二进制库到项目,对git并不是很友好。
现在通过find_package
和option
,我们可以实现更好的包管理:
option("zlib")
set_showmenu(true)
before_check(function (option)
import("lib.detect.find_package")
option:add(find_package("zlib"))
end)
target("test")
add_options("zlib")
通过定义一个名为zlib的选项作为包,关联到target,在选项被检测之前,先从系统中查找zlib包,如果存在,则添加对应的links
, linkdirs
等配置信息,然后进行选项检测,如果选项检测通过,这个target在编译的时候就会启用zlib。
如果要手动禁用这个zlib包,使其不参与自动检测和链接,只需要:
$ xmake f --zlib=n
$ xmake
注:2.2.1版本将会实现包管理3.0,更加自动化的依赖包管理和使用,具体详情见:Remote package management。
例如:
add_requires("mbedtls master optional")
add_requires("pcre2 >=1.2.0", "zlib >= 1.2.11")
add_requires("git@github.com:glennrp/libpng.git@libpng >=1.6.28")
target("test")
add_packages("pcre2", "zlib", "libpng", "mbedtls")
目前正在努力开发中,尽情期待。。