Introduction to the new features of xmake v2.1.5

The 2.1.5 version has now entered the final stage. This version has added a large wave of new features. It is currently undergoing stability testing and repair. Here, let's first introduce what new features and improvements have been introduced in the new version.

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工程生成插件,支持多模式、多架构同时构建和自由切换不干扰

Use find_package to find dependent packages

This interface refers to the cmake find_*'s design of a series of interfaces to realize dynamic search and add package dependencies in the project.

target("test")
    set_kind("binary")
    add_files("*.c")
    on_load(function (target)
        import("lib.detect.find_package")
        target:add(find_package("zlib"))
    end)

The above description code uses lib.detect.find_package to find the package. If the package is found zlib, then add links, includedirsand linkdirsother information to the target.

Implement package management 2.0

Before the 2.1.4 version, xmake used the built-in pkg/zlib.pkgmethod of the project to detect the link for package management. Although it also supports automatic detection, the search function is limited, and the built-in binary libraries of various architectures to the project are not very good for git. friendly.

Now through find_packageand option, we can achieve better package management:

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")

By defining a package named as zlib option, associated with target, before the options is checked to find zlib package from the system, if present, is added to the corresponding links, linkdirsother configuration information, and then detecting the option, if the option is detected by , This target will enable zlib when compiling.

If you want to manually disable this zlib package so that it does not participate in automatic detection and linking, you only need to:

$ xmake f --zlib=n 
$ xmake

Note: Version 2.2.1 will implement package management 3.0, more automated dependent package management and use, see: Remote package management for specific details .

E.g:

add_requires("mbedtls master optional")
add_requires("pcre2 >=1.2.0", "zlib >= 1.2.11")
add_requires("[email protected]:glennrp/libpng.git@libpng >=1.6.28")
target("test")
    add_packages("pcre2", "zlib", "libpng", "mbedtls")

It is currently under development, so please look forward to it. .

Custom extension of the module

We can xmake.luaexpand the modules directory by specifying at the beginning of the project file:

add_moduledirs("$(projectdir)/xmake/modules")

In this way, xmake can find custom extension modules, for example:

projectdir
 - xmake
   - modules
     - detect/package/find_openssl.lua

By adding a find_openssl.luascript to the custom project module directory , it can be extended find_packageto make the package search more accurate.

Here is a summary of find_packagethe search order:

  1. If you specify a {packagedirs = ""}parameter, the local package will be searched first from the path specified by this parameter*.pkg
  2. If xmake/modulesthere is a detect.packages.find_xxxscript below , then try to call this script to improve the search results
  3. If the system exists pkg-configand the library of the system environment is searched, try pkg-configto find it using the provided path and link information
  4. If the system exists homebrew, and the library of the system environment is searched, try brew --prefix xxxto find it using the provided information
  5. Parameters specified in the path pathes path and some known systems /usr/lib, /usr/includethe lookup

Quickly judge the compiler feature detection support

Through core.tool.compilerthe compiler.has_features interface of the module , you xmake.luacan prejudge the language features supported by the current compilation period in the module to achieve conditional compilation.

Here is also a reference to the cmake design, see: issues#83 for specific details .

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)

The code, when loading the target, and determine the current compiler supports constant expression syntax characteristic of c ++, if you add support for macro definitions: HAS_CXX_CONSTEXPR=1.

We can also add some parameters to control compilation options when judging, for example, the above features need to be c++11supported, we can enable it:

if compiler.has_features({
   
   "c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then
    -- ok
end

If the target has been set before c++11, then we can also pass in the target object and inherit all the settings of the target:

if compiler.has_features("cxx_constexpr", {target = target, defines = "..", includedirs = ".."}) then
    -- ok
end

For a list of all c/c++ compiler features, see: compiler.features

Determine whether the specified c/c++ header file exists

Use lib.detect.has_cincludes to detect the existence of c header files.

import("lib.detect.has_cincludes")

local ok = has_cincludes("stdio.h")
local ok = has_cincludes({
   
   "stdio.h", "stdlib.h"}, {target = target})
local ok = has_cincludes({
   
   "stdio.h", "stdlib.h"}, {defines = "_GNU_SOURCE=1", languages = "cxx11"})

For the detection of c++ header files, see: lib.detect.has_cxxincludes

Determine whether the specified c/c++ function exists

Through lib.detect.has_cfuncs to detect whether the c function exists.

import("lib.detect.has_cfuncs")

local ok = has_cfuncs("setjmp")
local ok = has_cfuncs({
   
   "sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"})

For the detection of c++ functions, see: lib.detect.has_cxxfuncs .

Determine whether the specified c/c++ type exists

Through lib.detect.has_ctypes to detect whether the c function exists.

import("lib.detect.has_ctypes")

local ok = has_ctypes("wchar_t")
local ok = has_ctypes({
   
   "char", "wchar_t"}, {includes = "stdio.h"})
local ok = has_ctypes("wchar_t", {includes = {
   
   "stdio.h", "stdlib.h"}, "defines = "_GNU_SOURCE=1", languages = "cxx11"})

For c++ type detection, see: lib.detect.has_cxxtypes .

Check whether the c/c++ code fragment can be compiled

The universal c/c++ code snippet detection interface, by passing in multiple code snippet lists, it will automatically generate a compilation file, and then compile it with common sense, and return true if the compilation passes.

For some complex compiler features, when even compiler.has_features cannot be detected, you can use this interface to detect it by trying to compile.

import("lib.detect.check_cxsnippets")

local ok = check_cxsnippets("void test() {}")
local ok = check_cxsnippets({
   
   "void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"})

This interface is a general version of interfaces such as detect.has_cfuncs , detect.has_cincludes and detect.has_ctypes , and it is also more low-level.

So we can use it to detect: types, functions, includes and links, or combine them to detect.

The first parameter is a list of code snippets, which is generally used for the detection of some custom features. If it is empty, you can only detect the conditions in the optional parameters, for example:

local ok = check_cxsnippets({}, {types = {
   
   "wchar_t", "char*"}, includes = "stdio.h", funcs = {
   
   "sigsetjmp", "sigsetjmp((void*)0, 0)"}})

The above call will check whether the types, includes and funcs are all satisfied at the same time, and return true if it passes.

More powerful xmake lua plugin

In version 2.1.4, this plug-in already supports REPL (read-eval-print), which enables interactive operation to facilitate testing modules:

$ xmake lua
> 1 + 2
3

> a = 1
> a
1

> for _, v in pairs({
   
   1, 2, 3}) do
>> print(v)
>> end
1
2
3

Now you can test the module interface more quickly with one line of command:

$ xmake lua lib.detect.find_package openssl

The returned results are as follows:{links = {"ssl", "crypto", "z"}, linkdirs = {"/usr/local/lib"}, includedirs = {"/usr/local/include"}}

Precompiled header file support

xmake adds pre-compiled header files to speed up c/c++program compilation, currently supported compilers are: gcc, clang and msvc.

The usage is as follows:

target("test")
    set_precompiled_header("header.h")

Normally, to set the pre-compilation of the c header file, you need to add this configuration. If it is the pre-compilation of the c++ header file, change it to

target("test")
    set_precompiled_header("header.hpp")

The parameter specifies the path of the header file that needs to be precompiled, relative to the current xmake.luadirectory.

If you just call the xmake command line for direct compilation, then the above settings are sufficient, and various compilers have been supported, but in some cases, the above settings cannot meet the requirements:

  1. If you want to use the xmake projectproject plug-in to generate the vs project file, there is still a lack of a similar stdafx.cppfile (the above setting will automatically generate a temporary when msvc compiles, but it is not friendly to IDE projects).
  2. If you header.hwant to use gcc/clang as a precompiled header file for c++, it will not be supported unless you change it header.hpp(the default will be precompiled as a c header file).

Therefore, in order to be more versatile cross-platform, you can create a similar project in vc inside stdafx.cppsource file: header.cpp.

target("test")
    set_precompiled_header("header.h", "header.cpp")

header.cppThe contents are as follows:

#include "header.h"

The above settings can handle the pre-compilation processing in various situations well, and the addition header.cppalso tells xmake: it header.his pre-compiled as C++.

Compared with the stdafx.cppsum in the classic vc project stdafx.h, it can also perfectly support:

target("test")
    set_precompiled_header("stdafx.h", "stdafx.cpp")

Generate compiler_commands plugin

Extension xmake projectproject generation plug-in, support compiler_commands.jsonfile output, used to export the compilation information of each source file, generate a compiled database file based on clang, in json format, which can be used to interact with ide, editor, and static analysis tools.

$ xmake project -k compile_commands

The output content format is as follows:

[
  { "directory": "/home/user/llvm/build",
    "command": "/usr/bin/clang++ -Irelative -DSOMEDEF=\"With spaces, quotes and \\-es.\" -c -o file.o file.cc",
    "file": "file.cc" },
  ...
]

Generally used to integrate with IDEs, editor plug-ins, and static analysis tools. For compile_commandsdetailed instructions, see: https://clang.llvm.org/docs/JSONCompilationDatabase.html ">JSONCompilationDatabase

Custom option detection script

Before option detection, some configuration conditions are dynamically added:

option("zlib")
    before_check(function (option)
        import("lib.detect.find_package")
        option:add(find_package("zlib"))
    end)

By overwriting the detection script, the detection result of the control option:

option("test")
    add_deps("small")
    set_default(true)
    on_check(function (option)
        if option:dep("small"):enabled() then
            option:enable(false)
        end
    end)

If the test dependent option passes, the test option is disabled.

After the option detection is completed, execute this script to do some post-processing, or you can disable the option again at this time:

option("test")
    add_deps("small")
    add_links("pthread")
    after_check(function (option)
        option:enable(false)
    end)

Custom target load script

When the target is initialized and loaded, on_load will be executed , and some dynamic target configuration can be done in it to achieve more flexible target description definition, for example:

target("test")
    on_load(function (target)
        target:add("defines", "DEBUG", "TEST=\"hello\"")
        target:add("linkdirs", "/usr/lib", "/usr/local/lib")
        target:add({includedirs = "/usr/include", "links" = "pthread"})
    end)

You can dynamically add various target attributes on_loadthrough target:set, inside target:add.

Target custom build script supports sub-platform architecture

By setting 平台|架构parameters, the execution conditions of custom scripts are controlled, so that different scripts can be called to build under different platforms and architectures:

target("test")
    on_build("iphoneos|arm*", function (target) 
        -- TODO
    end)

Or for all macosx platforms, execute the script:

target("test")
    after_build("macosx", function (target) 
        -- TODO
    end)

Other scripts, such as: on_clean, before_packageetc. are also supported Oh, and before 2.1.4 only supports:

target("test")
    on_package(function (target) 
        -- TODO
    end)

It cannot deal with different architectures and platforms separately.

Get the value of a built-in variable

Built-in variables can be directly obtained through this interface, without the need for additional $()packages, which makes it easier to use, for example:

print(val("host"))
print(val("env PATH"))
local s = val("shell echo hello")

Using vformat is more cumbersome:

local s = vformat("$(shell echo hello)")

However, it vformatsupports string parameter formatting and is more powerful, so the application scenarios are different.

Goal depends on achieving property inheritance

In versions prior to 2.1.4, target.add_deps is only used to add dependencies and modify the compilation order:

target("test1")
    set_kind("static")
    set_files("*.c")

target("test2")
    set_kind("static")
    set_files("*.c")

target("demo")
    add_deps("test1", "test2")
    add_links("test1", "test2")
    add_linkdirs("test1dir", "test2dir")

After version 2.1.5, the target will also automatically inherit the configuration and attributes in the dependent target, no additional calls are needed add_links, add_includedirsand the add_linkdirsinterface to associate the dependent target with the other interface is no longer required . The above code can be simplified to:

target("test1")
    set_kind("static")
    set_files("*.c")

target("test2")
    set_kind("static")
    set_files("*.c")

target("demo")
    add_deps("test1", "test2") -- 会自动链接依赖目标

And the inheritance relationship supports cascade, for example:

target("library1")
    set_kind("static")
    add_files("*.c")
    add_headers("inc1/*.h")

target("library2")
    set_kind("static")
    add_deps("library1")
    add_files("*.c")
    add_headers("inc2/*.h")

target("test")
    set_kind("binary")
    add_deps("library2")

New search tool interface

The lib.detect.find_tool interface is used to find executable programs. It is more advanced and powerful than lib.detect.find_program . It encapsulates executable programs and provides the concept of tools:

  • toolname: tool name, referred to as an executable program, used to identify a tool, such as: gcc, clangetc.
  • program: executable program command, for example:xcrun -sdk macosx clang

lib.detect.find_programIt can only be judged whether the program exists through the original program command or path passed in.
Instead, find_toolyou can find the tool through a more consistent toolname, and return the full command path of the corresponding program, for example:

import("lib.detect.find_tool")

local tool = find_tool("clang")

We can also specify {version = true}parameters to get the version of the tool, and specify a custom search path, and also support built-in variables and custom scripts:

local tool = find_tool("clang", {check = "--help"}) 
local tool = find_tool("clang", {check = function (tool) os.run("%s -h", tool) end})
local tool = find_tool("clang", {version = true, {pathes = {
   
   "/usr/bin", "/usr/local/bin", "$(env PATH)", function () return "/usr/xxx/bin" end}})

Finally, find_toolthe search process is summarized :

  1. First pass {program = "xxx"}the parameters to try to run and test.
  2. If xmake/modules/detect/toolsthe presence of detect.tools.find_xxxthe script, the script calls this a more accurate detection.
  3. Try to check from /usr/bin, /usr/local/binwait for the system catalog.

We can also project in xmake.luathe add_moduledirsspecified module directory, add a custom look for scripts to improve the detection mechanisms:

projectdir
  - xmake/modules
    - detect/tools/find_xxx.lua

More secure root permission compilation

Since xmake provides powerful support for custom modules and scripts, and has built-in actions such as installation and uninstallation, xmake.luaimproper script descriptions can easily lead to overwriting system files. Therefore, the new version has made improvements:

  1. To compile the project under root, first determine the user authority attribute of the project directory, and try to lower the authority to a non-root user for compilation.
  2. If you need to write some system files, you will be prompted that the current permissions are not safe, and it is forbidden to continue running unless you add --rootparameters to force root to run.
  3. If the current project directory has root user authority, the same as 2.

For details, see: pull#113

API interface improvements

Use includes to replace the old add_subdirs and add_subfiles interfaces.
Use set_config_header to replace the old set_config_h and set_config_h_prefix interfaces.

Add a large number of expansion modules

  • file download
  • unzip
  • git operation and other interfaces

For details, see the document: Extension Module

Guess you like

Origin blog.csdn.net/waruqi/article/details/76826610