【问题标题】:How to instruct cmake/automoc to find external header如何指示 cmake/automoc 查找外部标头
【发布时间】:2015-04-14 01:42:45
【问题描述】:

我有一个 Qt 小部件 C++ 类,它加载在 Qt Creator 中创建的 ui 文件。类的头文件和源文件位于两个单独的目录中。我无法指示 cmake/automoc 找到该类的标题。 cmake 认识到它需要 moc C++ 文件,但找不到类似的头文件。

我可以做些什么来帮助 cmake 找到文件吗?

如果 cpp 和头文件都在同一个目录中,则一切正常。仅当标题位于其他位置时才会出现这种情况。

我的目录结构是

project
    src
        include
            Foo
                Bar.h
    lib
        Foo
            Bar.cpp
            forms
                Bar.ui           

在 src/include/Foo/Bar.h 我有:

// Bar.h
#include <QtWidgets/QWidget>

namespace Ui { class Bar; }

class Bar : public QWidget {
    Q_OBJECT
    ...
}

在 src/Foo/Bar.cpp 文件中:

#include "Foo/Bar.h"
#include "moc_Bar.cpp"
#include "ui_Bar.h"

我在 src/lib/Foo 中的 CMakeLists.txt 设置如下:

# 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})

当我运行 cmake 时,我收到以下错误:

AUTOGEN:错误:/automoc/src/lib/Foo/Bar.cpp 该文件包含 moc 文件“moc_Bar.cpp”,但找不到标题“Bar{.h,.hh,.h++,.hm ,.hpp,.hxx,.in,.txx}" 在 /automoc/src/lib/Foo/

【问题讨论】:

  • 您的代码中不需要很多东西(例如,如果您已经在使用find_package(Qt5Widget),则在find_package(Qt5Core) 中不需要)。但我认为错误在于您需要包含 ui_Bar.h 而不是 *.cpp 文件。见example
  • 当我从我为重现问题而构建的实际测试用例中重新键入代码时,这只是一个错误。真正的代码包括头文件,而不是 cpp。谢谢你抓住它;我更正了这个问题。
  • 顺便说一句,我尝试通过 set(CMAKE_AUTOUIC ON) 使用其他一些自动功能(我不知道),例如 autouic,但这也失败了,因为它只寻找Bar.cpp 所在目录中的 ui 文件;它无法确定它位于单独的目录(表单)中。
  • 是的,我尝试将表单移动到其他目录,但编译失败。看起来它的设计使所有文件都必须在同一个目录中。
  • 似乎这是当前 CMake automoc 代码中的一个限制,作为一种解决方法,您可以添加一个自定义目标来调用 moc src/include/Foo -o src/lib/moc_Foo.cpp。似乎没有任何选项可以通过AUTOMOC_MOC_OPTIONS 传递给 moc,CMAKE_AUTOMOC_RELAXED_MODE 也不起作用。看起来唯一的解决方案是提交 CMake 错误报告。

标签: qt cmake qt5 moc


【解决方案1】:

您必须手动包装头文件。 将其放入您的 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

这种方法的真实示例 https://github.com/paceholder/nodeeditor/blob/master/CMakeLists.txt#L133

【讨论】:

  • 也许可以在其他方向工作,但如果(如你所说)src/include公共包含目录,那么大概是从哪里安装标头。安装一个只包含另一个(未安装的)头文件的相对包含的头文件会创建一个非常糟糕的安装。
【解决方案2】:

我发现只需将AUTOMOC 留给自己的设备即可简化解决方法。这是我所做的(适用于我们所有支持的 CMake 版本,目前为3.2...3.17):

  1. 删除文件Bar.cpp中的#include "moc_Bar.cpp"
  2. 将外部(到当前目录)头文件添加为目标的PRIVATE 源:
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 在 MOC 处理相关文件时将创建一个 Foo_autogen 输出目录。 moc_Bar.cppmoc_Baz.cpp 将在一个随机命名的 ABDEADBEEF 子目录中创建,并且将创建一个 mocs_compilation.cpp 文件,其内容如下:

// This file is autogenerated. Changes will be overwritten.
#include "ABDEADBEEF/moc_Bar.cpp"
#include "ABDEADBEEF/moc_Baz.cpp"

该文件被编译并与目标的最终输出链接。

使用CMAKE_AUTOMOC 全局设置True,每个目标也将收到它自己的target_autogen 目录,尽管在没有任何MOC'able 类的目标中不会生成任何内容。不过,最好只在需要它的目标上设置AUTOMOC 目标属性:

set_property(TARGET Foo PROPERTY AUTOMOC ON)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多