Skip to content

Latest commit

 

History

History
141 lines (102 loc) · 4.54 KB

modules-converter.md

File metadata and controls

141 lines (102 loc) · 4.54 KB

This doc is a step by step example to show how to use clang-modules-converter to help the rewriting process of a real world project.

The changes required are listed in the git log history.

Step 0: Checkout to baseline

git checkout b0bb413ec51bce99ef6a90a325df7445cc6336e2

Step 1: Build the project itself to get the compile commands

An example to build the project may be:

mkdir build
cd build
CXX=clang++ CC=clang cmake ../ -DCMAKE_BUILD_TYPE=Release -DASYNC_SIMPLE_DISABLE_AIO=ON
make -j4

Then we should be able to find compile_commands.json in build.

Step2: Rewrite some special files

In this project, there are two special headers: coro/Generator.h and experimental/coroutine.h. They provides a fallback implementation for generator and coroutine in the STL when <generator> and <coroutine> are not available.

Their preamble are too complex to be analyzed. Some I tried to rewrite them manually.

The change lives in update special files.

Step3: Add module config file

Add a config file:

modules:
  - name: async_simple
    path: async_simple/async_simple.cppm
    headers: async_simple/**/*.h
    excluded_headers:
      - "**/test/**"
mode: header-wrapper
controlling_macro: ASYNC_SIMPLE_USE_MODULES
is_std_module_available: false
srcs_to_rewrite: async_simple/uthread/internal/thread.cc
compilation_database: build/compile_commands.json
std_module_path: async_simple/std.cppm

Here we changed the implementation file thread.cc to use modules. If we still hope to keep the implementation to use headers (then the user of the codes won't be forced to use modules), we can remove the line for srcs_to_rewrite.

The change lives in Add config file.

Step4: run the tool

Then let's run the tool:

clang-modules-converter --config=modules.yml

And the changed codes are in here.

Step5: Update the build scripts

We need fill the build scripts ourselves: cmake update:

if (${ASYNC_SIMPLE_BUILD_MODULES})
  set(MODULES_SRCS "")
  list(APPEND MODULES_SRCS async_simple.cppm)
  list(APPEND MODULES_SRCS std.cppm)
  target_sources(async_simple_static
    PUBLIC
      FILE_SET CXX_MODULES FILES
        ${MODULES_SRCS}
  )
  target_sources(async_simple
    PUBLIC
      FILE_SET CXX_MODULES FILES
        ${MODULES_SRCS}
  )
endif()

Note that it is generally not good to make std.cppm a part of your project.

Step6: Adjust the generated module interface

In ThreadPool, there are some conditionally enabled codes based on platforms:

#ifdef __linux__
static void getCurrentCpus(std::vector<uint32_t> &ids) {
    cpu_set_t set;
    ids.clear();
    if (sched_getaffinity(0, sizeof(set), &set) == 0)
        for (uint32_t i = 0; i < CPU_SETSIZE; i++)
            if (CPU_ISSET(i, &set))
                ids.emplace_back(i);
}
#endif

Note that CPU_ISSET macro is used. So we need to update the module interface to contain the macro used manually:

#ifdef __linux__
#include <sched.h>
#endif

The dependency may be introduced implicitly by <thread>. So the tool failed to detect it. But even if it did, we still need to update manually since the tool is not smart enough to understand the conditionally includes.

Adjust module interface

Step7: Adjust the implementation files

In thread.cc, we are able to see the warning:

// There unhandled macro uses found in the body:
//	'AS_UNLIKELY' defined in /home/chuanqi.xcq/async_simple/async_simple/CommonMacros.h:22:9
//	'assert' defined in /usr/include/assert.h:50:10

Then let's add it directly (but use <cassert> instead of <assert.h>): https://github.com/ChuanqiXu9/async_simple/commit/dc2ab2becc359e8d7b3f31b5bda2561f45e495e7

Step8: Done

Let's verify it by building the modules:

mkdir build_modules
cd build_modules
CXX=clang++ CC=clang cmake ../ -DCMAKE_BUILD_TYPE=Release -DASYNC_SIMPLE_DISABLE_AIO=ON -GNinja -DASYNC_SIMPLE_BUILD_MODULES=ON
ninja