Reputation: 411
I have a Qt widget C++ class that loads a ui file created in Qt Creator. The header and the source file for the class live in two separate directories. I have trouble instructing cmake/automoc to find the header for the class. cmake recognizes it needs to moc the C++ file but it cannot find the analogous header.
Is there something I can do to help cmake find the files?
Everything works fine if both the cpp and the header file are in the same directory. This only comes up when the headers are elsewhere.
My directory structure is
project
src
include
Foo
Bar.h
lib
Foo
Bar.cpp
forms
Bar.ui
In src/include/Foo/Bar.h I have:
// Bar.h
#include <QtWidgets/QWidget>
namespace Ui { class Bar; }
class Bar : public QWidget {
Q_OBJECT
...
}
In src/Foo/Bar.cpp file:
#include "Foo/Bar.h"
#include "moc_Bar.cpp"
#include "ui_Bar.h"
My CMakeLists.txt in src/lib/Foo is set up as follows:
# there is a project() call at the root that defines PROJECT_SOURCE_DIR
set(PUBLIC_HEADERS_DIR ${PROJECT_SOURCE_DIR}/src/include)
# Pick up public library headers
include_directories(${PUBLIC_HEADERS_DIR})
# Pick up private headers in library dir
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# Set up Qt
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
include_directories(${Qt5Core_INCLUDE_DIRS})
include_directories(${Qt5Gui_INCLUDE_DIRS})
include_directories(${Qt5Widgets_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
# Set up Qt forms/resources
qt5_wrap_ui(UI_OUT_FILES forms/Bar.ui)
qt5_add_resources(RESOURCE_FILE resources.qrc)
# Library cpp and header files
set(CORE_CPP_FILES Bar.cpp)
set(LIB_CPP_FILES ${LIB_CPP_FILES} ${CORE_CPP_FILES} ${UI_OUT_FILES} ${RESOURCE_FILE})
set(LIB_HEADER_FILES ${PUBLIC_HEADERS_DIR}/Foo/Bar.h)
# Build library
add_library(Foo SHARED ${LIB_CPP_FILES} ${LIB_HEADER_FILES})
target_link_libraries(Foo ${Qt5Widgets_LIBRARIES})
When I run cmake, I get the following error:
AUTOGEN: error: /automoc/src/lib/Foo/Bar.cpp The file includes the moc file "moc_Bar.cpp", but could not find header "Bar{.h,.hh,.h++,.hm,.hpp,.hxx,.in,.txx}" in /automoc/src/lib/Foo/
Upvotes: 11
Views: 5769
Reputation: 2094
I find the workarounds for this can be simplified by just leaving AUTOMOC
to its own devices. Here's what I do (which works for all of our supported CMake versions, currently 3.2...3.17
):
#include "moc_Bar.cpp"
line(s) from file(s) Bar.cpp
PRIVATE
sources for the target:
set(CMAKE_AUTOMOC True)
target_sources(Foo PRIVATE
${CMAKE_SOURCE_DIR}/src/include/Foo/Bar.h
${CMAKE_SOURCE_DIR}/src/include/Foo/Baz.h)
AUTOMOC
will create a Foo_autogen
output directory when it MOCs the files in question. moc_Bar.cpp
and moc_Baz.cpp
will be created in a randomly-named ABDEADBEEF
subdirectory, and a mocs_compilation.cpp
file will be created with content like the following:
// This file is autogenerated. Changes will be overwritten.
#include "ABDEADBEEF/moc_Bar.cpp"
#include "ABDEADBEEF/moc_Baz.cpp"
That file gets compiled and linked in with the final output of the target.
With CMAKE_AUTOMOC
globally set True
, each target will also receive its own target_autogen
directory, though nothing will be generated there in targets that don't have any MOC'able classes. Still, it may be better to set the AUTOMOC
target property only on the target(s) that need it:
set_property(TARGET Foo PROPERTY AUTOMOC ON)
The AUTOMOC
process can even be told to avoid scanning certain source files (headers, actually), to avoid it doing unnecessary work at build time. To do that, set the SKIP_AUTOMOC
property directly on the relevant files:
set_property(SOURCE Bar.h PROPERTY SKIP_AUTOMOC ON)
That will prevent moc
from being run in an attempt to generate a moc_Bar.cpp
for that header, if one isn't needed. (moc
will typically recognize that it isn't needed and quickly skip over the header anyway, but perhaps projects with very large headers or a large number of non-Qt headers might see some benefits from not scanning all of them unnecessarily.)
Upvotes: 0
Reputation: 1114
You have to wrap your header files manually. Put it into your CMakeLists.txt:
file(GLOB HEADERS_TO_MOC src/include/Foo/ *.h)
qt5_wrap_cpp(PROCESSED_MOCS
${HEADERS_TO_MOC}
TARGET Foo
OPTIONS --no-notes) # Don't display a note for the headers which don't produce a moc_*.cpp
target_sources(Foo PRIVATE ${PROCESSED_MOCS}) # This adds generated moc cpps to target
Real example of this approach https://github.com/paceholder/nodeeditor/blob/master/CMakeLists.txt#L133
Upvotes: 3