CMake在生成前运行自定义命令?

pu3pd22g  于 2023-02-19  发布在  其他
关注(0)|答案(3)|浏览(186)

我希望在每次构建之前运行的二进制示例源代码,每add_executable运行一次:

#include <stdio.h>

int main(int argc, char *argv[]) {
    for(int i=0; i<argc; ++i)
        printf("argv[%d] = %s\n", i, argv[i]);
    fclose(fopen("foo.hh", "a"));
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(foo_proj)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
set(SOURCE_FILES main.cpp)
# ---- this line changes ----
add_executable(foo_proj ${SOURCE_FILES})

尝试:

add_custom_target(create_foo_hh COMMAND /tmp/bin/create_foo_hh)
add_dependencies(${SOURCE_FILES} create_foo_hh)

错误:无法向不存在的目标"main.cpp"添加目标级依赖关系。add_dependencies适用于由add_executable、add_library或add_custom_target命令创建的顶级逻辑目标。如果要添加文件级依赖关系,请参阅add_custom_target和add_custom_command命令的DEPENDS选项。

execute_process(COMMAND /tmp/bin/create_foo_hh main.cpp)

没有错误,但是foo.hh没有创建。
如何自动运行此命令?

zf9nrax1

zf9nrax11#

execute_process()在配置时调用。
您可以使用add_custom_command()

add_custom_command(
  OUTPUT foo.hh
  COMMAND /tmp/bin/create_foo_h main.cpp
  DEPENDS ${SOURCE_FILES} /tmp/bin/create_foo_hh main.cpp
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(foo_proj ${SOURCE_FILES} foo.hh)

这样,foo.hh就是foo_proj的依赖项:并且您的命令将在构建foo_proj时被调用。它依赖于${SOURCE_FILES}/tmp/bin/create_foo_hh main.cpp,因此如果其中一个文件发生变化,它将再次生成。
关于路径,add_custom_command()被配置为在当前构建目录中运行以在那里生成文件,include_directories()用于将构建目录添加到include目录。

bejyjqdl

bejyjqdl2#

您可能不希望自定义目标依赖于源文件(因为它们本身不是目标,因此永远不会“运行”),而是依赖于您使用它们创建的目标:

target_add_dependencies(foo_proj create_foo_hh)
gorkyyrv

gorkyyrv3#

我认为最干净的方法是添加两个新的project()(目标),然后将生成的文件添加到最终的可执行文件中,这就是cmake如何构建一个有效的依赖树,以便当源文件更改时,它们会被重新编译,命令会运行,从而使所有内容都保持最新。

构建可执行文件

首先,正如您在示例中所做的那样,我从某个.cpp文件创建一个可执行文件:

  • (示例摘自as 2 js项目)*
project(unicode-characters)

add_executable(${PROJECT_NAME}
    unicode_characters.cpp
)

target_include_directories(${PROJECT_NAME}
    PUBLIC
        ${ICU_INCLUDE_DIRS}
        ${SNAPDEV_INCLUDE_DIRS}
)

target_link_libraries(${PROJECT_NAME}
    ${ICU_LIBRARIES}
    ${ICU_I18N_LIBRARIES}
)

正如我们所看到的,我们可以在m中添加特定的包含路径(-I)和库路径(-L),它是特定于一个目标的,因此您可以拥有一组与其他可执行文件所使用的路径不同的路径。

生成附加文件

接下来,创建一个自定义命令来运行可执行文件,如下所示:

project(unicode-character-types)

set(UNICODE_CHARACTER_TYPES_CI ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ci)

add_custom_command(
    OUTPUT
        ${UNICODE_CHARACTER_TYPES_CI}

    COMMAND
        unicode-characters >${UNICODE_CHARACTER_TYPES_CI}

    WORKING_DIRECTORY
        ${PROJECT_BINARY_DIR}

    DEPENDS
        unicode-characters
)

add_custom_target(${PROJECT_NAME}
    DEPENDS
        ${UNICODE_CHARACTER_TYPES_CI}
)

请注意以下几点:
1.我设置了一个变量(UNICODE_CHARACTER_TYPES_CI),因为我将多次引用该文件
a.注意我是如何将目标放入二进制文件的(cmake输出文件夹)。这是避免在源代码树中生成这些文件的最佳方法(最后可能会把这个文件添加到你的源代码跟踪系统中,比如svn或者git)b. add_custom_command()的一个重要方面是DEPENDS部分,它包含了你的 special 命令的名字,我们在上一步中定义的。

  1. add_custom_target()允许cmake在源文件(也称为依赖项)发生变化时找到目标并执行相应的命令;注意DEPENDS的定义。

使用输出

最后,这是一个主项目(在我的例子中是一个库),它使用了我们在上一步中生成的文件。
请注意,我使用在上一步中定义的变量引用该文件,这样,当我想更改名称时,只需编辑该变量即可。

project(as2js)

configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
    ${CMAKE_CURRENT_BINARY_DIR}/version.h
)

add_library(${PROJECT_NAME} SHARED
    compiler/compiler.cpp
    ...
    parser/parser_variable.cpp
    ${UNICODE_CHARACTER_TYPES_CI}

    file/database.cpp
    ...
)
  • (注:...代表文件列表,此处显示时请缩短,因为这些文件并不重要,上面的链接将带您访问包含完整列表的文件。)*

通过将文件名包含在add_library()(或您的示例中的add_executable())中定义的文件列表中,您创建了一个依赖项,该依赖项将找到您的custom_target(),因为文件名在add_custom_command() ¹的OUTPUT部分中定义。
¹ * 可以为add_custom_command()定义多个输出。例如,我的一些生成器输出.cpp.h。在这种情况下,我只需在OUTPUT节中定义这两个文件。*

结果

关于此解决方案最终结果的要点:
1.生成器的输出文件保存在二进制输出路径中,而不是保存在当前工作目录中
1.由cmake生成的Makefile包括所有必要的目标/依赖项,这意味着更改任何输入文件都会按预期重新生成所有内容(即使您只是更新注解)
1.如果生成器失败,则构建按预期失败
1.文件由构建步骤(make time)生成,而不是由生成步骤(cmake time,类似于execute_process())生成

相关问题