2016-10-27 1 views
3

часто необходимо для того, чтобы CMake строить проекты в конечном итоге в определенном месте после компиляции, а команда add_custom_command(..POST_BUILD...) является общим шаблоном дизайна для достижения этой цели:альтернативы команды CMake POST_BUILD, когда цель находится в подкаталоге

add_custom_command(
    TARGET mytarget 
    POST_BUILD 
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:mytarget> ${CMAKE_BINARY_DIR}/final_destination 
) 

К сожалению, это не работает, когда рассматриваемая цель находится в подкаталоге относительно файла, содержащего вызов add_custom_command, который был скомпилирован с помощью команды add_subdirectory(). Попытка сделать это приводит к следующему сообщению об ошибке:

CMake Warning (dev) at CMakeLists.txt:4 (add_custom_command): 
    Policy CMP0040 is not set: The target in the TARGET signature of 
    add_custom_command() must exist. Run "cmake --help-policy CMP0040" for 
    policy details. Use the cmake_policy command to set the policy and 
    suppress this warning. 

    TARGET 'mytarget' was not created in this directory. 
This warning is for project developers. Use -Wno-dev to suppress it. 

Во многих случаях существует простой обходной путь: просто убедитесь, что add_custom_command() вызова происходит в CMakeLists.txt файле подкаталога, и все будет работать.

Однако это не всегда возможно! Подкаталог может быть проектом CMake внешней зависимости, над которым мы не контролируем. Например, довольно распространено комбинирование рекурсивных компиляций CMake с подмодулями Git, и в этом случае нет возможности постоянно сохранять модификации системы построения дочерних проектов.

Мой вопрос затем сводится к следующему: предлагает ли CMake другой механизм для создания цели, которая будет автоматически инициирована при восстановлении цели дочернего проекта и которая может быть использована для копирования окончательной исполняемой или разделяемой библиотеки на некоторые Другое место?

Моя цель заключается в том, что это происходит автоматически, без необходимости специально называть 'make'/'ninja' другой целью. Кроме того, копия должна выполняться только тогда, когда она действительно необходима (в соответствии с документами cmake некоторые команды add_custom_ * не отслеживают, нужно ли их фактически запускать и консервативно предположить, что цель всегда устарела).

+0

Все ли файлы подкаталогов 'CMakeLists.txt' из того же самого/вашего проекта? И говорим ли мы обо всех объектах, например, или вы хотите применить это только к конкретным целям? В моем проекте я создал свою собственную функцию() 'для всех стандартных вызовов CMake, поэтому я могу изменить стандартное поведение, например. 'add_executable()' для моего проекта (например, автоматически добавляет шаг после сборки ко всем таким вызовам). – Florian

+0

Подкаталоги обычно будут разделяемыми библиотеками, распространяемыми как git-подмодули, клонированные из github (например, '' pugixml''). Эти проекты имеют свои проекты CMake, которые я не могу (и не хочу) менять. Родительский проект может генерировать смесь исполняемых или разделяемых библиотек - я не утончаю, это особенно важно для этого вопроса. –

ответ

2

Просто используйте общую комбинацию add_custom_command и add_custom_target, когда первый один производит файл для второго:

# Because OUTPUT option may not use generator expressions, 
# extract name of file from target's properties. 
get_target_properties(mytarget_basename mytarget OUTPUT_NAME) 
get_target_properties(mytarget_suffix mytarget SUFFIX) 
set(mytarget_filename ${mytarget_basename}${mytarget_suffix}) 
# make copied file be dependent from one which is build. 
# Note, that DEPENDS here creates dependencies both from the target 
# and from the file it creates. 
add_custom_command(OUTPUT 
     ${CMAKE_BINARY_DIR}/final_destination/${mytarget_filename} 
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:mytarget> 
     ${CMAKE_BINARY_DIR}/final_destination 
    DEPENDS mytarget 
) 
# Create target which consume the command via DEPENDS. 
add_custom_target(copy_files ALL 
    DEPENDS ${CMAKE_BINARY_DIR}/final_destination/${mytarget_filename} 
) 

Сравнение с POST_BUILD используя этот код использует дополнительные цели. Но у вас нет другого выбора: add_custom_command не может быть прикреплен к цели, созданной в другом каталоге.


Обычно вместо copiing исполняемых/библиотек в другой двоичный каталог проще указать этот каталог с помощью CMAKE_<TYPE>_OUTPUT_DIRECTORY переменных.

+0

Это не совсем соответствует счету: моя цель заключается в том, что копия запускается по умолчанию, даже если не указывать цель «сделать» или «ниндзя». Кроме того, копия должна выполняться только тогда, когда она действительно необходима (в соответствии с документами cmake некоторые команды add_custom_ * не отслеживают, нужно ли их фактически запускать и консервативно предположить, что цель всегда устарела). –

+0

'моя цель в том, что копия запускается по умолчанию' - после добавления опции * ALL * цель будет построена по умолчанию. Я исправил ответ. 'copy должен выполняться только тогда, когда это действительно необходимо' - это то, что делает' add_custom_command'. Это * target * (добавлено с 'add_custom_target'), который считается всегда устаревшим. Но поскольку он не содержит опции * COMMAND *, ничего не делается. – Tsyvarev

+0

Я пропустил '' ВСЕ'' выше - это аккуратно, спасибо! –