Try building with another build system (TryBuild Mode)

xmake v2.3.1 and above directly interface with other third-party build systems. Even if other projects do not use xmake.lua for maintenance, xmake can directly call other build tools to complete the compilation.

Then the user can directly use a third-party build tool to compile, so why use xmake to call it? The main benefits are:

  1. Completely consistent behavior, simplifying the compilation process. No matter which other build system is used, you only need to execute the xmake command to compile. Users no longer need to study the different compilation processes of other tools
  2. Docking the configuration environment of xmake config, reuse the platform detection and SDK environment detection of xmake, simplify the platform configuration
  3. Docking cross-compilation environment, even for projects maintained with autotools, you can quickly cross-compile through xmake

Build systems currently supported:

Automatically detect build system and compile

For example, for a project maintained using cmake, executing xmake directly in the project root directory will automatically trigger a detection mechanism, detect CMakeLists.txt, and then prompt the user if cmake is needed to continue compiling.

$ xmake note: CMakeLists.txt found, try building it (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) -- Symbol prefix: -- Configuring done -- Generating done -- Build files have been written to:/Users/ruki/Downloads/libpng-1.6.35/build [ 7%] Built target png-fix-itxt [ 21%] Built target genfiles [ 81%] Built target png [ 83%] Built target png_static ... output to/Users/ruki/Downloads/libpng-1.6.35/build/artifacts build ok!

Seamless using xmake command

Currently supports common commands such as xmake clean, xmake --rebuild and xmake config to seamlessly interface with third-party systems.

We can directly clean the compiled output files of the cmake maintenance project

$ xmake clean $ xmake clean --all

If you bring --all to perform the cleanup, all files generated by autotools/cmake will be cleared, not only the object files.

The default xmake is docked with incremental build behavior, but we can also force a quick rebuild:

$ xmake --rebuild

Manually switch the specified build system

If there are multiple build systems under maintenance in a project, such as the libpng project, which comes with autotools/cmake/makefile and other build system maintenance, xmake defaults to using autotools by default. If you want to force switch to other build systems, you can execute:

$ xmake f --trybuild=[autotools|cmake|make|msbuild|..] $ xmake

In addition, the --trybuild= parameter is configured to manually specify the default build system, and the subsequent build process will not prompt the user for selection.

Fastly cross compile

As we all know, although many projects maintained by autotools support cross-compilation, the configuration process of cross-compilation is very complicated. There are still many differences in different toolchain processing methods, and many pits will be stepped in the middle.

Even if you run through a toolchain's cross-compilation, if you switch to another toolchain environment, it may take a long time, and if you use xmake, you usually only need two simple commands:

!> At present cmake/autotools supports cross-compilation of xmake.

Cross compile android platform

$ xmake f -p android --trybuild=autotools [--ndk=xxx] $ xmake

!> Among them, the --ndk parameter configuration is optional. If the user sets the ANDROID_NDK_HOME environment variable, or if the ndk is placed in ~/Library/Android/sdk/ndk-bundle, xmake can automatically detect it.

Is not it simple? If you think this is not much, then you can directly operate ./configure to configure cross-compilation. You can see this document for comparison: [Using NDK with other compilation systems] (https://developer.android .com/ndk/guides/other_build_systems # autoconf)

To put it bluntly, you probably have to do this, you may not be able to do it once:

$ export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG $ export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar $ export AS=$TOOLCHAIN/bin/aarch64-linux-android-as $ export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang $ export CXX=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ $ export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld $ export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib $ export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip $ ./configure --host aarch64-linux-android $ make

If it is cmake, cross-compilation is not an easy task. For the android platform, this configuration is required.

$ cmake \ -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABI=$ABI \ -DANDROID_NATIVE_API_LEVEL=$MINSDKVERSION \ $OTHER_ARGS

For the ios platform, I did not find a short-answer configuration method, but found a third-party ios toolchain configuration, which is very complicated: https://github.com/leetal/ios-cmake/blob/master/ios.toolchain.cmake

For mingw, it is another way. I have been tossing about the environment for a long time, which is very tossing.

After using xmake, whether it is cmake or autotools, cross-compilation is very simple, and the configuration method is exactly the same, streamlined and consistent.

Cross compile iphoneos platform

$ xmake f -p iphoneos --trybuild=[cmake|autotools] $ xmake

Cross-compile mingw platform

$ xmake f -p mingw --trybuild=[cmake|autotools] [--mingw=xxx] $ xmake

Using other cross-compilation toolchains

$ xmake f -p cross --trybuild=[cmake|autotools] --sdk=/xxxx $ xmake

For more cross compilation configuration details, please refer to the document: Cross Compilation, except for an additional --trybuild= parameter, all other cross-compilation configuration parameters are completely universal.

Passing user configuration parameters

We can use --tryconfigs= to pass additional configuration parameters of the user to the corresponding third-party build system. For example: autotools will be passed to . / Configure, cmake will be passed to the cmake command.

$ xmake f --trybuild=autotools --tryconfigs="-enable-shared=no" $ xmake

For example, the above command, pass --enable-shared=no to./configure to disable dynamic library compilation.

In addition, for --cflags, --includedirs and --ldflags, you don't need to pass --tryconfigs, you can pass the built-in parameters like xmake config --cflags= to pass through.

Examples of compiling other build system

General Compilation

In most cases, the compilation method after each docking system is consistent, except for the --trybuild= configuration parameter.

$ xmake f --trybuild=[autotools|cmake|meson|ninja|bazel|make|msbuild|xcodebuild] $ xmake

!> We also need to make sure that the build tool specified by --trybuild is installed and working properly.

Building Android jni programs

If jni/Android.mk exists in the current project, then xmake can directly call ndk-build to build the jni library.

$ xmake f -p android --trybuild=ndkbuild [--ndk =] $ xmake

We also provided xmake-gradle to build jni library in gradle, you can see Uses xmake to build JNI in Gradle

Automatically generate xmake.lua

we need not write any make-like file (xmake.lua, makefile.am, cmakelist.txt, etc.) and also build it directly.

It will scan all source files and generate xmake.lua automatically for building project.

And xmake will detect 'main' function in source file in order to distinguish between static libraries and executable programs.

(Currently, only projects with single-level directory are supported)

Used scenes

  1. Temporarily quickly compile and run some scattered test code
  2. Try porting and compiling other open source libraries
  3. Quickly create a new xmake project based on existing code

How to use it

Execute xmake directly in the directory with the source code (no xmake.lua), and follow the prompts:

$ xmake note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)? please input: n (y/n) y

Compile open source libraries

Although this approach has some limitations, but it is sufficient to complie and run some temporary codes for testing.

For example, we downloaded a zlib-1.2.10 source and want to compile it.

We only need to enter the zlib source directory and run the following command:

$ cd zlib-1.2.10 $ xmake note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)? please input: n (y/n) y

It's done, the output results:

target(zlib-1.2): static [+]: ./adler32.c [+]: ./compress.c [+]: ./crc32.c [+]: ./deflate.c [+]: ./gzclose.c [+]: ./gzlib.c [+]: ./gzread.c [+]: ./gzwrite.c [+]: ./infback.c [+]: ./inffast.c [+]: ./inflate.c [+]: ./inftrees.c [+]: ./trees.c [+]: ./uncompr.c [+]: ./zutil.c xmake.lua generated, scan ok!👌 checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.12 checking for the target minimal version ... 10.12 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the debugger (dd) ... xcrun -sdk macosx lldb checking for the golang compiler (go) ... go configure { ex = "xcrun -sdk macosx ar" , sh = "xcrun -sdk macosx clang++" , host = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , plat = "macosx" , xcode_dir = "/Applications/Xcode.app" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , go = "go" , target_minver = "10.12" , ccache = "ccache" , mode = "release" , clean = true , cxx = "xcrun -sdk macosx clang" , cc = "xcrun -sdk macosx clang" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.12" , sc = "xcrun -sdk macosx swiftc" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: ccache compiling.release ./adler32.c [06%]: ccache compiling.release ./compress.c [13%]: ccache compiling.release ./crc32.c [20%]: ccache compiling.release ./deflate.c [26%]: ccache compiling.release ./gzclose.c [33%]: ccache compiling.release ./gzlib.c [40%]: ccache compiling.release ./gzread.c [46%]: ccache compiling.release ./gzwrite.c [53%]: ccache compiling.release ./infback.c [60%]: ccache compiling.release ./inffast.c [66%]: ccache compiling.release ./inflate.c [73%]: ccache compiling.release ./inftrees.c [80%]: ccache compiling.release ./trees.c [86%]: ccache compiling.release ./uncompr.c [93%]: ccache compiling.release ./zutil.c [100%]: archiving.release libzlib-1.2.a build ok!👌

Xmake will scan the current directory to detect all source codes and it do not found main function.

So it should be static library project and we build it as a static library: libzlib-1.2.a

We did not write any make-like files (xmake.lua, ..) and did not use the makefile of zlib project.

It is compiled directly and a xmake.lua file was generated which we can edit this xmake.lua to build more complicated project.

The content of the generated xmake.lua:

-- define target target("zlib-1.2") -- set kind set_kind("static") -- add files add_files("./adler32.c") add_files("./compress.c") add_files("./crc32.c") add_files("./deflate.c") add_files("./gzclose.c") add_files("./gzlib.c") add_files("./gzread.c") add_files("./gzwrite.c") add_files("./infback.c") add_files("./inffast.c") add_files("./inflate.c") add_files("./inftrees.c") add_files("./trees.c") add_files("./uncompr.c") add_files("./zutil.c")

Fastly compile and run testing code

For example, I want to write a simple program in the main.c only to echo 'hello world!'.

If we use gcc to compile and run it, need run two commands:

gcc ./main.c -o demo ./demo

If we use xmake to run it, only need run:

xmake run

Or

xmake r

It's done, the output results is:

hello world!

If we have a lot of code files, need also run only one command:

xmake run

It's so easy and convenient.

Multi-language support

This feature of code detection and real-time compilation not only supports c/c++, also supports objc/swift and it will support golang in future.

For example, we downloaded a ios open source framework code 'fmdb':

. ├── FMDB.h ├── FMDatabase.h ├── FMDatabase.m ├── FMDatabaseAdditions.h ├── FMDatabaseAdditions.m ├── FMDatabasePool.h ├── FMDatabasePool.m ├── FMDatabaseQueue.h ├── FMDatabaseQueue.m ├── FMResultSet.h └── FMResultSet.m

There are no any make-like files in it's project directory.

We uses xmake to build it directly as a ios static library:

$ xmake f -p iphoneos; xmake

The output results are:

xmake.lua not found, scanning files .. target(FMDB): static [+]: ./FMDatabase.m [+]: ./FMDatabaseAdditions.m [+]: ./FMDatabasePool.m [+]: ./FMDatabaseQueue.m [+]: ./FMResultSet.m xmake.lua generated, scan ok!👌 checking for the architecture ... armv7 checking for the Xcode SDK version for iphoneos ... 10.1 checking for the target minimal version ... 10.1 checking for the c compiler (cc) ... xcrun -sdk iphoneos clang checking for the c++ compiler (cxx) ... xcrun -sdk iphoneos clang checking for the objc compiler (mm) ... xcrun -sdk iphoneos clang checking for the objc++ compiler (mxx) ... xcrun -sdk iphoneos clang++ checking for the assember (as) ... gas-preprocessor.pl xcrun -sdk iphoneos clang checking for the linker (ld) ... xcrun -sdk iphoneos clang++ checking for the static library archiver (ar) ... xcrun -sdk iphoneos ar checking for the static library extractor (ex) ... xcrun -sdk iphoneos ar checking for the shared library linker (sh) ... xcrun -sdk iphoneos clang++ checking for the swift compiler (sc) ... xcrun -sdk iphoneos swiftc configure { ex = "xcrun -sdk iphoneos ar" , ccache = "ccache" , host = "macosx" , ar = "xcrun -sdk iphoneos ar" , buildir = "build" , as = "/usr/local/share/xmake/tools/utils/gas-preprocessor.pl xcrun -sdk iphoneos clang" , arch = "armv7" , mxx = "xcrun -sdk iphoneos clang++" , cxx = "xcrun -sdk iphoneos clang" , target_minver = "10.1" , xcode_dir = "/Applications/Xcode.app" , clean = true , sh = "xcrun -sdk iphoneos clang++" , cc = "xcrun -sdk iphoneos clang" , ld = "xcrun -sdk iphoneos clang++" , mode = "release" , kind = "static" , plat = "iphoneos" , xcode_sdkver = "10.1" , sc = "xcrun -sdk iphoneos swiftc" , mm = "xcrun -sdk iphoneos clang" } configure ok! clean ok! [00%]: ccache compiling.release ./FMDatabase.m [20%]: ccache compiling.release ./FMDatabaseAdditions.m [40%]: ccache compiling.release ./FMDatabasePool.m [60%]: ccache compiling.release ./FMDatabaseQueue.m [80%]: ccache compiling.release ./FMResultSet.m [100%]: archiving.release libFMDB.a build ok!👌

Compile multiple executables at the same time

$ cd jpeg-6b $ xmake

The output results are:

xmake.lua not found, scanning files .. target(jpeg-6b): static [+]: ./cdjpeg.c [+]: ./example.c [+]: ./jcapimin.c [+]: ./jcapistd.c [+]: ./jccoefct.c [+]: ./jccolor.c [+]: ./jcdctmgr.c [+]: ./jchuff.c [+]: ./jcinit.c [+]: ./jcmainct.c [+]: ./jcmarker.c [+]: ./jcmaster.c [+]: ./jcomapi.c [+]: ./jcparam.c [+]: ./jcphuff.c [+]: ./jcprepct.c [+]: ./jcsample.c [+]: ./jctrans.c [+]: ./jdapimin.c [+]: ./jdapistd.c [+]: ./jdatadst.c [+]: ./jdatasrc.c [+]: ./jdcoefct.c [+]: ./jdcolor.c [+]: ./jddctmgr.c [+]: ./jdhuff.c [+]: ./jdinput.c [+]: ./jdmainct.c [+]: ./jdmarker.c [+]: ./jdmaster.c [+]: ./jdmerge.c [+]: ./jdphuff.c [+]: ./jdpostct.c [+]: ./jdsample.c [+]: ./jdtrans.c [+]: ./jerror.c [+]: ./jfdctflt.c [+]: ./jfdctfst.c [+]: ./jfdctint.c [+]: ./jidctflt.c [+]: ./jidctfst.c [+]: ./jidctint.c [+]: ./jidctred.c [+]: ./jmemansi.c [+]: ./jmemmgr.c [+]: ./jmemname.c [+]: ./jmemnobs.c [+]: ./jquant1.c [+]: ./jquant2.c [+]: ./jutils.c [+]: ./rdbmp.c [+]: ./rdcolmap.c [+]: ./rdgif.c [+]: ./rdppm.c [+]: ./rdrle.c [+]: ./rdswitch.c [+]: ./rdtarga.c [+]: ./transupp.c [+]: ./wrbmp.c [+]: ./wrgif.c [+]: ./wrppm.c [+]: ./wrrle.c [+]: ./wrtarga.c target(ansi2knr): binary [+]: ./ansi2knr.c target(cjpeg): binary [+]: ./cjpeg.c target(ckconfig): binary [+]: ./ckconfig.c target(djpeg): binary [+]: ./djpeg.c target(jpegtran): binary [+]: ./jpegtran.c target(rdjpgcom): binary [+]: ./rdjpgcom.c target(wrjpgcom): binary [+]: ./wrjpgcom.c xmake.lua generated, scan ok!👌 checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.12 checking for the target minimal version ... 10.12 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the debugger (dd) ... xcrun -sdk macosx lldb checking for the golang compiler (go) ... go configure { ex = "xcrun -sdk macosx ar" , sh = "xcrun -sdk macosx clang++" , host = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , plat = "macosx" , xcode_dir = "/Applications/Xcode.app" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , go = "go" , target_minver = "10.12" , ccache = "ccache" , mode = "release" , clean = true , cxx = "xcrun -sdk macosx clang" , cc = "xcrun -sdk macosx clang" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.12" , sc = "xcrun -sdk macosx swiftc" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: ccache compiling.release ./cdjpeg.c [00%]: ccache compiling.release ./example.c [00%]: ccache compiling.release ./jcapimin.c [00%]: ccache compiling.release ./jcapistd.c [00%]: ccache compiling.release ./jccoefct.c [00%]: ccache compiling.release ./jccolor.c [01%]: ccache compiling.release ./jcdctmgr.c [01%]: ccache compiling.release ./jchuff.c [01%]: ccache compiling.release ./jcinit.c [01%]: ccache compiling.release ./jcmainct.c [01%]: ccache compiling.release ./jcmarker.c [02%]: ccache compiling.release ./jcmaster.c [02%]: ccache compiling.release ./jcomapi.c [02%]: ccache compiling.release ./jcparam.c [02%]: ccache compiling.release ./jcphuff.c [02%]: ccache compiling.release ./jcprepct.c [03%]: ccache compiling.release ./jcsample.c [03%]: ccache compiling.release ./jctrans.c [03%]: ccache compiling.release ./jdapimin.c [03%]: ccache compiling.release ./jdapistd.c [03%]: ccache compiling.release ./jdatadst.c [04%]: ccache compiling.release ./jdatasrc.c [04%]: ccache compiling.release ./jdcoefct.c [04%]: ccache compiling.release ./jdcolor.c [04%]: ccache compiling.release ./jddctmgr.c [04%]: ccache compiling.release ./jdhuff.c [05%]: ccache compiling.release ./jdinput.c [05%]: ccache compiling.release ./jdmainct.c [05%]: ccache compiling.release ./jdmarker.c [05%]: ccache compiling.release ./jdmaster.c [05%]: ccache compiling.release ./jdmerge.c [06%]: ccache compiling.release ./jdphuff.c [06%]: ccache compiling.release ./jdpostct.c [06%]: ccache compiling.release ./jdsample.c [06%]: ccache compiling.release ./jdtrans.c [06%]: ccache compiling.release ./jerror.c [07%]: ccache compiling.release ./jfdctflt.c [07%]: ccache compiling.release ./jfdctfst.c [07%]: ccache compiling.release ./jfdctint.c [07%]: ccache compiling.release ./jidctflt.c [07%]: ccache compiling.release ./jidctfst.c [08%]: ccache compiling.release ./jidctint.c [08%]: ccache compiling.release ./jidctred.c [08%]: ccache compiling.release ./jmemansi.c [08%]: ccache compiling.release ./jmemmgr.c [08%]: ccache compiling.release ./jmemname.c [09%]: ccache compiling.release ./jmemnobs.c [09%]: ccache compiling.release ./jquant1.c [09%]: ccache compiling.release ./jquant2.c [09%]: ccache compiling.release ./jutils.c [09%]: ccache compiling.release ./rdbmp.c [10%]: ccache compiling.release ./rdcolmap.c [10%]: ccache compiling.release ./rdgif.c [10%]: ccache compiling.release ./rdppm.c [10%]: ccache compiling.release ./rdrle.c [10%]: ccache compiling.release ./rdswitch.c [11%]: ccache compiling.release ./rdtarga.c [11%]: ccache compiling.release ./transupp.c [11%]: ccache compiling.release ./wrbmp.c [11%]: ccache compiling.release ./wrgif.c [11%]: ccache compiling.release ./wrppm.c [12%]: ccache compiling.release ./wrrle.c [12%]: ccache compiling.release ./wrtarga.c [12%]: archiving.release libjpeg-6b.a [12%]: ccache compiling.release ./wrjpgcom.c [25%]: linking.release wrjpgcom [25%]: ccache compiling.release ./ansi2knr.c [37%]: linking.release ansi2knr [37%]: ccache compiling.release ./jpegtran.c [50%]: linking.release jpegtran [50%]: ccache compiling.release ./djpeg.c [62%]: linking.release djpeg [62%]: ccache compiling.release ./ckconfig.c [75%]: linking.release ckconfig [75%]: ccache compiling.release ./rdjpgcom.c [87%]: linking.release rdjpgcom [87%]: ccache compiling.release ./cjpeg.c [100%]: linking.release cjpeg build ok!👌

In addition to a static library, we also compiled some other executable programs.

target(ansi2knr): binary [+]: ./ansi2knr.c target(cjpeg): binary [+]: ./cjpeg.c target(ckconfig): binary [+]: ./ckconfig.c target(djpeg): binary [+]: ./djpeg.c target(jpegtran): binary [+]: ./jpegtran.c target(rdjpgcom): binary [+]: ./rdjpgcom.c target(wrjpgcom): binary [+]: ./wrjpgcom.c

Manual configuration

we need add them before compiling if the source code requires some special compiler options

For example:

$ xmake f --cxflags="" --ldflags="" --includedirs="" --linkdirs=""; xmake

Unity Build compilation acceleration

We know that C++ code compilation speed is usually very slow, because each code file needs to parse the imported header file.

With Unity Build, we accelerate the compilation of the project by combining multiple cpp files into one. The main benefit is to reduce the repetitive work of parsing and compiling the contents of the header files contained in multiple source files. The contents of the header files are usually It accounts for most of the code in the source file after preprocessing.

Unity build also reduces the overhead caused by having a large number of small source files by reducing the number of object files created and processed by the compilation chain, and allows inter-procedural analysis and optimization across files that form a unified build task (similar to optimization during effect linking ).

It can greatly improve the compilation speed of C/C++ code, usually by 30%. However, depending on the complexity of the project, the benefits it brings depend on the situation of the project.

xmake has also supported this build mode in v2.5.9. For related issues, see #1019.

How to enable it?

We provide two built-in rules to handle Unity Build for C and C++ code respectively.

add_rules("c.unity_build") add_rules("c++.unity_build")

Batch mode

By default, as long as the above rules are set, Unity Build in Batch mode will be enabled, that is, xmake will automatically organize and merge according to the project code files.

target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp")

We can additionally specify the size of each merged Batch by setting the {batchsize = 2} parameter to the rule, which means that every two C++ files are automatically merged and compiled.

The compilation effect is roughly as follows:

$ xmake -r [11%]: ccache compiling.release build/.gens/test/unity_build/unity_642A245F.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_bar.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_73161A20.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_F905F036.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_foo.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/main.c [77%]: linking.release test [100%]: build ok

Since we only enabled the Unity Build of C++, the C code is still compiled one by one normally. In addition, in the Unity Build mode, we can still speed up the parallel compilation as much as possible without conflicting each other.

If the batchsize parameter is not set, all files will be merged into one file for compilation by default.

Group Mode

If the automatic merging effect of the above Batch mode is not satisfactory, we can also use custom grouping to manually configure which files are merged together to participate in the compilation, which makes users more flexible and controllable.

target("test") set_kind("binary") add_rules("c++.unity_build", {batchsize = 0}) - disable batch mode add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"})

We use {unity_group = "foo"} to specify the name of each group and which files are included. The files in each group will be merged into one code file separately.

In addition, batchsize = 0 also forcibly disables the Batch mode, that is, if there is no unity_group grouped code files, we will still compile them separately, and will not automatically turn on automatic merging.

Batch and Group mixed mode

As long as we change the above batchsize = 0 to a value other than 0, we can let the remaining code files continue to open the Batch mode in the grouping mode to automatically merge and compile.

target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"})

Ignore the specified file

If it is in Batch mode, because it is an automatic merge operation, all files will be merged by default, but if some code files do not want to participate in the merge, then we can also ignore them through {unity_ignored = true}.

target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/test/*.c", {unity_ignored = true}) - ignore these files

Unique ID

Although the benefits of Unity Build are good, we still encounter some unexpected situations. For example, in our two code files, under the global namespace, there are global variables and functions with the same name.

Then, merge compilation will bring about compilation conflicts, and the compiler usually reports global variable redefinition errors.

In order to solve this problem, we need to make some modifications to the user code, and then cooperate with the build tool to solve it.

For example, our foo.cpp and bar.cpp both have global variable i.

foo.cpp

namespace { int i = 42; } int foo() { return i; }

bar.cpp

namespace { int i = 42; } int bar() { return i; }

Then, our merge compilation will conflict, and we can introduce a Unique ID to isolate the global anonymous space.

foo.cpp

namespace MY_UNITY_ID { int i = 42; } int foo() { return MY_UNITY_ID::i; }

bar.cpp

namespace MY_UNITY_ID { int i = 42; } int bar() { return MY_UNITY_ID::i; }

Next, we also need to ensure that after the code is merged, the definitions of MY_UNITY_ID in foo and bar are completely different, and a unique ID value can be calculated according to the file name, which does not conflict with each other, which is to achieve the following merge effect:

#define MY_UNITY_ID <hash(foo.cpp)> #include "foo.c" #undef MY_UNITY_ID #define MY_UNITY_ID <hash(bar.cpp)> #include "bar.c" #undef MY_UNITY_ID

This may seem troublesome, but the user does not need to care about these, xmake will automatically process them when merging, the user only needs to specify the name of the Unique ID, for example, the following:

target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2, uniqueid = "MY_UNITY_ID"}) add_files("src/*.c", "src/*.cpp")

Dealing with global variables, as well as global macro definitions with the same name, functions, etc., can be used in this way to avoid conflicts.

Remote Compilation

v2.6.5 provides remote compilation support, through which we can compile code on a remote server, run and debug remotely.

The server can be deployed on Linux/MacOS/Windows for cross-platform compilation, e.g. compile and run Windows programs on Linux, and compile and run macOS/Linux programs on Windows.

Compared to ssh remote login compilation, it is more stable and smoother to use, no lagging of ssh terminal input due to network instability, and fast local editing of code files.

We can even seamlessly implement remote compilation in editors and IDEs such as vs/sublime/vscode/idea without relying on the IDE's own support for remote compilation.

Start service

$ xmake service <remote_build_server>: listening 0.0.0.0:9096 ..

with verbose logs

$ xmake service -vD <remote_build_server>: listening 0.0.0.0:9096 ..

Start and stop service with daemon mode

$ xmake service --start $ xmake service --restart $ xmake service --stop

Configure service on server side

run xmake service will generate a default service.conf file in ~/.xmake/service.conf

{ logfile = "/Users/ruki/.xmake/service/logs.txt", remote_build = { server = { listen = "0.0.0.0:9096" } } }

Configure service on client side

~/.xmake/service.conf

{ logfile = "/Users/ruki/.xmake/service/logs.txt", remote_build = { client = { connect = "192.168.56.101:9096", } } }

Import the given configuration file

$ xmake service --config=/tmp/service.conf

Connect remote build service on client side

enter project directory

$ xmake create test $ cd test $ xmake service --connect <remote_build_client>: connect 192.168.56.110:9096 .. <remote_build_client>: connected! <remote_build_client>: sync files in 192.168.56.110:9096 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. <remote_build_client>: sync files ok!

Build project with remote mode

$ xmake <remote_build_client>: run xmake in 192.168.56.110:9096 .. checking for platform ... macosx checking for architecture ... x86_64 checking for Xcode directory ... /Applications/Xcode.app checking for Codesign Identity of Xcode ... Apple Development: waruqi@gmail.com (T3NA4MRVPU) checking for SDK version of Xcode for macosx (x86_64) ... 11.3 checking for Minimal target version of Xcode for macosx (x86_64) ... 11.4 [ 25%]: ccache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! <remote_build_client>: run command ok!

Run target with remote mode

$ xmake run <remote_build_client>: run xmake run in 192.168.56.110:9096 .. hello world! <remote_build_client>: run command ok!

Rebuild target with remote mode

$ xmake -rv <remote_build_client>: run xmake -rv in 192.168.56.110:9096 .. [ 25%]: ccache compiling.release src/main.cpp /usr/local/bin/ccache /usr/bin/xcrun -sdk macosx clang -c -Qunused-arguments -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp [ 50%]: linking.release test "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -stdlib=libc++ -Wl,-x -lz [100%]: build ok! <remote_build_client>: run command ok!

Configure project with remote mode

$ xmake f --xxx --yy

Sync files to remote server

$ xmake service --sync <remote_build_client>: sync files in 192.168.56.110:9096 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. <remote_build_client>: sync files ok!

Disconnect remote service for the current project

$ xmake service --disconnect <remote_build_client>: disconnect 192.168.56.110:9096 .. <remote_build_client>: disconnected!

View service logs

$ xmake service --logs

Clean all built and cache files for the current project on server

$ cd projectdir $ xmake service --clean

TODO