A bug with auto moc and object libraries that has been fixed. Or not?

Hi everyone.

In the project that I’m working on we’re trying to introduce a trick to improve build parallelization - whenever a library or an application target is about to be created, we instead create an object library containing all the source files of that target; the main target is then created without any source files and just links to the object library. The parallelization improvement comes from the fact that those object libraries don’t need to depend on each other and the code can be compiled in parallel.

The problem arises when a library uses Qt: the automoc-generated symbols may be missing from the library.

I’ve created a test project to demonstrate the problem, but unfortunately the forum won’t allow me to upload files, so here is its main part:
lib.h:

#include <QObject>

class Foo : public QObject
{
    Q_OBJECT
public:
    void doStuff();
signals:
    void foo();
};

lib.cpp:

#include "lib.h"

void Foo::doStuff()
{
    emit foo();
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)

project(foo CXX)

set(CMAKE_AUTOMOC ON)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)

find_package(Qt5Core REQUIRED)

include_directories(${Qt5Core_INCLUDE_DIRS})

add_library(the_lib_objects OBJECT lib.cpp)

add_library(the_lib SHARED)
target_link_libraries(the_lib the_lib_objects Qt5::Core)

When the_lib is being built, only the_lib_autogen/mocs_compilation.cpp.o and lib.cpp.o are passed to the linker, but the_lib_objects_autogen/mocs_compilation.cpp.o is not. And when I try to use the library in a test application, I get linker errors about missing symbols, like “undefined reference to Foo::staticMetaObject”.

The issue is no longer reproducible with CMake 3.22

Now the interesting part:

  1. It looks very much like https://gitlab.kitware.com/cmake/cmake/-/issues/22085 but that one is supposed to exist only in 3.20, and mine is also reproducible in 3.19 and 3.21. So it must be something different.
  2. I can reproduce it only with prebuilt CMake binaries downloaded from cmake.org or github (I’ve tried only the linux-x86_64 ones); and when I build CMake from source, the issue is gone.
    E.g. I’ve just tried it with 3.21.4 - build cmake-3.21.4.tar.gz from source, it works, use the pre-built cmake-3.21.4-linux-x86_64.tar.gz, it doesn’t.

So, my questions are:

  1. How are the prebuilt binaries built exactly?

  2. Could the issue be not fixed in 3.22 but rather masked by slightly different build settings/environment?

I’ve just noticed that my CMakeLists.txt was a bit lame, because it didn’t “link” Qt5::Core into the object library. I don’t seem to be able to edit the post, so below is a less lame version. I doesn’t change anything, though.

cmake_minimum_required(VERSION 3.16)

project(foo CXX)

set(CMAKE_AUTOMOC ON)

find_package(Qt5Core REQUIRED)

add_library(the_lib_objects OBJECT lib.cpp)
target_link_libraries(the_lib_objects Qt5::Core)

add_library(the_lib SHARED)
target_link_libraries(the_lib the_lib_objects Qt5::Core)

add_executable(the_exe main.cpp)
target_link_libraries(the_exe the_lib)

But another fun fact is that if I add an unrelated library to the project, e.g.

file(WRITE "${CMAKE_BINARY_DIR}/empty.cpp" "")
add_library(foo ${CMAKE_BINARY_DIR}/empty.cpp)

it’ll magically fix the issue.

I faced the same issue with 3.26.3 on linux x64 with rpm from the repository. “Fix” with foo library didn’t help, but I noticed, that OBJECT libraries are composed to the static library - mocs_compilation files are passed to the linker instead of the case with shared library

And one more fact: the very first OBJECT library propagates its mocs_compilation to the shared library, but others are not