I was accustomed to using CMake and vcpkg on Windows to compile C/C++ programs. However, vcpkg has a drawback: each time it is updated, it requires downloading the source code from GitHub to compile the library files. For large libraries like Qt, the compilation alone can take an entire day, which is impractical, not to mention connection issues caused by DNS problems.

First, let’s outline the requirements. The main requirements for a C/C++ package manager are as follows:

  1. Support for commonly used libraries such as GTK, Qt, wxWidgets, CGAL, and VTK.
  2. Support for cross-platform use and IDE integration, or CMake support.
  3. Timely updates.

Apart from vcpkg, the only option that meets these criteria is xrepo. Xrepo is a package manager that comes with xmake, and xmake1 is an emerging C/C++ build tool. This article attempts to use xmake repo as a replacement for CMake and vcpkg to see if it can meet the current compilation needs for C/C++ programs.

1. Install xmake

According to the guidance in the official documentation2, here we use the PowerShell command to directly download and install:

irm https://xmake.io/psget.text | iex

When executing the above command on Windows, it will be automatically installed in the C:\Users\[username]\xmake directory.

You can also use other methods to install it, such as downloading the installation package for your platform directly from the Release page and installing it manually.

After installing xmake, xrepo can be used. To check the versions of xmake and xrepo:

xmake --version
xrepo --version

cd8eda41c5999fbe3a3714490ad3369c.png

VSCode can install the following extensions for viewing and building xmake projects.

2. Create Project

You can directly use xmake’s create command to create a project, which defaults to a C++ project.

xmake create hello_cpp

f122d0c81fc444640af563890205868d.png

You can also add the -l switch to specify the language type of the project.

xmake create -l c hello_c

Open the xmake.lua file automatically generated by the hello_c project. The lengthy comment section that follows provides instructions on how to configure, build, and install the project. You can compile the project by simply running the xmake command in the project directory.

If you have installed the extension in VSCode, you can also execute the compile command using the button below.

4d7371ad8f240c02d2c5e07b6c104cd2.png

In the extensions panel on the right side of VSCode, you can configure the toolchain, platform, and debugging information used for compilation.

3fe3de466fa2444dab9e6f66128556a3.png

3. Specified Toolchain

Xmake can automatically detect the system-provided MSVC and MSVC-Clang toolchains, and switching between them can be easily achieved through extensions. However, the installation paths of MSYS2 and MinGW cannot be automatically detected. This can be resolved by adding a custom toolchain in the xmake.lua file3.

toolchain("msys2-mingw64") -- Custom toolchain, named msys2-mingw64
    set_kind("standalone") -- Independent Toolchain
    set_sdkdir("D:/opt/msys64/mingw64") -- msys2-mingw64 toolchain SDK directory
toolchain_end() 

target("hello_c")
    set_kind("binary")
    add_files("src/*.c")
    set_toolchains("msys2-mingw64") -- Bind the current target to the custom toolchain above.
target_end()

Alternatively, you can configure via the following command line and compile using the msys2-mingw64 toolchain.

xmake f --toolchain=mingw --mingw='D:/opt/msys64/mingw64'
xmake b

If it is the msys2-clang64 toolchain, specify it using the following command.

xmake f -c --sdk='D:/opt/msys64/clang64' --toolchain=llvm 
xmake b -v

4. Enable Debugging

Configure the compilation for the debug version.

xmake f -m debug
xmake b

Enable debugging and run.

xmake run -d hello_c # hello_c is the target name output by the project

You can also specify a debugger, which needs to be configured before compilation.

xmake f --debugger=lldb -m debug
xmake b
xmake run -d hello_c

On Windows, only the debugger that comes with MSVC can be used. Attempts to use GDB or LLDB from the MSYS2 toolchain have been unsuccessful. It is unclear how Xmake determines the installation path of the debugger…

5. Add dependencies (for compiling a CGAL project)

Now, create a CGAL project.

xmake create cgal_test

You can first pre-install the CGAL library via xrepo, downloading the dependencies from the official repository.

xrepo install cgal
  • If this step is not performed, when executing the xmake build command, you will be prompted to download the CGAL dependencies; however, there is a high likelihood that the installation will fail.
  • Moreover, even if this step is executed, when you run the xmake build command, it will still prompt you to download the CGAL dependencies; it’s just less likely to fail. It’s unclear how the package manager xrepo is designed.
  • The download process is very slow…

Edit the xmake.lua file to add support for dependent libraries.

-- xmake.lua
add_rules("mode.debug", "mode.release")
add_requires("cgal") -- Request to Add xrepo Dependency
set_languages("c++17") -- Enable C++17 Support

target("cgal_test")
set_kind("binary")
add_packages("cgal") -- Add a reference to the CGAL package
add_files("src/main.cpp")
target_end()
  • Add a dependency on CGAL in the file using add_requires().
  • Introduce the CGAL package into the target using add_packages().
  • Compiling CGAL requires the use of C++17, which can be enabled by using set_languages("c++17").

Edit main.cpp, referring to the official CGAL source code4.

// main.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/Labeled_mesh_domain_3.h>
#include <CGAL/make_mesh_3.h>

// Domain
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::FT FT;
typedef K::Point_3 Point;
typedef FT(Function)(const Point &);
typedef CGAL::Labeled_mesh_domain_3<K> Mesh_domain;

#ifdef CGAL_CONCURRENT_MESH_3
typedef CGAL::Parallel_tag Concurrency_tag;
#else
typedef CGAL::Sequential_tag Concurrency_tag;
#endif

// Triangulation
typedef CGAL::Mesh_triangulation_3<Mesh_domain, CGAL::Default, Concurrency_tag>::type Tr;
typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3t3;
// Criteria
typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria;
namespace params = CGAL::parameters;
// Function
FT sphere_function(const Point &p)
{
    return CGAL::squared_distance(p, Point(CGAL::ORIGIN)) - 1;
}
int main()
{
    Mesh_domain domain =
        Mesh_domain::create_implicit_mesh_domain(sphere_function,
                                                 K::Sphere_3(CGAL::ORIGIN, K::FT(2)));
    // Mesh criteria
    Mesh_criteria criteria(params::facet_angle(30).facet_size(0.1).facet_distance(0.025).cell_radius_edge_ratio(2).cell_size(0.1));
    // Mesh generation
    C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria);
    // Output
    std::ofstream medit_file("out.mesh");
    CGAL::IO::write_MEDIT(medit_file, c3t3);
    medit_file.close();
    return 0;
}

Use the following command to compile the project. If xrepo was not previously used to install dependencies, and the required libraries are not found in the SDK directory, you will be automatically prompted to download the dependencies via xrepo.

xmake f -c --require=yes # Automatic Dependency Request
xmake b -v

Fortunately, after downloading once, there is no need to repeatedly download dependencies during subsequent compilations.

If the CGAL library is already installed on the local machine and you do not want to download it via GitHub, you can execute the following command to specify the local search path while disabling xrepo’s automatic dependency requests. However, this is likely to fail most of the time because xmake cannot find the locally installed CGAL header files.

xmake f -c --sdk='D:/opt/msys64/clang64' --toolchain=llvm --pkg_searchdirs='D:/opt/msys64/clang64' --require=no
xmake b -v

It seems that xmake’s local library search function could be further improved. Although the MSYS2 toolchain on this machine has CGAL installed, the local library search is unable to detect it and insists on downloading it from GitHub……

6. Enable IntelliSense highlighting

After configuring and compiling a project using xmake in VSCode, the relevant dependencies’ IntelliSense highlighting is not automatically enabled. In other words, IntelliSense cannot locate the header file paths for CGAL and its dependencies. You need to first generate the compile_commands.json file in the .vscode directory, and then manually edit the c_cpp_properties.json file to add the following code in order to enable IntelliSense highlighting.

// c_cpp_properties.json
{
    "configurations": [
        {
            "compileCommands": ".vscode/compile_commands.json"
        }
    ],
    "version": 4
}

As for the compile_commands.json file, it can be generated by executing the extension command XMake: Package in the VSCode search bar by pressing Ctrl Shift P.

87324d024f767e6b64798ceb981a639d.png

You can also generate the compile_commands.json file by executing the following command in the terminal.

xmake p

7. Compiled the Qt project (successful)

Execute the following command to create a Qt project, establishing the application based on the widget approach.

xmake create -t qt.widgetapp qwidget_test

Checking the automatically generated xmake.lua file, the Qt library files were not included.

-- xmake.lua
add_rules("mode.debug", "mode.release")

target("qwidget_test")
    add_rules("qt.widgetapp")
    add_headerfiles("src/*.h")
    add_files("src/*.cpp")
    add_files("src/mainwindow.ui")
    -- add files with Q_OBJECT meta (only for qt.moc)
    add_files("src/mainwindow.h")

...

At this point, the compilation will display an error message: error: Qt SDK not found!

Add a Qt6 package reference in the target.

-- xmake.lua
...
add_requires("qt6widgets") -- Download Qt6 via xrepo

target("qwidget_test")
add_packages("qt6widgets") -- Add a reference to Qt6
add_rules("qt.widgetapp")
...

Download Qt6 and its dependencies through xrepo.

xrepo install qt6widgets

After waiting for the QT6 download and installation to complete, proceed with the configuration and compilation commands.

xmake f -c -m release
xmake b -v

Run the program and view the Qt interface.

xmake run

4294f94c7103e87e7179e022ec950b5b.png

By default, Windows uses the MSVC cl compiler. Let’s try switching to clang-cl now.

xmake f -c -m release --toolchain=clang-cl
xmake b -v

There is no need to download it again.

Switch to the static version of Qt6 and modify the xmake.lua file.

-- xmake.lua
...
add_rules("qt.widgetapp_static") -- Change to a statically compiled version
...

Download the specified version of Qt6.

xrepo install -f "runtime='MT'" -k static qt6widgets

Strangely, no package files were downloaded, nor were any errors reported.

Configure and compile a static version of a Qt6 application.

xmake f -c -m release --toolchain=clang-cl --runtimes=MT 
xmake b -v

The configuration process completed without errors, but the compilation reported link errors. This is likely because xrepo does not provide a statically compiled Qt6 package. It seems that we will have to use a self-compiled static Qt6 library.

b553d40283015a8fa95d59924a7c87c6.png

According to the official source list of xrepo5, there is no indication of whether the qt6widgets package has a static version. The official sources do not provide a complete set of Qt6 modules, and the Qt6 WebEngine module is not available.

bd1268ca9ca655a810594387a122a1f3.png

8. Compiled the wxWidgets project (successful)

Create a C++ project directory.

xmake create -l c++ wx_test

Edit main.cpp.

// src/main.cpp

#include <wx/wx.h>

namespace wxWidgetsHelloWorldExample
{
    class Frame : public wxFrame
    {
    public:
        Frame() : wxFrame{nullptr, wxID_ANY, "Hello World"}
        {
            auto menuFile = new wxMenu;
            menuFile->Append(ID_HELLO, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
            menuFile->AppendSeparator();
            menuFile->Append(wxID_EXIT);

            auto menuHelp = new wxMenu;
            menuHelp->Append(wxID_ABOUT);

            auto menuBar = new wxMenuBar;
            menuBar->Append(menuFile, "&File");
            menuBar->Append(menuHelp, "&Help");
            SetMenuBar(menuBar);

            CreateStatusBar();
            SetStatusText("Welcome to wxWidgets!");

            Bind(wxEVT_MENU, &Frame::OnHello, this, ID_HELLO);
            Bind(wxEVT_MENU, &Frame::OnAbout, this, wxID_ABOUT);
            Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT);
        }

    private:
        void OnAbout(wxCommandEvent &event)
        {
            wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", wxOK | wxICON_INFORMATION);
        }

        void OnExit(wxCommandEvent &event)
        {
            Close(true);
        }

        void OnHello(wxCommandEvent &event)
        {
            wxLogMessage("Hello world from wxWidgets!");
        }

        inline static const int ID_HELLO = 1;
    };

    class Application : public wxApp
    {
        bool OnInit() override { return (new Frame)->Show(); }
    };
}

wxIMPLEMENT_APP(wxWidgetsHelloWorldExample::Application);

Edit the xmake.lua file.

-- xmake.lua
add_rules("mode.debug", "mode.release")
add_requires("wxwidgets")

target("wx_test")
set_kind("binary")
add_files("src/*.cpp")
add_packages("wxwidgets")
target_end()

Configure and compile using the clang-cl compiler; during the configuration process, you will be prompted to confirm whether to download the wxWidgets package from xrepo.

xmake f -c -m release --toolchain=clang-cl
xmake b -v

Run the program to observe the results.

xmake run

e78d7efeced700374caa2259dba8605e.png

It seems that xrepo only has the dynamic library version, and I could not find a way to switch to the static library.

9. Compiling GTK4 Project (Failed)

Xrepo provides two versions, gtk4 and gtk 4. Upon reviewing the code, gtk 4 is redirected to gtk4, so gtk4 is selected here.

6112734d49c8b753d9939454af442100.png

Establish a directory for a C project.

xmake create -l c gtk_test

Edit the main.c source file.

// src.main.c
#include <gtk/gtk.h>

static void
print_hello(GtkWidget *widget,
            gpointer data)
{
    g_print("Hello World\n");
}

static void
activate(GtkApplication *app,
         gpointer user_data)
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *box;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(box, GTK_ALIGN_CENTER);

    gtk_window_set_child(GTK_WINDOW(window), box);

    button = gtk_button_new_with_label("Hello World");

    g_signal_connect(button, "clicked", G_CALLBACK(print_hello), NULL);
    g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_window_destroy), window);

    gtk_box_append(GTK_BOX(box), button);

    gtk_window_present(GTK_WINDOW(window));
}

int main(int argc,
         char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return status;
}

Edit the xmake.lua file.

-- xmake.lua
add_rules("mode.debug", "mode.release")
add_requires("gtk4")

target("gtk_test")
set_kind("binary")
add_files("src/*.c")
add_packages("gtk4")
target_end()

Configure the compiler as MSVC’s clang-cl, automatically download the dependencies, and compile.

xmake f -c -m release --toolchain=clang-cl

f2112875adab96ad8a7789ec18a095e1.png

I would like to compile using MSVC, but my attempts have been unsuccessful.

134454c656cbdb8a9a8be2a8486d5f32.png

Try switching to MSYS2’s MinGW64 and then attempt again.

xmake f -c -m release --toolchain=mingw --mingw='D:/opt/msys64/mingw64'

bf2978806347bec0fe8b505e7f1854ca.png

Trying to use xrepo to download GTK4 on MinGW64 was still unsuccessful.

97cb6e3361b7508f5cac934fbdb3627f.png

Helplessly, I directly searched for the locally installed GTK4.

xmake f -c -m release --toolchain=mingw --mingw='D:/opt/msys64/mingw64' --pkg_searchdirs='D:/opt/msys64/mingw64' --require=no
xmake b -v

Alright, this time I can’t even find the header file…

0de221e675cf475e4ab6752c60f7608f.png

10. Summary

Through practical testing, xmake xrepo has shown quite a few issues. The quality of packages provided by the official sources is inconsistent; locally installed packages cannot be found even after adding them to the search path, and specifying GDB or LLDB as the debugger fails, among other problems.

The biggest issue with xmake is that it is still too new, and many details have not been fully refined. In the future, once it is further improved, it may be considered for managing large-scale projects.

It appears that, for the foreseeable future, primary development on Windows will still rely heavily on vcpkg and CMake.