cmake qt6: unresolved external symbol "public: virtual struct QMetaObject"

I am building qt6 project with cmake. I want to generate a library with QWidget, and the code is:

	#ifndef GLOBAL_EXPORTS
		#define GLOBAL_EXPORT __declspec(dllexport)
	#else
		#define GLOBAL_EXPORT __declspec(dllimport)
	#endif // !GLOBAL_EXPORTS

class GLOBAL_EXPORT SWidgets : public QWidget
{
	Q_OBJECT
public:
	SWidgets();
};

In compiling, the vs reports:

 unresolved external symbol "public: virtual struct QMetaObject const SWidgets::staticMetaObject"

The bug can be solved by copy moc file. But, it is inconvenient because I have many qt relative library.

How to solve this bug by cmake? Any suggestion is appreciated~~~

You can tell CMake to enable moc support on the target by either:

set(CMAKE_AUTOMOC 1) # setting this variable

add_library(mylib) # before this call

# or by setting the property
set_property(TARGET mylib PROPERTY AUTOMOC 1)

# or by using the Qt API
qt5_wrap_cpp(moc_files swidgets.cpp TARGET mylib)
target_sources(mylib PRIVATE ${moc_files})

I have tried set(CMAKE_AUTOMOC 1), but it do not work.

I have upload the code in github (GitHub - zhang-qiang-github/cmake_qt: a test).

The set(CMAKE_AUTOMOC 1) has been added in export_dll/CMakeLists.txt (line 9).

Could you please have a look at my code?

It looks OK to me. Some more debugging would be necessary to see why moc isn’t being run here.

@craig.scott ?

My environment is:

win10
vs 2022
Qt 6.3.1
cmake 3.22.1

Would this environment provide the bug unresolved external symbol?

What is supposed to be defining your GLOBAL_EXPORTS symbol? It looks like your code is expecting that to be defined when building the export_dll library, but nothing seems to be.

Rather than providing your own hand-written Global.h, consider taking a look at CMake’s GenerateExportHeader module. The docs for that module are worth a read.

The GLOBAL_EXPORTS is used to declare that SWidgets would export a dll.

After several experiments, I find:

  1. If I remove Q_OBJECT, the code works and the exported dll can be imported by others.
  2. If I just import Qt rather than export a dll, the code also works.

So, how can I export the SWidgets with Q_OBJECT as a dll.

You didn’t answer my question. What is defining/setting GLOBAL_EXPORTS?

It has been defined as:

#ifndef GLOBAL_EXPORTS_H
#define GLOBAL_EXPORTS_H


#if (WIN32)
	#ifndef GLOBAL_EXPORTS
		#define GLOBAL_EXPORT __declspec(dllexport)
	#else
		#define GLOBAL_EXPORT __declspec(dllimport)
	#endif // !GLOBAL_EXPORTS

#else
	#define GLOBAL_EXPORT 
#endif


#endif


Nothing there is defining GLOBAL_EXPORTS. That code is only checking if it is defined. I also think you might have the sense of the block that checks it reversed. Compare your hand-written file with the one that GenerateExportHeader creates.

I have tried GenerateExportHeader , and it can solve my problem. But I still feel confuzed.

If the bug is caused by GLOBAL_EXPORTS, why I can correctly generate the dll if I remove Q_OBJECT? If I remove Q_OBJECT, I can correctly generate dll, and import the generated dll in other project.

Q_OBJECT expands to a whole bunch of stuff. It defines and calls methods, and adds some static members.
If you have your defines backwards, like you had, you’re not exporting the things correctly, and the linker cannot find the definitions.
If you remove the macro, no “special souse” will be added, so you won’t see the problem (at least not right away - it will eventually appear again).

When and how it will appear?

As you extend your class, it’s almost guaranteed that at some point you’ll call some unexported method or something like that.
In any case this has already nothing to do with CMake and everything to do with C/C++, shared objects, and symbol visibility. If you want to know more, google something along those terms.