【问题标题】:Converting Qt project with a .pro file to project using CMake - Problems with moc使用 CMake 将带有 .pro 文件的 Qt 项目转换为项目 - moc 的问题
【发布时间】:2017-09-07 13:01:03
【问题描述】:

我有一个项目,当我使用 QtCreator 使用 .pro 文件构建它时,它编译得很好。我正在尝试将其转换为 cmake,但遇到了问题。

使用最少的可复制示例进行编辑:

项目文件夹结构:

src/foo.cpp   --> #include "ui_foo.h"
src/bar.cpp   --> #include "bar.moc"
inc/foo.h   
ui/foo.ui    

预期编译流程(使用qmake时观察到的流程):

(1)  moc  inc/foo.h              -o moc_foo.cpp
(2)  moc  src/bar.cpp            -o bar.moc
(3)  uic  ui/foo.ui              -o ui_foo.h
(4)  gcc  src/foo.cpp            -o foo.o
(5)  gcc  src/bar.cpp            -o bar.o
(6)  gcc  moc_foo.cpp            -o moc_foo.o
(7)  ld   foo.o bar.o moc_foo.o  -o foobar

CMakeLists.txt(1)

set(CMAKE_INCLUDE_CURRENT_DIR ON )
set(CMAKE_AUTOMOC ON )
set(CMAKE_AUTOUIC ON )
add_executable(foobar src/foo.cpp src/bar.cpp)

moc_foo.cpp 没有被创建(第 1 步和第 6 步被遗漏了)并且没有在第 7 步中被添加到add_executable。由于缺少对象而出现了对 vtable 的未定义引用。我认为这是因为 foo.cpp 和 foo.h 位于不同的文件夹中。

CMakeLists.txt(2)

set(CMAKE_INCLUDE_CURRENT_DIR ON )
qt5_wrap_cpp(foobar_moc inc/foo.h src/bar.cpp)
qt5_wrap_ui (foobar_ui  ui/foo.ui)
add_executable(foobar src/foo.cpp src/bar.cpp ${foobar_moc} ${foobar_ui})

moc_bar.cpp 在步骤 2 中生成,而不是 bar.moc。我收到编译器错误,因为在步骤 5 中找不到 bar.moc

【问题讨论】:

    标签: qt cmake moc


    【解决方案1】:

    尝试 moc .cpp 文件会使自己的生活变得不必要。

    尽管名称 qt5_wrap_cpp 实际上将 头文件 作为其参数并调用 moc 以生成匹配的 cpp 文件:

    qt5_wrap_cpp(foobar_moc inc/foo.h)
    
    # ${foobar_moc} will contain the name 
    # of the .cpp file created for foo.h
    # which we pass to add_executable()
    

    请注意,通常您还会在某个地方有一个foo.cpp 源文件,它与moc 生成的.cpp 一起为foo.h 中定义的类提供了完整的实现。

    现在,如果您真的想在 .cpp 文件上调用 moc,则过程略有不同。在这里,由于类定义不再在头文件中,moc 生成的源文件无法访问它所引用的类定义。结果,生成的源文件不能再自行编译。相反,您需要在使用#include 的类定义之后将其拉入原始源文件,这不是很好。

    更糟糕的是,对于 CMake 调用 moc 命令,生成的源文件仍然需要在某个地方成为依赖链的一部分,这使得一切都变得非常丑陋。

    我们将使用qt5_generate_moc 命令调用bar.cpp 上的moc(因为qt5_wrap_cpp 也会将生成的源添加到构建中,这会中断):

    qt5_generate_moc(src/bar.cpp ${CMAKE_BINARY_DIR}/bar.moc)
    

    请注意,我们将生成的文件放在二叉树中,以免生成的文件污染源树。如果您设置了AUTOGEN_BUILD_DIR,您可能希望将生成的文件放在那里。为了让#include能够找到生成的文件,我们将二叉树添加到我们项目的包含目录中:

    target_include_directories(foobar PUBLIC ${CMAKE_BINARY_DIR})
    

    最后,我们需要一个构建步骤依赖的自定义目标。没有这个,moc 命令永远不会真正被调用:

    add_custom_target(moc_dummy DEPENDS ${CMAKE_BINARY_DIR}/bar.moc)
    add_dependencies(foobar moc_dummy)
    

    底线:只需将所有的类定义移动到头文件中,然后再对它们进行 moc。没有任何缺点,而且你会为自己省去很多丑陋的样板。

    【讨论】:

    • 太棒了。谢谢。我试图根本不更改这个旧代码。它与 qmake 一起使用,所以我认为必须有一种方法可以使其与 CMAKE 一起使用而无需更改代码。 qt5_generate_moc 正是我所需要的。我在 cmake 文档中没有看到它,我想我只是在 Qt 文档中没有注意到它。这正是我所需要的。
    【解决方案2】:

    最简单的做法是添加AUTOMOC 属性,然后自动让CMake handle QT mocs

    set_target_properties(MyProj PROPERTIES AUTOMOC TRUE)

    【讨论】:

    • 哇,这确实有效。感谢您的链接。我不确定如何使用AUTOMOC。我发现this 帖子表明qt5_wrap_cpp 提供了更多控制权,并说AUTOMOC 存在一些问题,所以我没有尝试。我最终需要使用 AUTOMOC 和 qt5_wrap_ui,因为 AUTOUIC 有一些问题。
    • 等等。它并不完全奏效。 AUTOMOC 找到了#include "qttelnet.moc" 并从qttelnet.cpp 正确生成了它,但是我在需要moc_foo.cpp.cpp 文件的单独目录中也有.h 文件。因为这些文件位于不同的 AUTOMOC 中,所以无法生成 moc。这就是qt5_cpp_wrap() 解决的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 2011-10-23
    • 2013-09-11
    • 1970-01-01
    相关资源
    最近更新 更多