【问题标题】:Optional dependencies for add_custom_commandadd_custom_command 的可选依赖项
【发布时间】:2021-09-17 06:30:27
【问题描述】:

如果我在add_custom_commandDEPENDS 部分中包含不存在的文件,则会失败。有没有办法指定依赖项可能不存在,但如果它创建了命令应该重新运行?

具体来说,我使用的是 Coco/R,它要求 Parser.frameScanner.frame 文件存在于与语法文件相同的目录中或由命令行参数指定的目录中,并且 Copyright.frame 可以存在或不是。所以我这样做:

foreach(FRAME_DIR IN ITEMS ${GRAMMAR_DIR} ${ARG_FRAME_DIR})
    foreach(FRAME_FILE_NAME IN ITEMS Copyright Scanner Parser)
        set(FULL_FRAME_FILE ${FRAME_DIR}/${FRAME_FILE_NAME}.frame)
        if(EXISTS ${FULL_FRAME_FILE})
            list(APPEND FRAME_FILES ${FULL_FRAME_FILE})
        endif()
    endforeach()
endforeach()

...

set(GEN_SOURCES ${ARG_OUTPUT_DIR}/Scanner.h ${ARG_OUTPUT_DIR}/Parser.h ${ARG_OUTPUT_DIR}/Scanner.cpp ${ARG_OUTPUT_DIR}/Parser.cpp)

add_custom_command(OUTPUT ${GEN_SOURCES}
    COMMAND ${COCOCPP} ${COCOCPP_ARGS}
    MAIN_DEPENDENCY ${ARG_GRAMMAR}
    DEPENDS ${FRAME_FILES}
    VERBATIM)

add_library(${ARG_TARGET} ${GEN_SOURCES})

如果我在foreach 中注释掉if,我会得到

ninja: error: '../verification/debug/config/Copyright.frame', needed by 'verification/gen/config/Parser.h', missing and no known rule to make it

在构建时。有了这个if,文件就生成了,但是如果我稍后创建Copyright.frame,当然构建程序不会知道它。

【问题讨论】:

  • 你能提供一个具体的例子来说明你正在尝试做什么吗?我要么设计一个生成器表达式来计算正确的依赖集,要么将相同的命令附加到通过POST_BUILD构建可选文件的目标。
  • 看看你得到的确切失败/生产它的 MRE 也很好。
  • 我已经添加了我实际在做的事情。当然,这不是 MRE,但如果仍然需要,我明天会这样做。使用检查文件是否存在并将结果写入另一个文件的命令就足够了。太糟糕了,没有 CMake 操场(我能找到)。
  • Copyright.frame 可以是空文件吗?你不能在配置阶段检查文件是否存在吗? if I create Copyright.frame later, 然后重新配置您的项目,这在您添加文件时通常是正常的,您通常需要重新配置 cmake。
  • "你不能在配置阶段检查文件是否存在吗?"这就是我目前所做的,并询问是否有更好的方法。

标签: cmake cmake-language


【解决方案1】:

对于常见的生成器,您可以使用 DEPFILE 来生成 Make-ish 依赖项,就像 gcc -MT -MD 对包含文件所做的那样。您必须编写一个包装器来检测文件是否存在,然后生成文件。

depfile 大致如下所示:

output: source1 source2

并且output 必须与 CMAKE_BINARY_DIR 相关(这花了我很长时间才知道)和source1 是绝对的。而且您还必须在名称中转义空格、制表符和换行符以及# (!)。一般:

# CMakeLists.txt
# Example for one file
set(output ${ARG_OUTPUT_DIR}/Scanner.h)
# Relative to BINARY_DIR!
file(RELATIVE_PATH outputbinrela "${CMAKE_BINARY_DIR}" "${output}")
set(depfile "${CMAKE_CURRENT_BINARY_DIR}/Scanner.h.d" ABSOLUTE)
add_custom_command(
   OUTPUT ${someoutput}
   DEPFILE ${depfile}
   # TODO: write it in cmake, to be portable
   COMMAND sh ./thescript.sh
        "${outputbinrela}" "${depfile}" "${input}"
        ${FRAME_DIR}/Copyright.frame
   DEPENDS ${input}
   WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
   VERBATIM
)

# ./thescript.sh
outputbinrela="$1"
depfile="$2"
input="$3"
copyright="$4"

# Check if the file exists - if it does, add it to depfile
if [[ -e "$copyright" ]]; then
    copyright=$(readlink -f "$copyright") # absolute!
    echo "$outputbinrela: $copyright" > "$depfile"
else
    echo "$outputbinrela:" > "$depfile"
fi

# Run the command to generate output.
yourcommand "$copyright" > "$input"

我曾经为m4预处理器写过这样的系统,你可以看看this script运行m4预处理器并抓取m4包含的文件并生成depfile,以及this script调用add_custom_command( ${CMAKE_COMMAND} -Pthe_other_script.cmake DEPFILE ...)

不过,如果你添加Copyright.frame 文件,cmake 将不会在第一次运行时选择它,但如果你修改其他内容,则将重新生成 depfile 并选择依赖关系。或者您可以在每次构建时重新生成 depfile。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-15
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多