Comparison Analysis of Build Tools: XMake and CMake

Comparison Analysis of Build Tools: XMake and CMake

Syntax Comparison

Empty Project

XMake
target("test")
    set_kind("binary")
    add_files("src/main.c")
CMake
add_executable(test "")
target_sources(test PRIVATE src/main.c)

Adding Source Files

XMake

XMake supports wildcard matching to add a batch of source files. *.c matches all files in the current directory, while **.c matches all files in recursive directories.

This method saves time as there’s no need to modify xmake.lua every time new files are added to the project.

target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_files("test/*.c", "example/**.cpp")

The add_files() function in XMake is very flexible and powerful, allowing various types of source files to be added while also excluding specified files.

For example, to recursively add all C files under src but exclude all C files under src/impl/:

add_files("src/**.c|impl/*.c")

For more details on using this interface, refer to the relevant documentation: add_files Interface Documentation

CMake

CMake does not seem to support this method and requires files to be added one by one.

add_executable(test "")
target_sources(test PRIVATE 
    src/main.c
    src/demo.c
    test/test1.c
    example/test1.cpp
    example/xxx/test2.cpp
)

Conditional Compilation

XMake
target("test")
    set_kind("binary")
    add_files("src/main.c")
    if is_plat("macosx", "linux") then
        add_defines("TEST1", "TEST2")
    end
    if is_plat("windows") and is_mode("release") then
        add_cxflags("-Ox", "-fp:fast")
    end
CMake
add_executable(test "")
if (APPLE OR LINUX)
    target_compile_definitions(test PRIVATE TEST1 TEST2)
endif()
if (WIN32)
    target_compile_options(test PRIVATE $<$<CONFIG:Release>:-Ox -fp:fast>)
endif()
target_sources(test PRIVATE
    src/main.c
)

Custom Scripts

XMake

XMake allows users to insert custom scripts at different stages of the build process (including compiling, installing, packaging, and running). For example, to print a line of output after the build is complete:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    after_build(function (target)
        print("target file: %s", target:targetfile())
    end)

Or to customize run and install logic:

target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_install(function (target)
        os.cp(target:targetfile(), "/usr/local/bin")
    end)
    on_run(function (target)
        os.run("%s --help", target:targetfile())
    end)

In custom scripts, users can write complex scripts and import various extension modules using the import interface.

target("test")
    set_kind("binary")
    add_files("src/*.c")
    before_build(function (target)
        import("net.http")
        import("devel.git")
        http.download("https://xmake.io", "/tmp/index.html")
        git.clone("[email protected]:tboox/xmake.git", {depth = 1, branch = "master", outputdir = "/tmp/xmake"})
    end)
CMake

CMake can also achieve this through add_custom_command:

add_executable(test "")
target_sources(test PRIVATE src/main.c)
add_custom_command(TARGET test POST_BUILD
    COMMENT "hello cmake!"
)

However, the methods for custom scripts at different stages are not entirely the same; add_custom_command can only be used for custom commands during the build stage. To customize the install stage, you need to:

install(SCRIPT cmake_install.cmake)

And it can only entirely replace the installation logic, with no support for custom logic before or after installation, and other stages like packaging and running do not seem to be supported either.

Build Methods

Default Compilation Platform

XMake

Generally, executing xmake for the default platform does not rely on other third-party build tools, not even make, and does not generate IDE/Makefile files. Instead, it directly calls the compiler toolchain for compilation, automatically enabling multi-tasking to speed up the build based on the number of CPU cores.

xmake
CMake

CMake typically generates corresponding IDE/Makefile and other third-party build files first, then calls third-party build tools like make or msbuild for compilation.

cmake .
cmake --build .

Compile for Specific Platforms

XMake

XMake allows quick switching between different platforms and architectures for compilation in a nearly consistent manner.

xmake f -p [iphoneos|android|linux|windows|mingw] -a [arm64|armv7|i386|x86_64]
xmake
CMake

CMake seems to have some differences in the configuration method for compiling for different platforms and architectures, which requires some time to research.

cmake -G Xcode -DIOS_ARCH="arm64" .
cmake --build .
cmake -G "Visual Studio 9 2008" -A x64
cmake --build .

For Android platform compilation, configuring the NDK seems to be quite complicated.

cmake .. -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK%\build\cmake\android.toolchain.cmake -DCMAKE_SYSTEM_NAME="Android" -DANDROID_NDK=%ANDROID_NDK% -DANDROID_TOOLCHAIN=clang -DANDROID_PLATFORM=android-24

Installation Targets

XMake
xmake install 
CMake
cmake -P cmake_install.cmake

Running Targets

XMake

In most cases, XMake does not require custom scripts and can directly load and run the compiled target program.

xmake run 
CMake

I couldn’t find a quick way to run a specified target program with CMake, but it should be possible to run it by writing a custom script.

cmake -P cmake_run.cmake

Dependency Support

Finding Dependency Libraries

XMake

XMake supports a similar interface to CMake’s find_package to directly find system libraries and integrate them. Upon finding the library, it automatically appends includedirs, links, linkdirs, and other related settings.

target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_load(function (target)
        target:add(find_packages("openssl", "zlib"))
    end)
CMake
add_executable(test main.c)

find_package(OpenSSL REQUIRED)
if (OpenSSL_FOUND)
    target_include_directories(test ${OpenSSL_INCLUDE_DIRS})
    target_link_libraries(test ${OpenSSL_LIBRARIES})
endif() 

find_package(Zlib REQUIRED)
if (Zlib_FOUND)
    target_include_directories(test ${Zlib_INCLUDE_DIRS})
    target_link_libraries(test ${Zlib_LIBRARIES})
endif() 

Using Third-Party Libraries (Conan)

XMake

XMake will automatically call the Conan tool to download and install the OpenSSL library and integrate it. Just executing the xmake command is enough to complete the compilation.

add_requires("conan::OpenSSL/1.0.2n@conan/stable", {alias = "openssl"}) 
target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_packages("openssl")
CMake
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
   message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
   file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.14/conan.cmake"
                 "${CMAKE_BINARY_DIR}/conan.cmake")
endif()

include(${CMAKE_BINARY_DIR}/conan.cmake)

conan_cmake_run(REQUIRES OpenSSL/1.0.2n@conan/stable
                BASIC_SETUP 
                BUILD missing)

add_executable(test main.c)
target_link_libraries(main ${CONAN_LIBS})

Using Built-in Package Repositories

XMake

XMake has its own package repository, which, while not extensive, will continue to improve: xmake-repo

Users can easily add the required packages, supporting multiple version choices and semantic version control.

Some commonly used packages support multi-platform integration, such as the zlib library, which can be directly downloaded and installed for platforms like Android, iPhoneOS, and MinGW.

add_requires("libuv master", "ffmpeg", "zlib 1.20.*")
add_requires("tbox >1.6.1", {optional = true, debug = true})
target("test")
    set_kind("shared")
    add_files("src/*.c")
    add_packages("libuv", "ffmpeg", "tbox", "zlib")

After executing the xmake command, it will automatically download the corresponding packages from the repository, compile, and install them, integrating them as follows:

Comparison Analysis of Build Tools: XMake and CMake

In addition to the official package repository, users can create multiple private repositories to integrate private packages, which is very helpful for maintaining dependencies in internal company projects.

Users just need to add their private repository address in xmake.lua:

add_repositories("my-repo [email protected]:myrepo/xmake-repo.git")

Or add it directly via the command line:

xmake repo --add my-repo [email protected]:myrepo/xmake-repo.git

For detailed explanations in this area, refer to the relevant documentation:

  • Remote Dependency Mode

  • add_requires Interface Documentation

Finally, here’s a dependency management architecture diagram for XMake:

Comparison Analysis of Build Tools: XMake and CMake

CMake

I haven’t seen CMake support this feature, but I don’t use CMake much, so if there are any inaccuracies, please correct me.

XMake GitHub Repository:

https://github.com/xmake-io/xmake

———— END ————

Comparison Analysis of Build Tools: XMake and CMake

● Column: Embedded Tools

● Column: Embedded Development

● Column: Keil Tutorials

● Selected Tutorials from Embedded Column

Follow our public account Reply “Join Group” to join the technical exchange group, reply “1024” for more content.

Comparison Analysis of Build Tools: XMake and CMake

Comparison Analysis of Build Tools: XMake and CMake

Click “Read Original” for more shares.

Leave a Comment