Xmake

If you are bored of CMake and writing CMakeLists.txt, this is a better alternative.

Xmake is a powerful tool to manage build, execution, dependency management, and mix languages toghether. The biggest advantage for PRG1 is its watch mode and how easy it it to add GoogleTest. It has intuitive commands to execute and create a new project. Here are the basics you should understand to use it in PRG1.

Installation: See this page. On Fedora the Ansible playbook installs it via DNF.

Basics Xmake uses xmake.lua as a configuration file, that's the equivalent of CMakeLists.txt for CMake. The most noticable difference is that is written in Lua. If you don't know Lua that's fine, I don't know it either and I managed to make it work.

How to create a new C++ project ?

Just xmake create will create a new folder with the same name as the current one, if you prefer to give it a name directly, use --project=<name>

temp> xmake create --project=super-project
create super-project ...
  [+]: src/main.cpp
  [+]: xmake.lua
  [+]: .gitignore
create ok!
temp> cd super-project/
super-project> tree -a
.
├── .gitignore
├── src
│   └── main.cpp
└── xmake.lua

The file xmake.lua contains this

add_rules("mode.debug", "mode.release")

target("super-project")
    set_kind("binary")
    add_files("src/*.cpp")

It will generate a target (an executable binary) named super-project including all CPP files in the src folder.

Basic commands

  • xmake is an alias of xmake build or xmake b, this is the first command to run
  • xmake run or xmake r let you run all targets, xmake run tests will run the target named tests to avoid running all of them
  • xmake clean if you need to clean build files

Watch mode 🔥🔥
There is a xmake watch command, with a quiet mode (-q) and a way to give commands -c to run, separated by ;, this make is very easy to build + clear console + run the program like this:

xmake watch -q -c 'xmake; clear; xmake run'

Note: on Windows, clear is cls...

Recommended shell aliases

alias x "xmake"
alias xr "xmake run"
alias xbr "xmake && xmake run"
alias xw "xmake watch -q -c 'xmake; clear; xmake run'"

TODO: improve that

IDE integration
I guess there is a plugin for CLion and VSCode.

  • TODO document this later

Formatting code
You can just format your code with Clang format by calling: xmake format

How to write tests with GoogleTest ??
GoogleTest (gtest) is a library very useful to write C++ tests. We could install it globally but that's easier to let Xmake download it from xmake-repo (a Github repository containing definitions of "packages", with one package named gtest)

  • To ask Xmake to download this package: add_requires("gtest") at the top
  • To include it during build: add_packages("gtest") on the target
  • Include all files tests files and source files except the main.cpp
  • Include the default main() implementation from gtest to avoid writing it yourself

Copy paste this content in your xmake.lua

add_rules("mode.debug", "mode.release")
add_requires("gtest")

target("super-project") -- your existing target
    set_kind("binary")
    add_files("src/*.cpp")

target("tests") -- build a `tests` target with all `.cpp` files inside `tests` folder
    add_files("tests/*.cpp") -- include all tests files
    add_files("src/*.cpp|src/main.cpp") -- add source files are they are included by tests files, but ignore src/main.cpp
    add_packages("gtest") -- link the package we installed above
    add_links("gtest_main") -- use the main() function written by the gtest library
  • Note 1: if you have another structure that's fine too, adapt the add_files and remove_files calls
  • Note 2: tabs are esthetic and entirely optionnal, if your formatter removes them it will also works.

Setup a first function to test, we'll create src/super-math.h and src/super-math.cpp to write some math functions there.

super-math.h

#ifndef SUPER_MATH_H
#define SUPER_MATH_H

int double_me(int a);

#endif

super-math.cpp

#include "super-math.h"

int double_me(int a) {
    return a * 2;
}

Then we can setup the tests/tests.cpp (call it like you want, you might want several testing files later)

#include "../src/super-math.h"
#include <gtest/gtest.h>

TEST(DoubleMeSuite, DoubleMePositiveValues) {
    EXPECT_EQ(double_me(23), 46);
}

We can now build and run this tests target

> xmake && xmake run tests
[ 30%]: cache compiling.debug tests/tests.cpp
[ 40%]: cache compiling.debug src/super-math.cpp
[ 50%]: cache compiling.debug src/super-math.cpp
[ 60%]: cache compiling.debug src/main.cpp
[ 70%]: linking.debug super-project
[ 80%]: linking.debug tests
[100%]: build ok, spent 0.238s
Running main() from /builddir/build/BUILD/gtest-1.14.0-build/googletest-1.14.0/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from DoubleMeSuite
[ RUN      ] DoubleMeSuite.DoubleMePositiveValues
[       OK ] DoubleMeSuite.DoubleMePositiveValues (0 ms)
[----------] 1 test from DoubleMeSuite (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

As you can see the test is passing !

See section Writing automated tests with GoogleTest if you need an introduction.