Use xmake to describe the project elegantly

original
2017/04/06 10:29
Reading 198

Descriptive syntax

The description syntax of xmake is based on the implementation of lua, so the description syntax inherits the flexibility and conciseness of lua, and separates the description scope (simple description) and script scope (complex description) through the 28 principle, making the project more concise and intuitive, and the readability is very good.

Because 80% of projects do not need very complex script control logic, only a few lines of configuration description can meet the construction requirements. Based on this assumption, xmake separates the scope, making 80% xmake.lua The file only needs to be described as follows:

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

Only 20% of the projects need to be described as follows:

 target("demo") set_kind("shared") set_objectdir("$(buildir)/.objs") set_targetdir("libs/armeabi") add_files("jni/*.c") on_package(function (target)  os.run("ant debug")  end) on_install(function (target)  os.run("adb install -r ./bin/Demo-debug.apk") end) on_run(function (target)  os.run("adb shell am start -n com.demo/com.demo.DemoTest") os.run("adb logcat") end)

above function () end Some belong to the custom script field, which generally does not need to be set. They need to be customized only when complex project descriptions and highly customized requirements are required. In this scope, you can use various extension modules provided by xmake. For more information about this, see: Explain xmake description syntax and scope

The above code is also an android project for custom hybrid construction of jni and java code, which can be directly xmake run Command to automatically build, install, and run apk programs with one click.

Here are some common examples of xmake descriptions:

Build an executable program

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

This is the simplest and classic example. Generally, you don't need to write any xmake.lua File, directly executed in the current code directory xmake Command, you can complete the construction, and will automatically generate a xmake.lua

For more information about automatic generation, see: Xmake intelligent code scanning compilation mode, no need to write any make file

Build a library program that can be configured and switched

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

You can switch whether to compile dynamic libraries or static libraries through configuration:

 $ xmake f --kind=static;  xmake $ xmake f --kind=shared;  xmake

Added debug and release compilation mode support

Maybe the default lines describing the configuration can no longer meet your needs. If you need to switch the compilation mode to build the debug and release versions of the program, you only need to:

 if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end target("demo") set_kind("binary") add_files("src/*.c")

You only need to switch the construction mode through configuration:

 $ xmake f -m debug;  xmake $ xmake f -m release;  xmake

[-m|--mode] It is a built-in option and does not need to be defined by yourself option , and the mode value is defined and maintained by the user himself. You can set is_mode("xxx") Judge the status of various modes.

Sign ios programs through custom scripts

The executable program of ios, running on the device, needs to be signed after the construction is completed. At this time, you can use custom scripts to implement:

 target("demo") set_kind("binary") add_files("src/*.m")  after_build(function (target)) os.run("ldid -S %s", target:targetfile()) end

This is just a fake signature made with the ldid program. It can only be used on the jailbreak device, just as an example.

Built in variable and external variable

Xmake provides $(varname) To support the acquisition of built-in variables, such as:

 add_cxflags("-I$(buildir)")

It will build the built-in buildir Convert variables to the actual build output directory: -I./ build

General built-in variables can be used to quickly obtain and splice variable strings during parameter transfer, for example:

 target("test") add_files("$(projectdir)/src/*.c") add_includedirs("$(buildir)/inc")

It can also be used in the module interface of the custom script, for example:

 target("test") on_run(function (target) os.cp("$(scriptdir)/xxx.h", "$(buildir)/inc") end)

Of course, this variable mode can also be extended by default through xmake f --var=val Commands and configured parameters can be obtained directly, for example:

 target("test") add_defines("-DTEST=$(var)")

Since it supports obtaining from configuration options directly, it is also very convenient to extend custom options to obtain custom variables. For details on how to customize options, see: option

Modify target file name

We can separate the generated target files according to different architectures and platforms through built-in variables, such as:

 target("demo") set_kind("binary") set_basename("demo_$(arch)") set_targetdir("$(buildir)/$(plat)")

By default, the target file will be generated as build\demo By setting the above code, the path and file name of the target file are different under different configurations. Execute:

 $ xmake f -p iphoneos -a arm64;  xmake

Then the target file is: build/iphoneos/demo_arm64

Add Subdirectory Project Module

If you have multiple target submodules, you can select one xmake.lua For example:

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

However, if there are many submodules, it will be a bit bulky to place them in an xmake file, and they can be placed in the subdirectory of an independent module:

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

Through the above code, associate a sub project directory and add test The project goal of.

Installation header file

 target("tbox") set_kind("static") add_files("src/*.c") add_headers("../(tbox/**.h)|**/impl/**.h") set_headerdir("$(buildir)/inc")

The installed header file location and directory structure are: build/inc/tbox/*.h

among ../(tbox/**.h) The bracketed part is the actual root path to be installed, |**/impl/**. h Section is used to exclude files that do not need to be installed.

Its wildcard matching rules and exclusion rules can be referred to add_files

Multi objective dependency building

For multiple target project targets, the default construction order is undefined, which is generally carried out in a sequential manner. If you need to adjust the construction order, you can achieve it by adding a dependency 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")

In the above example, when compiling the target demo, you need to compile the test1 and test2 targets first, because the demo will use them.

Merge static libraries

Xmake's add_files The interface function is very powerful. It can not only support the mixed addition and construction of multi language files, but also directly add static libraries to automatically merge libraries into the current project goals.

We can write as follows:

 target("demo") set_kind("static") add_files("src/*.c", "libxxx.a", "lib*.a", "xxx.lib")

When compiling static libraries directly, merge multiple existing static libraries. Note that this is not a link add_links There are differences.

You can also directly append object files:

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

Add custom configuration options

We can define a configuration option ourselves, for example, to enable test:

 option("test") set_default(false) set_showmenu(true) add_defines("-DTEST")

Then it is associated to the specified target:

 target("demo") add_options("test")

In this way, an option is defined. If this option is enabled, it will be automatically added when compiling the target -DTEST Macro definition of.

The above settings are disabled by default test Option. Next, we can enable this option through configuration:

 $ xmake f --test=y $ xmake

The option support of xmake is very powerful. In addition to the above basic usage, you can also configure various detection conditions to achieve automatic detection. For details, please refer to: option and Dependency package addition and automatic detection mechanism

Add third-party dependency package

In the target scope, add integrated third-party package dependencies, such as:

 target("test") set_kind("binary") add_packages("zlib", "polarssl", "pcre", "mysql")

In this way, when compiling the test target, if the package exists, the macro definition, header file search path, and linked library directory in the package will be automatically appended, and all libraries in the package will be automatically linked.

Users no longer need to call independently add_links add_includedirs , add_ldflags And other interfaces to configure dependency library links.

For how to set the package search directory, refer to add_packagedirs For interface and dependent package details, please refer to: Dependency package addition and automatic detection mechanism

Generate configuration header file

If you want to write the detection results to the configuration header file after the xmake configuration project is successful or an option is automatically detected, you need to call this interface to enable automatic generation config.h File.

For example:

 target("test") set_config_h("$(buildir)/config.h") set_config_h_prefix("TB_CONFIG")

When the following interfaces are used in the target to add related option dependency, package dependency and interface dependency to the target, if a dependency is enabled, the corresponding macro definition configuration will be automatically written to the set config.h File.

In fact, the underlying layer of these interfaces uses some detection settings in the option, such as:

 option("wchar") --Add detection of wchar_t type add_ctypes("wchar_t") --If the detection passes, automatically generate the macro switch of TB_CONFIG_TYPE_HAVE_WCHAR to config. h add_defines_h_if_ok("$(prefix)_TYPE_HAVE_WCHAR") target("test") --Enable automatic generation of header files set_config_h("$(buildir)/config.h") set_config_h_prefix("TB_CONFIG") --Add dependency association on wchar option. Only when this association is added, the detection result of wchar option will be written to the specified config. h add_options("wchar")

Test library header files and interfaces

We can generate config.h Add some library interface detection in, for example:

 target("demo") --Setting and enabling config. h set_config_h("$(buildir)/config.h") set_config_h_prefix("TEST") --Set module name prefix only through parameter 1 add_cfunc("libc",        nil,        nil,         {"sys/select.h"},   "select") --Set the link library to be detected simultaneously through parameter 3: libpthread. a add_cfunc("pthread",    nil,        "pthread",  "pthread.h",        "pthread_create") --Set interface alias through parameter 2 add_cfunc(nil,          "PTHREAD",  nil,        "pthread.h",        "pthread_create")

Generated config.h The results are as follows:

 #ifndef TEST_H #define TEST_H //Macro naming rule: $(prefix) prefix_module name (if not nil) _HAVE_interface name or alias (uppercase) #define TEST_LIBC_HAVE_SELECT 1 #define TEST_PTHREAD_HAVE_PTHREAD_CREATE 1 #define TEST_HAVE_PTHREAD 1 #endif

In this way, we can control the code compilation according to the support of the interface in the code.

Custom plug-in task

The task field is used to describe a user-defined task implementation target and option Peer.

For example, here we define the simplest task:

 task("hello") --Set Run Script on_run(function () print("hello xmake!") end)

This task only needs to print hello xmake! How to run it?

Because it is not used here set_menu Set menu, so this task can only be set in xmake.lua Is called internally by a custom script or other tasks, such as:

 target("test") after_build(function (target) --Import task module import("core.project.task") --Run the hello task task.run("hello") end)

Here, run the hello task after building the test target. Of course, we can also pass parameters:

 task("hello") on_run(function (arg1, arg2, arg3) print("hello xmake!", arg1, arg2, arg3) end) target("test") after_build(function (target) import("core.project.task") task.run("hello", {}, "arg1", "arg2", "arg3") end)

above task.run Of {} This is used to pass the parameters in the plug-in menu set_menu Set menu, empty here.

The plug-in support of xmake is also very powerful and provides many built-in plug-ins. For details, please refer to: Xmake Plug in Manual and Task manual

Or you can refer to some of the built-in xmake Plug in demo

Another grammatical style

Xmake supports the most commonly used set-add In addition to the description style, another grammar style is supported: key-val , for example:

 target { name = "test", defines = "DEBUG", files = {"src/*.c", "test/*.cpp"} }

This is equivalent to:

 target("test") set_kind("static") add_defines("DEBUG") add_files("src/*.c", "test/*.cpp")

Users can choose appropriate style descriptions according to their preferences, but the suggestions here are:

 *For simple projects, the key val method can be used without too complex conditional compilation, which is more concise and readable *For complex projects that require greater controllability and flexibility, it is recommended to use the set add method *Try not to mix the two styles. Although it is supported, it will make the whole project description very messy, so try to unify the style as your own description specification

In addition, not only targets, such as options, tasks, and templates, can be set in two ways, such as:

 --Set add style option("demo") set_default(true) set_showmenu(true) set_category("option") set_description("Enable or disable the demo module", "    =y|n")
 --Key val style option { name = "demo", default = true, showmenu = true, category = "option", desciption = {"Enable or disable the demo module", "    =y|n"} }

Custom tasks or plug-ins can be written as follows:

 --Set add style task("hello") on_run(function () print("hello xmake!") end) set_menu { usage = "xmake hello [options]", description = "Hello xmake! ", options = {} }
 --Key val style task { name = "hello", run = (function () print("hello xmake!") end), menu = { usage = "xmake hello [options]", description = "Hello xmake! ", options = {} } }

epilogue

More descriptions can be read directly Official manual of xmake , which provides complete api documentation and usage descriptions.


Personal homepage: TBOOX open source project

Expand to read the full text
Loading
Click to lead the topic 📣 Post and join the discussion 🔥
Reward
zero comment
zero Collection
zero fabulous
 Back to top
Top