Qt4的xxx.cpp文件内包含Q_OBJECT时CMakeLists.txt的编写

这似乎是一个很简单的问题,因为用 qmake 的时候确实很简单,但是对于一个刚刚开始接触CMake的用户的来说,太困难了,耗了整整一天的时间来查资料,不过还好,总算找到一个可以工作的方案。废话少说,看问题

问题的引入

这是一个很简单的程序,不是么?

//main.cpp
#include <QtGui/QApplication>
#include <QtGui/QWidget>

class Widget:public QWidget {
    Q_OBJECT
public:
    Widget(QWidget * parent=0):QWidget(parent){}
};
int main(int argc, char** argv) {
    QApplication app(argc, argv);
    Widget w;
    w.show();
    return app.exec();
}
#include "main.moc"

这个例子确实很简单,当我们用 qmake 是,我们只需要编写下面的 .pro 文件即可:

TEMPLATE = app
SOURCES += main.cpp

然后 qmake, make, 万事大吉。可是,

CMakeLists.txt 怎么写

先写个框出来,

PROJECT(mytest)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui REQUIRED)
INCLUDE(${QT_USE_FILE})
SET(pro_SOURCES main.cpp)

#QT4_WRAP_CPP(Generated_MOC_SRCS main.cpp)
#SET_SOURCE_FILES_PROPERTIES(${Generated_MOC_SRCS} PROPERTIES HEADER_FILE_ONLY true)
#QT4_AUTOMOC(${pro_SOURCES})
#QT4_GENERATE_MOC(main.cpp ${CMAKE_CURRENT_BINARY_DIR}/main.moc)
#SET_SOURCE_FILES_PROPERTIES(main.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/main.moc)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})

ADD_DEFINITIONS(${QT_DEFINITIONS})
ADD_EXECUTABLE(mytest
    ${pro_SOURCES}
    #${CMAKE_CURRENT_BINARY_DIR}/main.moc
    #${Generated_MOC_SRCS}
)
TARGET_LINK_LIBRARIES(mytest ${QT_LIBRARIES})

QT4_WRAP_CPP

我们尝试一下,用 QT4_WRAP_CPP 能解决问题吗?

首先,我们启用

QT4_WRAP_CPP(Generated_MOC_SRCS main.cpp)
  • 显然,仅仅这样是不行的,因为moc后生成的文件是 moc_main.cxx,我们能接受这个名字的话,也好办,

只需要将程序内的 #include "main.moc"改为#include "moc_main.cxx"即可

  • 但这么做还不行,因为没有依赖关系,QT4_WRAP_CPP 根本就不会运行

好吧,我们将其生成的文件加入 ADD_EXECUTABLE 中(即去掉#${Generated_MOC_SRCS} 前面的注释)。

  • QT4_WRAP_CPP 可以运行了,但它还是不能工作。

为什么? 因为 moc_main.cxx 被加入到了依赖中:一方面他被包含进了我们的 main.cpp 文件,另一方面,cmake还会尝试去单独编译它。

或许你说不就是重复了嘛,把 main.cpp 中的 #include 去掉不就行了么。但不幸,这么做不工作,moc_main.cxx 无法编译通过

无法通过的原因:moc对 xxx.h 和 xxx.cpp 处理时采取的方式不同: 对头文件,生成的文件内中将包含自动包含xxx.h;后者则不会自动包含xxx.cpp!!于是这样的文件的无法单独编译的。

  • 只要我们不让 moc_main.cxx 不被单独编译就好了。我们只需要设置其属性为 HEADER_FILE_ONLY
SET_SOURCE_FILES_PROPERTIES(${Generated_MOC_SRCS} PROPERTIES HEADER_FILE_ONLY true)
  • 可以正常工作了,但很不喜欢这个,首先我希望moc的结果是 main.moc
    • 这样程序不做修改就可用 qmake 和 cmake 构建
    • 如果我们定义一个类 xxx.h, xxx.cpp 二者都包含 Q_OBJECT(比如当xxx.cpp中包含一个私有类时),两个文件moc后的文件重名,这是不可接受的。

QT4_AUTOMOC

QT4_WRAP_CPP 不理想,用 QT4_WRAP_CPP 能解决问题吗? 我们来看看

说明:cmake的手册中提到,该宏还处于试验阶段,不如QT4_WRAP_CPP成熟,我们不考虑这个,先看起能不能用

首先,启用automoc

QT4_AUTOMOC(${pro_SOURCES})
  • 有一点是好的,用这条语句,最终生成的moc文件时我们比较喜欢的 "main.moc"。但我们不高兴的的,这个文件不对

其工作方式是这样的,首先扫描pro_SOURCES文件,如果发现其中#include "xxx.moc",它将尝试生成 xxx.moc。

怎么生成xxx.moc呢?它将查找 xxx.hpp 和 xxx.h 文件,找到后将moc该头文件来生成 xxx.moc

发现问题了吧,它处理的是头文件,而不是我们的main.cpp文件。

  • 对我们这个例子来说,它将直接报错:main.h 文件无法找到。
  • 有没有解决方法呢?有这么一个方案,可以解决我们这个程序的问题: 当 xxx.h 文件不存在时,继续查找 xxx.cpp 就行了。恩,修改cmake的模块findqt4.cmake就行了。
IF(EXISTS ${_abs_PATH}/${_basename}.hpp)
   SET(_header ${_abs_PATH}/${_basename}.hpp)
ELSE(EXISTS ${_abs_PATH}/${_basename}.hpp)
  if(EXISTS  ${_abs_PATH}/${_basename}.h) # we add this line
    SET(_header ${_abs_PATH}/${_basename}.h)
  else(EXISTS  ${_abs_PATH}/${_basename}.h) #we add this line
    SET(_header ${_abs_PATH}/${_basename}.cpp) #we add this line
  endif(EXISTS  ${_abs_PATH}/${_basename}.h) #we add this line
ENDIF(EXISTS ${_abs_PATH}/${_basename}.hpp)
  • 这么一来,我们的程序可以正常工作的。但仍然不是我们所要的。它仍有我们前面提到的问题:如果 xxx.h 和 xxx.cpp 都存在且都包含 Q_OBJECT 怎么办?仍然无解。

QT4_GENERATE_MOC

QT4_AUTOMOC 不解决问题,接续探索答案,于是注意到 QT4_GENERATE_MOC

从 cmake的模块findqt4.cmake 中看到这个宏的时候,我眼睛一亮,认定答案就是它了:

QT4_GENERATE_MOC(main.cpp ${CMAKE_CURRENT_BINARY_DIR}/main.moc)

可是我怎么让他执行呢?肯定要添加依赖关系了,翻啊翻cmake手册,终于发现一个靠谱的

SET_SOURCE_FILES_PROPERTIES(main.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/main.moc)

太高兴了,这个可以工作了。

经高人指点,可以不用上面的 SET_SOURCE_FILES_PROPERTIES 语句,将文件加入 ADD_EXECUTABLE 即可。

${CMAKE_CURRENT_BINARY_DIR}/main.moc

现在它终于比较好的工作了.

完整的CMakeList.txt

PROJECT(mytest)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui REQUIRED)
INCLUDE(${QT_USE_FILE})
SET(pro_SOURCES main.cpp)

QT4_GENERATE_MOC(main.cpp ${CMAKE_CURRENT_BINARY_DIR}/main.moc)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})

ADD_DEFINITIONS(${QT_DEFINITIONS})
ADD_EXECUTABLE(mytest
    ${pro_SOURCES}
    ${CMAKE_CURRENT_BINARY_DIR}/main.moc
)
TARGET_LINK_LIBRARIES(mytest ${QT_LIBRARIES})

小记

  • 开始学 cmake 不到一周,用其尝试编译一个这个简单的 Qt4 程序,然后遇到一些系列问题。这是目前最关心的一个,所以单独列了出来。希望对其他仍有所帮助。
  • 这个问题我在 qt_insterest 和 cmake 的2010.07.31日邮件列表中都提到了,只可惜英文能力太差。只好用中文整理出来,而无法用英文在邮件列表中做个总结。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值