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:
xmake config
, reuse the platform detection and SDK
environment detection of xmake, simplify the platform configurationBuild systems currently supported:
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!
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
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.
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.
$ 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.
$ xmake f -p iphoneos --trybuild=[cmake|autotools]
$ xmake
$ xmake f -p mingw --trybuild=[cmake|autotools] [--mingw=xxx]
$ xmake
$ 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.
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.
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.
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
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)
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
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")
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.
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!👌
$ 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
we need add them before compiling if the source code requires some special compiler options
For example:
$ xmake f --cxflags="" --ldflags="" --includedirs="" --linkdirs=""; xmake
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.
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")
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.
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.
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"})
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
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.
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.
$ 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 ..
$ xmake service --start
$ xmake service --restart
$ xmake service --stop
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"
}
}
}
~/.xmake/service.conf
{
logfile = "/Users/ruki/.xmake/service/logs.txt",
remote_build = {
client = {
connect = "192.168.56.101:9096",
}
}
}
$ xmake service --config=/tmp/service.conf
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!
$ 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!
$ xmake run
<remote_build_client>: run xmake run in 192.168.56.110:9096 ..
hello world!
<remote_build_client>: run command ok!
$ 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!
$ xmake f --xxx --yy
$ 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!
$ xmake service --disconnect
<remote_build_client>: disconnect 192.168.56.110:9096 ..
<remote_build_client>: disconnected!
$ xmake service --logs
$ cd projectdir
$ xmake service --clean