Как всегда запускать команду при создании независимо от какой-либо зависимости?

Я хочу запустить команду cmake, которая анализирует все исходное дерево, поэтому я не могу перечислить все возможные зависимости в командах cmake add_custom_command/add_custom_target.

Можно ли сказать cmake просто запустить команду без каких-либо условий? Я пробовал все решения, найденные в сети (включая SO), но все они предполагают, что команда зависит от нескольких известных файлов, которые находятся в актуальном состоянии.

Я нашел решение, но оно не работает надежно:

cmake_minimum_required(VERSION 2.6)

project(main)

add_custom_command(
   OUTPUT file1
   COMMAND echo touching file1
   COMMAND touch file1
   DEPENDS file2)
add_custom_target(dep ALL DEPENDS file1 file2)

# this command re-touches file2 after dep target is "built"
# and thus forces its rebuild
ADD_CUSTOM_COMMAND(TARGET dep
          POST_BUILD
          COMMAND echo touching file2
          COMMAND touch file2
)

и это выводится:

[email protected]:~/testlib$ make
[100%] Generating file1
touching file1
touching file2
[100%] Built target dep
[email protected]:~/testlib$ make
[100%] Generating file1
touching file1
touching file2
[100%] Built target dep
[email protected]:~/testlib$ make
touching file2
[100%] Built target dep
[email protected]:~/testlib$ 

Как вы можете видеть, на третьем запуске он не сгенерировал файл1, хотя файл2 был тронут ранее. Иногда это происходит каждый второй раз, иногда каждый третий, иногда каждый четвертый. Это ошибка? Есть ли другой способ запустить команду без какой-либо зависимости в cmake?

Странно, но если я добавлю TWO команды для повторного касания файла2, т.е. просто скопируйте-вставьте команду post-build, она работает надежно. Или, может быть, он провалится каждый 1000-й запуск, я еще не уверен, -)

Ответ 1

Ответ на ideman42 заключается в создании фиктивного вывода с пустой инструкцией echo. Преимущество состоит в том, что у вас может быть несколько пользовательских команд, зависящих от этого фиктивного вывода.

Кроме того, система сборки cmake будет знать, что выходной файл вашей пользовательской команды так, чтобы любые зависимости от этого вывода могли быть правильно разрешены.

# Custom target will always cause its dependencies to be evaluated and is
# run by default
add_custom_target(dummy_target ALL
    DEPENDS
        custom_output
    )

# custom_output will always be rebuilt because it depends on always_rebuild
add_custom_command(
    OUTPUT custom_output
    COMMAND command_that_produces_custom_output
    DEPENDS
        always_rebuild
    )

# Dummy output which is never actually produced. Anything that depends on
# this will always be rebuilt.
add_custom_command(
    OUTPUT always_rebuild
    COMMAND cmake -E echo
    )

cmake -E echo находится как можно ближе к no-op, так как cmake имеет.

Ответ 2

Пока я не совсем доволен этим решением, отправляю сообщения, так как я наткнулся на эту страницу и не видел, чтобы это упоминалось.

Вы можете добавить пользовательскую цель, которая ссылается на отсутствующий файл,

например:

add_custom_target(
    my_custom_target_that_always_runs ALL
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/__header.h
    )

add_custom_command(
    OUTPUT
        ${CMAKE_CURRENT_BINARY_DIR}/__header.h  # fake! ensure we run!
        ${CMAKE_CURRENT_BINARY_DIR}/header.h    # real header, we write.
    # this command must generate: ${CMAKE_CURRENT_BINARY_DIR}/header.h
    COMMAND some_command
    )

Это приведет к запуску пользовательской команды, поскольку __header.h не найден.

Смотрите рабочий пример, где это используется.

Ответ 3

Я искал точно то же самое, и я наконец нашел решение "notSoWorkaround".

ADD_CUSTOM_TARGET(do_always ALL COMMAND yourCommandRegardlessOfAnyDependency)

Это добавляет цель, которая будет запущена после ВСЕ. И поскольку пользовательские цели всегда считаются устаревшими, он будет работать всегда.

Вам может понадобиться DEPENDS yourA.out для запуска после сборки

Мои источники:

Ответ 4

Итак, вот мое решение. Я добавляю фальшивую библиотеку:

add_subdirectory(fake)
add_dependencies(${PROJECT_NAME} fake)

и там я делаю это:

cmake_minimum_required (VERSION 2.6)
project(fake CXX)
add_library(${PROJECT_NAME} SHARED fake.cpp)
add_custom_command(TARGET fake
    POST_BUILD
    COMMAND ./mycommand.sh
    COMMAND rm ${ROOT_BIN_DIR}/libfake.so
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

Итак, как вы можете видеть, я просто удаляю файл .so после сборки, что заставляет поддельную библиотеку перестраиваться каждый раз, когда выполняется POST_BUILD, и все это перед основным PROJECT_NAME, потому что оно зависит от подделки.