Проверьте, создан ли заголовок с помощью cmake

Допустим, у меня есть два файла 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 запускается на этапе настройки, а не на этапе сборки, поэтому, если заголовок будет удален после настройки, он не будет создан заново, и сборка завершится неудачей. Есть ли способ проверить, содержит ли заголовок данные на этапе сборки? Заранее спасибо!

«Есть ли способ проверить, содержит ли заголовок данные на этапе сборки?» - Непонятно, чего вы хотите добиться такой проверкой. Например, если данная проверка прошла успешно, вы не сможете запускать add_dependencies или другие команды CMake, поскольку контекст CMake не существует на этапе сборки.

Tsyvarev 24.06.2024 14:22

Я этого не знал. Итак, если я не могу изменить свою конфигурацию во время сборки, означает ли это, что генерация условного заголовка — не очень хорошая идея, и мне следует просто каждый раз заново генерировать заголовок?

Nonlinear 24.06.2024 14:36

Однако для крупных проектов это кажется обременительным. Существуют ли какие-либо передовые методы решения подобных проблем?

Nonlinear 24.06.2024 14:37
only if it does not contain data. Что не содержит данных? Что это такое"? generate_header.cpp generates a header containing data that main.cpp needs to rungenerate_header.cpp компилируется в программу с именем generate_header, которую можно использовать для генерации заголовка, который main.cpp требует для выполнения или компиляции? Не могли бы вы объяснить цепочку зависимостей более подробно?
KamilCuk 24.06.2024 14:42

Я не считаю, что ваша проблема сама по себе практична. Если main.cpp использует какие-то «данные», то данные данные должны быть определены в любом случае. И от конфигурации вашего проекта зависит, будет ли main.cpp получать эти данные из сгенерированного заголовка или из другого источника.

Tsyvarev 24.06.2024 14:48

@KamilCuk «это» - это заголовок. generate_header необходимо выполнить для создания заголовка и для того, чтобы main можно было скомпилировать.

Nonlinear 24.06.2024 14:51

@tsyvarev Я не стал включать подробности в вопрос, так как считал, что они не имеют значения, но вот они: «данные» — это некоторые параметры нейронной сети. Первоначально эти параметры хранятся во внешнем файле .bin. generate_header.cpp берет параметры из этого внешнего файла и сохраняет их в заголовке. Затем main.cpp связывается с заголовком и компилируется в один исполняемый файл, содержащий нейронную сеть. Я действительно не вижу лучшего способа сделать это.

Nonlinear 24.06.2024 14:58
"it" is the header. Итак, вы генерируете заголовок из заголовка? Вы генерируете заголовок, если заголовок содержит какие-либо данные? Похоже, что если он не содержит никаких данных, то никогда и не будет. Можете ли вы четко отделить сгенерированные данные от входных? Каково исходное состояние? Можете ли вы нарисовать график зависимости draw.io?
KamilCuk 24.06.2024 15:16

Я хотел бы сгенерировать содержимое заголовка (используя generate_header.cpp), если заголовок не содержит никаких данных.

Nonlinear 24.06.2024 15:29

в любом случае я постараюсь реализовать ваш ответ, так как он, похоже, решает мою проблему (однако это не кроссплатформенность)

Nonlinear 24.06.2024 15:35

Добавил правку в ответ

KamilCuk 24.06.2024 16:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
11
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не используйте 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
  ....

Спасибо! Кажется, проверка существования заголовка лучше, чем мой первоначальный подход к проверке содержимого.

Nonlinear 24.06.2024 16:37

Для управления зависимостями существует cmake. Cmake проверяет, существует ли OUTPUT уже и превышает ли временная метка OUTPUT временную метку DEPENDS-ency.

KamilCuk 24.06.2024 17:03

Другие вопросы по теме