I have a simple CMake project:
proj (project folder)
├── a.h
├── a.cpp
└── CMakeLists.txt
CMakeLists.txt:
cmake_minimum_required(VERSION 3.2)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_AUTOMOC ON)
project(proj)
set( proj_SOURCE
a.cpp
)
find_package(Qt5Core)
set( proj_LIBRARIES
Qt5::Core
)
add_library(proj SHARED ${proj_SOURCE})
target_link_libraries(proj ${proj_LIBRARIES})
a.h:
#pragma once
#include <QObject>
class A : public QObject
{
Q_OBJECT
public:
explicit A(QObject *parent = 0);
};
a.cpp:
#include "a.h"
A::A(QObject *parent) : QObject(parent)
{
}
and everything compiles great. Next, I tried to move the header file and the source file into different folder as so:
proj (project folder)
├── include
│ └── a.h
├── src
│ └── a.cpp
└── CMakeLists.txt
And tried different configurations of the following calls:
include_directories("include")
include_directories("src")
set( proj_SOURCE
src/a.cpp
)
Dosen't matter what I do the compilation fails with variations of
a.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl A::metaObject(void)const
" (?metaObject@A@@UEBAPEBUQMetaObject@@XZ) [C:\Users\me\AppData\Local\Temp\subclass\build\proj.vcxproj]
a.obj : error LNK2001: unresolved external symbol "public: virtual void * __cdecl A::qt_metacast(char const *)" (?qt_metacast@A
@@UEAAPEAXPEBD@Z) [C:\Users\me\AppData\Local\Temp\subclass\build\proj.vcxproj]
a.obj : error LNK2001: unresolved external symbol "public: virtual int __cdecl A::qt_metacall(enum QMetaObject::Call,int,void *
*)" (?qt_metacall@A@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z) [C:\Users\me\AppData\Local\Temp\subclass\build\proj.vcxproj]
C:\Users\me\AppData\Local\Temp\subclass\build\Debug\proj.exe : fatal error LNK1120: 3 unresolved externals [C:\Users\me\Ap
pData\Local\Temp\subclass\build\proj.vcxproj]
I don't know if I need to set something extra for CMake to work or what the problem is. This answer says that CMake does not work well on this configuration (files on different folders), but maybe there is a way?
AUTOMOC something leads to misunderstanding because of word AUTO.
how CMake automatically (via AUTOMOC option) finds headers is described in the doc:
At configuration time, a list of header files that should be scanned by AUTOMOC is computed from the target's sources.
- All header files in the target's sources are added to the scan list.
- For all C++ source files <source_base>.<source_extension> in the target's sources, CMake searches for
- a regular header with the same base name (<source_base>.<header_extention>) and
- a private header with the same base name and a _p suffix (<source_base>_p.<header_extention>)
and adds these to the scan list.
With another words, you have two different scans:
All header files in the target's sources are added to the scan list.
CMake searches for a regular header with the same base name
As your header file a.h
is outside the source directory, you have to use the manual scan. Indeed, CMake cannot figure out, that the header file is somewhere else. That's why you have to add the header files to target's source in your CMake file manually.
set( proj_HEADER
include/a.h
)
add_library(proj SHARED ${proj_SOURCE} ${proj_HEADER})
By doing that, you add the header to the list for moc. Then as AUTOMOC
is ON, all header files will be automatically "moc-compiled" at build time.