Descriptive grammar
xmake's description grammar is based on Lua implementation, so the description grammar inherits the flexibility and conciseness of lua, and separates description scope (simple description) from script scope (complex description) through 28 principles, which makes the project more concise, intuitive and readable.
Because 80% of the projects do not need very complex script control logic, only a few simple lines of configuration description are needed to meet the construction requirements. Based on this assumption, xmake separates scopes, making 80% of the xmake.lua files only need to be described as follows:
target("demo") set_kind("binary") add_files("src/*.c")
Only 20% of the projects need to be described in this way:
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)
The function () end part above belongs to the custom script domain. Normally, it does not need to be set up. Only when complex engineering description and highly customized requirements are required, they need to be customized. Extension modules provided by various xmake can be used in this scope. For more information on this, see: xmake Description Grammar and Scope Explanation.
And the above code is also a custom android project that mixes jni and java code. It can build, install and run apk program by one button directly through xmake run command.
Here are some common examples of xmake descriptions:
Building an executable program
target("demo") set_kind("binary") add_files("src/*.c")
This is the simplest classic example, in general, this situation, you do not need to write any xmake.lua file yourself, in the current code directory, directly execute the xmake command, you can complete the construction, and will automatically help you generate an xmake.lua.
For more information on automatic generation, see: xmake Intelligent Code Scanning Compilation Mode, No Handwritten make Files.
Building a configurable switchable library program
target("demo") set_kind("$(kind)") add_files("src/*.c")
By configuring, you can switch whether to compile dynamic or static libraries:
$ xmake f --kind=static; xmake $ xmake f --kind=shared; xmake
Adding debug and release compilation mode support
Perhaps the default description configuration of several lines can no longer meet your needs. You need to build debug and release programs by switching compilation mode. Then you only need:
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 build mode by configuration:
$ xmake f -m debug; xmake $ xmake f -m release; xmake
[-m | - mode] is a built-in option that can be used without defining options. The value of the pattern is defined and maintained by the user himself. You can judge the state of various patterns in is_mode("xxx").
Signing ios program by custom script
The executable program of ios, which runs on the device, needs to be signed after the construction is completed. At this time, it can be implemented by using custom scripts:
target("demo") set_kind("binary") add_files("src/*.m") after_build(function (target)) os.run("ldid -S %s", target:targetfile()) end
Here is just a fake signature with ldid program. It can only be used on jailbreak equipment. Just for example.
Built-in and external variables
xmake provides the syntax of $(varname) to support the acquisition of built-in variables, such as:
add_cxflags("-I$(buildir)")
It converts the built-in buildir variable to the actual build output directory at actual compilation time: - I./build
General built-in variables can be used to quickly obtain and stitch variable strings when passing parameters, for example:
target("test") add_files("$(projectdir)/src/*.c") add_includedirs("$(buildir)/inc")
It can also be used in modular interfaces of custom scripts, such as:
target("test") on_run(function (target) os.cp("$(scriptdir)/xxx.h", "$(buildir)/inc") end)
Of course, this variable pattern can also be extended. By default, the configuration parameters can be obtained directly through the xmake F - var = Val command, such as:
target("test") add_defines("-DTEST=$(var)")
Now that support is obtained directly from the configuration options, of course, it is very convenient to extend the custom options to obtain the custom variables. See how to customize the options: option
Modify the name of the target file
We can use built-in variables to separate the generated object files according to different architectures and platforms, such as:
target("demo") set_kind("binary") set_basename("demo_$(arch)") set_targetdir("$(buildir)/$(plat)")
Previous default settings, the target file will be generated as build demo, and through the above code settings, the target file under different configuration builds, paths and file names are also different, execution:
$ xmake f -p iphoneos -a arm64; xmake
The target file is build/iphoneos/demo_arm64.
Add subdirectory engineering module
If you have multiple target sub-modules, you can define them in an 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 sub-modules, it would be a bit bloated to place them in an xmake file, which can be placed in the sub-directory of the independent module:
target("demo") set_kind("binary") add_files("src/demo.c") add_subdirs("src/test")
With the above code, associate a subproject directory with the project goal of test.
Installation header file
target("tbox") set_kind("static") add_files("src/*.c") add_headers("../(tbox/**.h)|**/impl/**.h") set_headerdir("$(buildir)/inc")
The location and directory structure of the installed header file are: build/inc/tbox/*.h.
The bracketed part of. /(tbox/**.h) is the actual root path to be installed, and the |**/impl/**.h part 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.
Multiobjective Dependency Construction
The default build order for multiple target projects is undefined, usually in a sequential manner. If you need to adjust the build 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 example above, when compiling the target demo, you need to compile the test1, test2 targets first, because demo will use them.
Merge static libraries
xmake add_files The interface function is very powerful. It can not only support the mixed addition and construction of multi-language files, but also add static libraries directly and merge libraries automatically into the current project objectives.
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 it's not a link. add_links There is a difference.
And you can add object files directly:
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 associate it with the specified target:
target("demo") add_options("test")
In this way, even if an option is defined, if this option is enabled, the macro definition of - DTEST will be added automatically when compiling the target.
The above settings, by default, disable the test option, and then we configure to enable this option:
$ xmake f --test=y $ xmake
The option support of xmake is very powerful. In addition to the basic usage mentioned above, it can also configure various detection conditions to realize automatic detection. Details can be referred to as follows: option and Addition of dependency packages and automatic detection mechanism.
Adding third-party dependency packages
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, it will automatically append the macro definition, header file search path, link library directory in the package, and automatically link all the libraries in the package.
Users no longer need to call add_links, add_includedirs, add_ldflags and other interfaces separately to configure dependency library links.
For how to set up the package search directory, you can refer to add_packagedirs Interface, Dependency Package Details Refer to: Addition of dependency packages and automatic detection mechanism.
Generate configuration header file
If you want to write the results of the test to the configuration header file after the xmake configuration project is successful or after an option is passed automatically, you need to call this interface to enable the automatic generation of config.h files.
For example:
target("test") set_config_h("$(buildir)/config.h") set_config_h_prefix("TB_CONFIG")
If a dependency is enabled, the corresponding macro definition configurations will be automatically written into the config.h file set up after adding relevant option dependencies, package dependencies, and interface dependencies to the target through the following interfaces.
These interfaces, in fact, use some of the detection settings in the option options at the bottom, such as:
option("wchar") -- Add pairs wchar_t Type Detection add_ctypes("wchar_t") -- If the detection passes, automatic generation TB_CONFIG_TYPE_HAVE_WCHAR Macro switch to config.h add_defines_h_if_ok("$(prefix)_TYPE_HAVE_WCHAR") target("test") -- Enable automatic header file generation set_config_h("$(buildir)/config.h") set_config_h_prefix("TB_CONFIG") -- Add pairs wchar The dependency Association of options is added only. wchar The test result of the option is written to the specified config.h Go in add_options("wchar")
Detecting library header files and interfaces
We can add some library interface detection to the newly generated config.h, such as:
target("demo") -- Setting and enabling config.h set_config_h("$(buildir)/config.h") set_config_h_prefix("TEST") -- Setting module name prefix only by parameter one add_cfunc("libc", nil, nil, {"sys/select.h"}, "select") -- Through parameter three, set up simultaneous detection link library: libpthread.a add_cfunc("pthread", nil, "pthread", "pthread.h", "pthread_create") -- Setting interface aliases by parameter two add_cfunc(nil, "PTHREAD", nil, "pthread.h", "pthread_create")
The generated config.h results are as follows:
#ifndef TEST_H #define TEST_H // Macro Naming Rules: $(prefix) prefix_module name (if not nil) HAVE_interface name or alias (capitalization) #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 compilation of the code according to the support of the interface.
Custom Plug-in Tasks
The task domain is used to describe a custom task implementation, and target and option At the same level.
For example, here we define the simplest task:
task("hello") -- Setting up run scripts on_run(function () print("hello xmake!") end)
This task only needs to print hello xmake!, so how to run it?
Because it is not used here set_menu Set the menu so that the task can only be invoked within a custom script for xmake.lua or other tasks, such as:
target("test") after_build(function (target) -- Import task Modular import("core.project.task") -- Function hello task task.run("hello") end)
Here we run the hello task after we have built 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)
The task.run {} above is used to pass parameters from the plug-in menu, which is not passed here. set_menu Set the menu and pass it empty here.
xmake plug-in support is also very powerful, and provides many built-in plug-ins for use, please refer to: xmake plug-in manual and task manual
Or you can refer to some of the xmake's own Plug-in demo.
Another grammatical style
In addition to supporting the most commonly used set-add descriptive style, xmake supports another grammatical style: 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 the appropriate style description according to their preferences, but the suggestion here is:
* For simple projects, it does not need too complicated conditional compilation, and can use the key-val mode, which is more concise and readable. * For complex engineering, if more controllability and flexibility are needed, the set-add method is recommended. * Try not to mix the two styles, although it is supported, but this description of the whole project will feel very confused, so try to unify the style as their own description specification.
In addition, not only target s, such as options, tasks, templates are 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 are available for direct reading. xmake's official manual The complete api documentation and usage description are provided above.
Personal Home Page: TBOOX Open Source Project