I'm using Qt, CMake, and the VS2010 compiler. There seems to be a problem when I'm linking a small piece of test code. The linkers gives the following error:
plotter.cpp.obj : error LNK2001: unresolved external symbol "public: virtual str
uct QMetaObject const * __thiscall Plotter::metaObject(void)const " (?metaObject
#Plotter##UBEPBUQMetaObject##XZ)...
(it goes on for a while)
The error occurs when I'm trying to inherit from QObject in the following code:
class Plotter : public QObject
{
Q_OBJECT
public:
If I leave out the Q_OBJECT, the program links, but I can't use the class slots at runtime.
I noticed that no moc file is generated for plotter.h. This is my CMakeLists.txt:
cmake_minimum_required (VERSION 2.6)
project (ms)
SET(CMAKE_BUILD_TYPE "Release")
FIND_PACKAGE(Qt4)
INCLUDE(${QT_USE_FILE})
ADD_DEFINITIONS(${QT_DEFINITIONS})
LINK_LIBRARIES(
${QT_LIBRARIES}
)
set(all_SOURCES plotter.cpp main.cpp dialog.cpp)
QT4_AUTOMOC(${all_SOURCES})
add_executable(ms ${all_SOURCES})
target_link_libraries(ms ${LINK_LIBRARIES})
A moc file is generated for dialog.cpp, but not for plotter.cpp, how is this possible?
Thanks!
First of all, ensure you are using QT4_AUTOMOC correctly. As the documentation points out, you still need to properly include the mocced files in your sources.
Also notice that QT4_AUTOMOC is still marked as experimental by CMake, so be sure it actually does what you expect and correctly generates the required files. If not, consider switching to the more robust classic solution using QT4_WRAP_CPP:
# notice that you need to pass the *header* here, not the source file
QT4_WRAP_CPP(MY_MOCED_FILES plotter.hpp)
# optional: hide the moced files in their own source group
# this is only useful if using an ide that supports it
SOURCE_GROUP(moc FILES ${MY_MOCED_FILES})
# then include the moced files into the build
add_executable(ms ${all_SOURCES} ${MY_MOCED_FILES})
Apart from that, your CMake file seems fine.
Related
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/
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
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):
Remove the #include "moc_Bar.cpp" line(s) from file(s) Bar.cpp
Add the external (to the current directory) header files as 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.)
Reading others questions here I found that is possible to change the outdir macro inside de visual studio. I really searched but didn't found/understand how to do it.
It's kind simple. I just want to change the Project property -> Configuration Properties -> General -> Output Directory. Because I know that will change the outdir macro.
I understand that is throught set_target_property using some kind of cmake PROPERTY but I really didn't found how.
It's pretty straightforward as you suspected. You need to look at the ARCHIVE_OUTPUT_DIRECTORY, LIBRARY_OUTPUT_DIRECTORY, and RUNTIME_OUTPUT_DIRECTORY target properties to modify the outdir path.
These all have config-specific variants too (e.g. ARCHIVE_OUTPUT_DIRECTORY_DEBUG) and can all be initialised by the global CMake variables of the same name with a CMAKE_ prepended.
So, you can do e.g.
set_target_properties(MyExe PROPERTIES RUNTIME_OUTPUT_DIRECTORY <custom path>)
or, to affect all targets,
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY <custom path>)
NB. From the docs:
Multi-configuration generators (VS, Xcode) append a per-configuration subdirectory to the specified directory.
Here's an example showing this behaviour. It writes its own trivial C++ source files, so all you should need to do is copy this to a folder, invoke CMake then try building the resultant solution in Debug, Release, MinSizeRel and RelWithDebInfo. Tested with VS2012. The executable always ends up in <build dir>/Exes/Debug regardless of build type, and similarly the library is always in <build dir>/Libs/Debug.
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
project(Example)
file(WRITE lib.hpp "void Print();\n")
file(WRITE lib.cpp "#include<iostream>\nvoid Print() { std::cout << \"Hello World\\n\"; }\n")
file(WRITE main.cpp "#include \"lib.hpp\"\nint main() { Print(); return 0; }\n")
set(ArchiveOutputDir ${CMAKE_BINARY_DIR}/Libs/Debug)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${ArchiveOutputDir})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL ${ArchiveOutputDir})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${ArchiveOutputDir})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${ArchiveOutputDir})
set(RuntimeOutputDir ${CMAKE_BINARY_DIR}/Exes/Debug)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${RuntimeOutputDir})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${RuntimeOutputDir})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${RuntimeOutputDir})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${RuntimeOutputDir})
add_library(MyLib lib.cpp lib.hpp)
add_executable(MyExe main.cpp)
target_link_libraries(MyExe MyLib)
In the end, what worked for me was placing the full path on the target_link_libraries with debug prefix and optimized prefix to point release config and relwithdebinfo config to release path and debug to debug. I also took off the link_directories... I don't if I didn't understand but it worked for me!
Currently, I use a variable MYPROJECT_CURRENT_HEADERS in CMake to list all the headers. As I use Qt, my CMakeLists.txt contains :
QT4_WRAP_CPP(MYPROJECT_CURRENT_MOC ${MYPROJECT_CURRENT_HEADERS})
The problem is that all headers are treated by moc, even those that don't have a Q_OBJECT : so it generates many empty file.
Is there a solution to "grep"/detect if the file contains the string Q_OBJECT and if it's the case, add it to MYPROJECT_CURRENT_MOC ?
Thank you
There's a new target property in the soon-to-be-released CMake 2.8.6 called "AUTOMOC" that may help you out.
The test for this feature (which you can use as a guide or example) is found here:
http://cmake.org/gitweb?p=cmake.git;a=tree;f=Tests/QtAutomoc;h=7dae3b16a54dc0b2f63bbfa5c218c48b9bbf34a9;hb=nightly-master
The very simple CMakeLists.txt file is here:
http://cmake.org/gitweb?p=cmake.git;a=blob;f=Tests/QtAutomoc/CMakeLists.txt;h=4a5ff1099ba5249a6f22eea745a031b76e6f440f;hb=nightly-master
If you use this feature, cmake will scan the headers for Q_OBJECT and automatically run moc for you.
If you'd like to try it out before the final release of CMake 2.8.6, you can download one of the release candidates here:
http://cmake.org/files/v2.8/?C=M;O=D
The "-rc2" files do include the AUTOMOC property.
Here's the help text from running "cmake --help-property AUTOMOC":
cmake version 2.8.6-rc2
AUTOMOC
Should the target be processed with automoc (for Qt projects).
AUTOMOC is a boolean specifying whether CMake will handle the Qt moc
preprocessor automatically, i.e. without having to use the
QT4_WRAP_CPP() macro. Currently Qt4 is supported. When this property
is set to TRUE, CMake will scan the source files at build time and
invoke moc accordingly. If an #include statement like #include
"moc_foo.cpp" is found, the Q_OBJECT class declaration is expected in
the header, and moc is run on the header file. If an #include
statement like #include "foo.moc" is found, then a Q_OBJECT is
expected in the current source file and moc is run on the file itself.
Additionally, all header files are parsed for Q_OBJECT macros, and if
found, moc is also executed on those files. The resulting moc files,
which are not included as shown above in any of the source files are
included in a generated _automoc.cpp file, which is
compiled as part of the target.This property is initialized by the
value of the variable CMAKE_AUTOMOC if it is set when a target is
created.
I don't know a simple command to pick headers having a string from the list but you can always make a loop to find all such headers:
set(HEADERS_HAVING_Q_OBJECT)
foreach(header ${MYPROJECT_CURRENT_HEADERS})
file(STRINGS "${header}" lines REGEX "Q_OBJECT")
if(lines)
list(APPEND HEADERS_HAVING_Q_OBJECT "${header}")
endif()
endforeach()
But this solution has its own drawback: if you add a Q_OBJECT into one of the filtered out files you need to rerun cmake manually. Otherwise moc code for new file will not be automatically generated during the build process.
I'm having a problem that I've been trying to solve for a while, but I am completely stumped. So I have two classes, X and Y, and they each have their own header files, X.h and Y.h. Each is a Q_OBJECT and has that macro definition in the header file.
class Y: public X { Q_OBJECT ...}
The definition of Y reads. The definition of X reads:
class X: public QGLWidget {Q_OBJECT ...}
When I compile, X.cpp and Y.cpp compile correctly and there are no problems at that stage. The moc files are also generated with no problem.
However, when the standard QT build process goes to compile moc_X.cpp, it gives me "C:\path\Y.h(34) : error C2504: 'X' : base class undefined". But this doesn't happen when it's compiling Y.cpp or X.cpp, it only happens when it's compiling the moc files! Any ideas? That the build would be failing at the moc stage and only moc stage seems extremely peculiar. Help is much appreciated!
I've tried reproducing it, with the description you give, and haven't been able to.
So, here are things that would be worth checking:
errors in include guards?
Check that you haven't accidentally got 2 include-guards of the same name in 2 different headers (i.e. the #ifndef X_H and #define X_H lines)
(This is perhaps less likely, from your description: it would be more likely if the error was in the compiling of moc_Y.cpp)
forward declarations for types used in signals or slots?
Sometimes a parameter in a signal or slot can require an extra header to be included.
The best way I can explain it is to say that there are cases where your header would be fine with a forward-declaration to a class, but the moc can generate code that needs to actually create or destroy a type that your header only forward-declares.
If this is the case, there is a way to add code to the .ui, that requests inclusion of the extra header for the forward-declared type. But the easiest solution is just to replace the forward declaration with the appropriate header instead, inside X.h or Y.h.
check the contents of the moc_X.cpp file
If the above doesn't help, and if you haven't done so already, I'd suggest opening up the offending moc file and reading the code. Once you see what it's doing, it might give you some ideas.
I'm using mingw32-make to compile a qt project that uses opengl, it compiles correctly and everything, but it spits countless warning messages of the form:
c:/qt3/include/qcolor.h:67: warning: inline function `int qGray(int, int,
int)' declared as dllimport: attribute ignored
For this particular instance, the function declaration is:
Q_EXPORT inline int qGray( int r, int g, int b )// convert R,G,B to gray 0..255
{ return (r*11+g*16+b*5)/32; }
My question is, why is it spitting all these warning? how can I silence them without silencing other legitimate warnings (i.e. warnings that are related directly to my code and could be potential problems)?
More importantly, why is mingw ignoring the dll import attribute in the first place?
I think Qt ought to only define Q_EXPORT (Q_DECL_EXPORT in Qt 4) to be the dllexport/import attribute if one of the following macros is defined, so make sure your makefiles or code that includes Qt headers (which eventually will include qglobal.h) aren't defining any of them: WIN32, _WIN32, __WIN32__, WIN64, _WIN64, __WIN64__. Or you can just define Q_EXPORT to be nothing in your compile (or preprocessor) flags, then Qt should skip defining it.