TBOOX

xmake vs cmake

2019-05-29

First of all, I have to admit that cmake is very powerful and has developed for so many years. The whole ecology is quite perfect and the functions are quite rich. This is not the case with xmake.

The purpose of doing xmake at the beginning was not to completely replace cmake. It didn’t make any sense. I just thought that the syntax and ease of use of cmake couldn’t satisfy me. I still prefer a simpler and more intuitive way to describe and maintain the project. Provides a near-consistent experience under the platform.

Therefore, xmake’s syntax description and experience is still very good, which is one of the biggest highlights of xmake. I have made a lot of improvements in this design, and it is easier to get started quickly in order to lower the threshold of learning and project maintenance.

Here, I only use some of the more dominant features of xmake to compare with cmake, just to highlight the advantages and ease of use of xmake in some aspects, and there is no meaning to devalue cmake.

If you have read the comparative analysis of this article, I feel that xmake is really easy to use, can meet the needs of some project maintenance, solve some pain points, and improve the efficiency of project maintenance.

Feature Support

I will first list some of the main basic features of the build tools. Most of the features are supported, and the advantages of xmake are mainly: syntax, package repository management, build experience.

feature xmake cmake
Grammar Lua syntax, simple and intuitive, fast to get started DSL, complex, high learning cost
Self-built package warehouse management Multi-warehouse support, self-built private package warehouse Not supported
Third Party Package Management Integration vcpkg/conan/brew vcpkg/conan/Other
Build Behavior Direct Build, No Dependencies Generate Project Files, Call Third Party Build Tools
Dependencies Depends only on the build toolchain Dependent build toolchain + third-party build tools
Find Dependencies Support Support
Compiler Feature Detection Support Support
Project File Generation Support Support
Cross-platform Support Support
IDE/Editor Plugin Support Support
Modules and Plugin Extensions Support Support

Syntax

Empty Project

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

Add Source Files

xmake

xmake supports wildcard matching, adding a batch of source files, *.c matches all files in the current directory, **.c matches all files in the recursive directory.

In this way, for some new files compiled in the normal project, you don’t need to modify xmake.lua every time, and automatically synchronize, which can save a lot of time.

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

Xmake’s add_files() is very flexible and powerful. It not only supports the addition of various types of source files, but also excludes certain specified files while adding them.

For example: recursively add all c files under src, but not all c files under src/impl/.

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

For more instructions on using this interface, see the related documentation: add_files

cmake
add_executable(test "")
file(GLOB SRC_FILES "src/*.c")
file(GLOB TEST_FILES "test/*.c")
file(GLOB_RECURSE EXAMPLE_FILES "example/*.cpp")
target_sources(test PRIVATE 
    ${SRC_FILES}
    ${TEST_FILES}
    ${EXAMPLE_FILES}
)

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 script

xmake

Xmake can be used to insert a custom script to handle its own logic at different stages of compilation and build (including compiling, installing, packaging, running), such as printing a line of output after compilation is complete:

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

Or customize the 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 the custom script, users can write a variety of complex scripts, through the import interface, you can import a variety of extension modules to use.

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("git@github.com:tboox/xmake.git", {depth = 1, branch = "master", outputdir = "/tmp/xmake"})
    end)
cmake

cmake can also be implemented with add_custom_command:

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

However, looked at the different stages, the way to customize the script is not exactly the same, add_custom_command can only be used for the customization of the build phase, if you want to customize the installation phase, you have:

install(SCRIPT cmake_install.cmake)

And only the entire installation logic can be replaced, some custom logic can not be implemented before and after the installation, and other customizations such as packaging, running, etc. do not seem to support.

Build behavior

Compile the default platform

xmake

Normally, the default platform is compiled to execute xmake. During the build, xmake does not depend on other third-party build tools. Even make does not depend on it, nor does it generate IDE/Makefile files. Instead, the compiled toolchain is directly compiled for compilation. By default, the multitask acceleration build is automatically started according to the cpu core number.

xmake
cmake

The cmake is usually a third-party build file such as IDE/Makefile, and then a third-party build tool such as make/msbuild is called to compile.

cmake .
cmake --build .

Compile the given platform

xmake

Xmake can quickly switch between different platforms and architectures in a nearly consistent way.

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

Cmake seems to be different for the compilation and configuration of different platforms and architectures. The difference is still somewhat, and it takes a little time to study.

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

Like the android platform to compile, the way to configure ndk seems to be very cumbersome.

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

Install target

xmake
xmake install 
cmake
cmake -P cmake_install.cmake

Run target

xmake

In most cases, xmake can load the target program running the compiled build without writing a custom script.

xmake run 
cmake

I haven’t found a way to quickly run the specified target program, but I should be able to load it by writing a custom script.

cmake -P cmake_run.cmake

Dependency support

Find dependent libraries

xmake

Xmake also supports the interface similar to cmake’s find_package to directly find the system library, and then integrate it. After finding the library, it will automatically add includers, 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 a third-party library (Conan)

xmake

Xmake will automatically call the conan tool to download and install the openssl library, and then integrate it, just execute the xmake command 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 the built-in package repository

xmake

Xmake has a self-built package repository. Although there aren’t a lot of bread here, it will be improved in the future: xmake-repo

We only need to add the relevant required packages, it is very convenient, and support multi-version selection and semantic version control.

Even some common packages support multi-platform integration, such as: zlib library, etc. Even if you compile android/iphoneos/mingw and other platforms, you can download and install them directly.

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 package from the repository and compile and install. The integrated link comes in, the effect is as follows:

In addition to the official package repository, users can also create multiple private repositories themselves to integrate some private packages, which is very helpful for the maintenance of the company’s internal projects.

We only need to add our own private repository address to xmake.lua:

add_repositories("my-repo git@github.com:myrepo/xmake-repo.git")

Or add it directly from the command line:

xmake repo --add my-repo git@github.com:myrepo/xmake-repo.git

A detailed description of this block can be seen in the relevant documentation:

Finally, with a xmake dependency package management architecture diagram:

cmake

I didn’t see cmake support, but cmake I didn’t use much. If there is something wrong, everyone can correct me.


中文

Similar Posts

Comments