Допустим, у меня есть два файла main.cpp
и generate_header.cpp
. generate_header.cpp
генерирует заголовок, содержащий данные, которые main.cpp
необходимо запустить. основываясь на этом ответе, я могу использовать следующий код для запуска generate_header.cpp
перед созданием main.cpp
:
add_custom_target(run_generate_header
COMMAND generate_header
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "generating header"
)
add_dependencies(main_target run_generate_header)
Однако, поскольку создание содержимого заголовка происходит медленно, я хотел бы генерировать содержимое заголовка только в том случае, если заголовок пуст.
Чтобы добиться этого, я подумал об использовании макроса препроцессора HEADER_GENERATED
и check_cxx_symbol_exists
для генерации содержимого заголовка только в том случае, если макрос не определен.
check_cxx_symbol_exists(HEADER_GENERATED ${HEADER_PATH} HEADER_GENERATED)
if (NOT HEADER_GENERATED)
add_dependencies(main_target run_generate_header)
endif ()
Проблема с этим подходом заключается в том, что check_cxx_symbol_exists
запускается на этапе настройки, а не на этапе сборки, поэтому, если заголовок будет удален после настройки, он не будет создан заново, и сборка завершится неудачей. Есть ли способ проверить, содержит ли заголовок данные на этапе сборки? Заранее спасибо!
Я этого не знал. Итак, если я не могу изменить свою конфигурацию во время сборки, означает ли это, что генерация условного заголовка — не очень хорошая идея, и мне следует просто каждый раз заново генерировать заголовок?
Однако для крупных проектов это кажется обременительным. Существуют ли какие-либо передовые методы решения подобных проблем?
only if it does not contain data.
Что не содержит данных? Что это такое"? generate_header.cpp generates a header containing data that main.cpp needs to run
generate_header.cpp
компилируется в программу с именем generate_header
, которую можно использовать для генерации заголовка, который main.cpp требует для выполнения или компиляции? Не могли бы вы объяснить цепочку зависимостей более подробно?
Я не считаю, что ваша проблема сама по себе практична. Если main.cpp
использует какие-то «данные», то данные данные должны быть определены в любом случае. И от конфигурации вашего проекта зависит, будет ли main.cpp
получать эти данные из сгенерированного заголовка или из другого источника.
@KamilCuk «это» - это заголовок. generate_header
необходимо выполнить для создания заголовка и для того, чтобы main
можно было скомпилировать.
@tsyvarev Я не стал включать подробности в вопрос, так как считал, что они не имеют значения, но вот они: «данные» — это некоторые параметры нейронной сети. Первоначально эти параметры хранятся во внешнем файле .bin
. generate_header.cpp
берет параметры из этого внешнего файла и сохраняет их в заголовке. Затем main.cpp
связывается с заголовком и компилируется в один исполняемый файл, содержащий нейронную сеть. Я действительно не вижу лучшего способа сделать это.
"it" is the header.
Итак, вы генерируете заголовок из заголовка? Вы генерируете заголовок, если заголовок содержит какие-либо данные? Похоже, что если он не содержит никаких данных, то никогда и не будет. Можете ли вы четко отделить сгенерированные данные от входных? Каково исходное состояние? Можете ли вы нарисовать график зависимости draw.io?
Я хотел бы сгенерировать содержимое заголовка (используя generate_header.cpp
), если заголовок не содержит никаких данных.
в любом случае я постараюсь реализовать ваш ответ, так как он, похоже, решает мою проблему (однако это не кроссплатформенность)
Добавил правку в ответ
Не используйте add_custom_target
для созданного объекта. Если что-то генерируется, то это add_custom_command(OUTPUT this_is_generated.h
— сообщите CMake, что, где и из чего генерируется. Обычно вам нужны пары add_custom_command
и add_custom_target
. Sole add_custom_target
предназначен для выполнения чего-либо, например запуска линтера или печати размера файла.
Я хотел бы сгенерировать его, только если он не содержит данных.
Так что сделайте это. Сгенерируйте файлы в текущем двоичном каталоге cmake.
add_custom_command(
COMMENT "generating header"
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/gen/the_generated_header.h
COMMAND
sh -xc
[=[
mkdir -vp "$(dirname "$2")"
if it does contains data; then
"$1" -o "$2"
else
echo > "$2"
fi
]=]
sh
$<TARGET_FILE:generate_header>
${CMAKE_CURRENT_BINARY_DIR}/gen/the_generated_header.h
DEPENDS generate_header
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(run_generate_header DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/gen/the_generated_header.h)
add_dependencies(main_target run_generate_header)
target_include_directries(main_target PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/gen)
однако это не кроссплатформенность
Чтобы обеспечить кроссплатформенность, вы должны написать сценарий sh
в cmake. Итак, создайте файл типа:
# script.cmake
# check if the file exists in cmake
execute_process(${ARGV1} -o ${ARGV2})
# etc
Затем вы должны вызвать этот скрипт cmake из cmake:
add_custom_command(
COMMAND cmake -P ./script.cmake
$<TARGET_FILE:generate_header>
${CMAKE_CURRENT_BINARY_DIR}/gen/the_generated_header.h
....
Спасибо! Кажется, проверка существования заголовка лучше, чем мой первоначальный подход к проверке содержимого.
Для управления зависимостями существует cmake. Cmake проверяет, существует ли OUTPUT уже и превышает ли временная метка OUTPUT временную метку DEPENDS-ency.
«Есть ли способ проверить, содержит ли заголовок данные на этапе сборки?» - Непонятно, чего вы хотите добиться такой проверкой. Например, если данная проверка прошла успешно, вы не сможете запускать
add_dependencies
или другие команды CMake, поскольку контекст CMake не существует на этапе сборки.