add_custom_command 的可选依赖项
Optional dependencies for add_custom_command
如果我在 add_custom_command
的 DEPENDS
部分包含不存在的文件,它会失败。有没有一种方法可以指定依赖项可能不存在,但如果已创建,则应重新运行命令?
具体来说,我正在使用 Coco/R,它要求 Parser.frame
和 Scanner.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
,当然构建程序不会知道它。
对于常见的生成器,您可以使用 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=""
depfile=""
input=""
copyright=""
# 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 that runs m4 preprocessor and grabs m4 included files and generates depfile, and this script 调用 add_custom_command( ${CMAKE_COMMAND} -Pthe_other_script.cmake DEPFILE ...)
.
仍然,如果你添加 Copyright.frame
文件,cmake 将 不会 在第一个 运行 上获取它,但是如果你修改其他内容,那么将重新生成 depfile 并选择 depdency。或者您可以在每次构建时重新生成 depfile。
如果我在 add_custom_command
的 DEPENDS
部分包含不存在的文件,它会失败。有没有一种方法可以指定依赖项可能不存在,但如果已创建,则应重新运行命令?
具体来说,我正在使用 Coco/R,它要求 Parser.frame
和 Scanner.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
,当然构建程序不会知道它。
对于常见的生成器,您可以使用 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=""
depfile=""
input=""
copyright=""
# 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 that runs m4 preprocessor and grabs m4 included files and generates depfile, and this script 调用 add_custom_command( ${CMAKE_COMMAND} -Pthe_other_script.cmake DEPFILE ...)
.
仍然,如果你添加 Copyright.frame
文件,cmake 将 不会 在第一个 运行 上获取它,但是如果你修改其他内容,那么将重新生成 depfile 并选择 depdency。或者您可以在每次构建时重新生成 depfile。