QBS:当通过另一个文件中的宏声明类时,qbs 无法运行 moc

Posted

技术标签:

【中文标题】QBS:当通过另一个文件中的宏声明类时,qbs 无法运行 moc【英文标题】:QBS: qbs fails to run moc when class is declared through a macro in another file 【发布时间】:2018-11-07 08:15:44 【问题描述】:

在我的项目中,我需要使用宏创建 QObject 派生类(带有 Q_OBJECT 和信号)。该宏位于一个单独的文件中。这是一个简化的例子:

宏在文件 CreateQtClass.h 中声明:

#ifndef __CREATEQTCLASS_H__
#define __CREATEQTCLASS_H__

#define CREATE_QT_CLASS( ClassName ) \
class ClassName : public QObject \
 \
  Q_OBJECT \
\
 signals: \
  Q_SIGNAL void mySignal( void ); \
; 

#endif //__CREATEQTCLASS_H__

我使用宏在文件 MyQtClass.h 中创建我的类

#ifndef __MYQTCLASS_H__
#define __MYQTCLASS_H__

#include <QObject>
#include "CreateQtClass.h"

CREATE_QT_CLASS( MyQtClass );

#endif //__MYQTCLASS_H__

在我的 .qbs 文件中,我将 MyQtClass.h 添加到 files 属性中,如下所示:

import qbs

QtApplication 
    name: "HelloWorld-Qt"
    files: [ "main.cpp", "MyQtClass.h" ]

现在,当运行 qbs build 时,qbs 不会在 MyQtClass.h 上运行“moc”。看起来它没有正确进行扫描,也没有检测到宏内部的 Q_OBJECT。

(我可能会注意到,如果宏声明和使用在同一个文件中,一切正常)。

我的问题:

有没有办法让用户手动强制 qbs 在文件上运行“moc”?

也许我们需要类似“force_moc”文件标签(与“unmocable”相反)之类的东西,我可以将其应用于包含 MyQtClass.h 的组。

补充:

我添加了一个简单的 Makefile 和 main.cpp 来证明 moc 可以很好地与上述方法配合使用:

文件main.cpp:

#include <QDebug>
#include "MyQtClass.h"

static void mySlot( void )

  qDebug() << "Hello slot";


int main( void )

  MyQtClass c;
  QObject::connect( &c, &MyQtClass::mySignal, &mySlot );
  emit c.mySignal();
  return 0;

Makefile:

CXX = /usr/bin/g++
MOC = /home/user/programs/Qt5.11.2/5.11.2/gcc_64/bin/moc

INCLUDES = \
    -I/home/user/programs/Qt5.11.2/5.11.2/gcc_64/include \
    -I/home/user/programs/Qt5.11.2/5.11.2/gcc_64/include/QtCore \
    -I/home/user/programs/Qt5.11.2/5.11.2/gcc_64/mkspecs/linux-g++ \
    -I/usr/include

LINK_FLAGS = \
    -Wl,-m,elf_x86_64,-rpath,/home/user/programs/Qt5.11.2/5.11.2/gcc_64/lib \
    -L/home/user/programs/Qt5.11.2/5.11.2/gcc_64/lib \
    -m64 /home/user/programs/Qt5.11.2/5.11.2/gcc_64/lib/libQt5Core.so.5.11.2 \
    -lpthread

C_FLAGS = \
    -g \
    -O0 \
    -Wall \
    -Wextra \
    -m64 \
    -pipe \
    -fexceptions \
    -fvisibility=default \
    -fPIC \
    -DQT_CORE_LIB \
    $(INCLUDES) \
    -std=c++11

SOURCES = main.cpp
OBJS = $(SOURCES:%.cpp=%.cpp.o)

HEADERS_THAT_NEED_MOC = MyQtClass.h
MOC_OBJS = $(HEADERS_THAT_NEED_MOC:%.h=moc_%.cpp.o)

all: HelloWorld-Qt

HelloWorld-Qt: $(OBJS) $(MOC_OBJS)
    $(CXX) $^ $(LINK_FLAGS) -o $@

%.cpp.o : %.cpp
    $(CXX) $(C_FLAGS) -c $^ -o $@

moc_%.cpp: %.h
    $(MOC) -DQT_CORE_LIB $(INCLUDES) $^ -o $@

clean:
    rm -f *.cpp.o HelloWorld-Qt moc_*.cpp

【问题讨论】:

【参考方案1】:

我认为您的方法行不通,独立于您使用的构建工具。请记住, moc 会查找 Q_OBJECT 宏。在 MyQtClass.h 中永远找不到这样的宏,因为 moc 和支持它的构建工具都不会扩展 CREATE_QT_CLASS 宏,因为宏扩展也会扩展 Q_OBJECT。 请注意,如果您将 CreateQtClass.h 添加到您的 qbs 文件中,您会注意到 qbs 确实运行 moc -- 但在 CreateQtClass 文件上。这是正确的行为,因为这是 Q_OBJECT 宏发生的地方。 我仔细检查了 qmake 和 cmake,它们的行为方式都相同:如果您没有在项目文件中列出 CreateQtClass,它们将不会运行 moc。如果您确实列出了它,则 moc 会在该文件上运行。 如果你想继续使用你的宏,你必须确保在调用站点引用 Q_OBJECT,例如作为宏参数。但即便如此,我也不确定 moc 本身是否会喜欢这种结构。

【讨论】:

谢谢@Christian。我的方法已经被证明可以与我以前基于普通 Make 文件(不是 qmake 或 cmake)的构建系统一起使用。如果我在宏内添加带有 Q_SIGNAL 的信号声明,moc 确实会正确处理 MyQtClass.h(我在上面的示例中忘记了它,并更正了它,但它仍然不能让 qbs 运行 moc)。所以我的观点是,根据 moc 可以成功处理这种宏在另一个文件中的经验,我也希望 Qbs(或其他构建系统)能够支持它。 您可能需要添加与您显示的源相对应的 Makefile,以便我们可以看到您所做的究竟是什么。 (对于@Christian):我写了一个简单的makefile并添加到上面。在这里,我手动指定要“moc”的文件,而不是让任何外部解析器/分析器决定要 moc 的文件。上面的示例 Makefile 只是显示了 moc 句柄的 MyQtClass.h 成功并生成了一个好的 cpp 文件。似乎 Qbs 解析文件的方式与 moc 不同,所以有时它最终会遗漏需要“moc”的文件。 出于这个原因,我认为也许 Qbs 应该为手动指定要由 moc 处理的文件提供“回退”,尽管这可能与用户仅使用 Depends name: "Qt.core" 的 Qbs 设计理念相矛盾,并且其他一切都是在后台自动完成的。 我想这可以通过引入一个新的文件标签来实现,比如“force_moc”。你可以在 bugreports.qt.io 提交一个任务。

以上是关于QBS:当通过另一个文件中的宏声明类时,qbs 无法运行 moc的主要内容,如果未能解决你的问题,请参考以下文章

MacOS 上的 Qbs 静态库

未找到库的 Qt qbs 项目设置编译错误

在qbs项目中添加子模块(使用cmake构建)

Qt 插件:从 qmake 到 qbs。联动错误

Tiled的qbs方式编译记录

Tiled的qbs方式编译记录