Установка только одной цели (и ее зависимостей) из сложного проекта с помощью cmake (открыты для лучших решений)

Допустим, у меня есть проект, состоящий из нескольких подпроектов A, B, C, D... Все подпроекты зависят от A, который довольно часто меняется. Кроме того, могут быть некоторые дополнительные зависимости: в этом примере D зависит от B.

Сейчас: над этими проектами работает много людей. Основной файл CMakeLists.txt должен включать в себя все каталоги, чтобы билд собирал все. Но люди также хотели бы иметь возможность работать только над одним из этих проектов, а не каждый раз собирать/устанавливать все подряд.

Если я работаю над D, я могу легко построить "только" D, позвонив

cmake --build . --target D -- -j7

or

ninja -j7 D

Это также построит A и B, если что-то для них изменилось. Идеально.

Но как я могу вызвать установку только для D, не запуская сборку всех? Я хотел бы, чтобы, если я позвоню:

ninja -j7 D install

он построил только D (и зависимости), а затем установил только D и его зависимости (A и B). Вместо этого он собирает целевое все и устанавливает все.

Я хотел бы сохранить эту цель, чтобы все продолжали строить все. Так что EXCLUDE_FROM_ALL не вариант. Но, двигаясь в этом направлении, я не мог найти никакого решения.

Поэтому я думаю о следующей стратегии:

  • За исключением подпроекта A, все остальные цели устанавливаются в EXCLUDE_FROM_ALL и НЕОБЯЗАТЕЛЬНО при установке.
  • Я добавляю один дополнительный подпроект, который просто зависит от всех других подпроектов (может быть, я заставляю каждую цель публиковать свое имя, используя некоторую переменную, установленную в PARENT_SCOPE), и людям придется вызывать ее, когда они хотят собрать и установить все.

Это сработает? Есть ли лучшее решение?

Мы хотели бы избежать того, чтобы всем приходилось редактировать основной файл CMakeLists.txt, чтобы исключить проекты, которые ему не интересны. Решение должно быть переносимым на разные ОС.

Изменить:

Я попробовал предложенную мной стратегию, но она не сработала: в моем случае размещение инструкции установки для цели (даже если она указана как НЕОБЯЗАТЕЛЬНАЯ) сделает неэффективным EXCLUDE_FROM_ALL. Почитав лучше документацию, я обнаружил, что:

Installing a target with EXCLUDE_FROM_ALL set to true has undefined behavior.

Я также получаю это предупреждение:

Target <targetname> has EXCLUDE_FROM_ALL set and will not be built by default but an install rule has been provided for it.  CMake does not define behavior for this case.

Редактировать 2:

Я попытался указать EXCLUDE_FROM_ALL в качестве опции add_subdirectory (вместо add_library/add_executable), но тогда все операторы установки в этих подкаталогах, похоже, игнорируются: будут установлены только операторы установки в неисключенных из всех подкаталогах.

Редактировать 3:

Даже если я активирую CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:

set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)

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

ninja -j7 D && ninja install

по какой-то причине не будет работать, заявив, что C (установка которого была установлена ​​как НЕОБЯЗАТЕЛЬНАЯ) не существует (он не был создан, потому что D зависел только от A и B)...

file INSTALL cannot find "<name of dll file for C>"

Редактировать 4:

Мне это кажется ошибкой cmake. (Я использую 2.8.11 под Windows, также тестировал 2.8.10) Эта команда INSTALL

install(TARGETS ${targetname} RUNTIME DESTINATION . LIBRARY DESTINATION . OPTIONAL)

преобразуется в cmake_install.cmake как:

IF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")   

ФАЙЛ(ЦЕЛЬ УСТАНОВКИ "${CMAKE_INSTALL_PREFIX}/." TYPE SHARED_LIBRARY FILES *path_to_dll*)

ЕСЛИ(СУЩЕСТВУЕТ "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./" AND NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./*dll_name*")

IF(CMAKE_INSTALL_DO_STRIP)

  EXECUTE_PROCESS(COMMAND "C:/Programs/MinGW/bin/strip.exe" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./*dll_name*")

ENDIF(CMAKE_INSTALL_DO_STRIP)   ENDIF() ENDIF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")

с командой ФАЙЛ отсутствует НЕОБЯЗАТЕЛЬНО! Если я добавлю НЕОБЯЗАТЕЛЬНО вручную, это сработает! (примечание: здесь я отредактировал заполнители *dll_name* и *path_to_dll*)

Редактировать 5:

Я подтверждаю, что это ошибка cmake или, по крайней мере, неправильная документация. Я сообщу об этом. Ситуация разрешилась либо поставив более простой

install(TARGETS ${targetname} DESTINATION . OPTIONAL)

(но в моем случае это также установит файлы .lib.a, которые мне не нужны)

или перед ДОПОЛНИТЕЛЬНЫМ флагом:

install(TARGETS ${targetname} OPTIONAL RUNTIME DESTINATION . LIBRARY DESTINATION .)

Из документации cmake следует понять, что НЕОБЯЗАТЕЛЬНЫЙ должен быть указан как последний вариант.


person Antonio    schedule 18.06.2013    source источник
comment
Вы должны сделать свои последние два редактирования ответом. Потому что это отвечает на ваш вопрос о текущем состоянии дел.   -  person Patrick B.    schedule 19.06.2013
comment
Да, теперь он готов.   -  person Antonio    schedule 20.06.2013


Ответы (4)


Что работает:

  • Удалить зависимость цели "установить" от цели "все" (один раз, в основном CMakeLists.txt):

    set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)

  • Установите ДОПОЛНИТЕЛЬНУЮ установку всех целей, которые вы не хотите всегда собирать:

    install(TARGETS <<targetname>> DESTINATION . OPTIONAL)

  • Создайте цели, которые вы хотите установить

    ninja -j7 <<list of targets>> или, в более общем смысле:

    <<your builder>> <<your options>> <<list of targets>>

    Это создаст все перечисленные цели и их зависимости.

  • Позвоните установщику ninja install. Это установит все библиотеки, которые вы создали, те, которые вы явно упомянули, и те, от которых они зависели. Это решает проблему установки конкретной цели вместе с ее зависимостями!

  • Чтобы объединить команды, как в Unix, так и в Windows вы можете использовать

    ninja -j7 <<list of targets>> && ninja install

  • Обратите внимание, что, по крайней мере, для ниндзя вы не можете просто добавить «установить» к списку целей, поскольку цель «установить» больше не зависит ни от какой цели, и ниндзя при распараллеливании задания запустит установку, пока вы все еще создаете свои цели. . Замена старому ninja -j7 install есть

    ninja -j7 && ninja install.

    Цель «все» по-прежнему доступна (и она по-прежнему является целью по умолчанию).

  • Если вам нужно создать список целей, которые вы хотите построить вместе, вы можете определить пользовательская цель:

    add_custom_target(<<collective target name>> DEPENDS <<list of targets>>)

    Это не будет включено в цель все. Добавление также КОМАНДЫ также позволило бы создать цель установки для любого количества целей, например ninja -j7 install_D, но я думаю, что сейчас мы выходим за рамки этого вопроса.

Дополнительные соображения:

  • (Примечание, июль 2018 г.: это может быть устаревшим) Если вы используете RUNTIME DESTINATION, LIBRARY DESTINATION и т. д., скорее всего, из-за ошибки CMake, НЕОБЯЗАТЕЛЬНОЕ ключевое слово должно быть помещено точно в позицию, указанную ниже:

    install(TARGETS <<targetname>> OPTIONAL RUNTIME DESTINATION <<some dir>> LIBRARY DESTINATION <<some (other) dir>>)

    Это противоречит тому, что написано в документации, и я сообщу об этом. как ошибка для разработчиков CMake.

  • #P14# <блочная цитата> #P15# #P16#
person Antonio    schedule 19.06.2013
comment
Можете ли вы опубликовать ссылку на ваш отчет об ошибке? Я наткнулся на ту же проблему заказа с OPTIONAL. - person letmaik; 19.10.2014
comment
@neo Должен признаться, я никогда не сообщал об ошибке ... Причина в том, что это перестало быть проблемой, когда мы начали использовать CMAKE_RUNTIME_OUTPUT_DIRECTORY, и мы смогли протестировать нашу программу прямо из каталога bin SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin), без необходимости позвонить установить. Мы прекратили использование функции OPTIONAL. - person Antonio; 20.10.2014

Как я ответил здесь, последние версии Ninja предоставляют возможность создавать только цели в подкаталоге.

То же самое относится и к установкам. Итак, если ваша цель D из вашего вопроса находилась в подкаталоге D, вы можете запустить эту команду:

ninja D/install

Дополнительные сведения см. в документации CMake к генератору Ninja здесь.

person Unapiedra    schedule 16.07.2017

Если вы использовали add_subdirectory для добавления своих подпроектов в свой CMakeLists.txt, вы увидите, что в вашем каталоге сборки у вас есть подкаталоги для всех ваших подпроектов.

Если вы хотите установить цели только из D, просто выполните:

cd build-dir/D
make install

это установит только D-цели.

Конечно, вы должны собрать свой проект раньше в build-dir. Я использую это так, используя gnu-make. Я не знаю, работает ли это с Ninja.

person Patrick B.    schedule 18.06.2013
comment
Это не установит зависимости: в этом случае будет установлено только D, а не A и B. Кроме того, make не подходит для этого проекта, который содержит тысячи файлов, что делает make крайне медленным. Примечание. Невозможно вызвать ninja из подпапки каталога bin, так как они не содержат файл build.ninja. - person Antonio; 18.06.2013

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

Это хорошо, но вы не можете сделать:

ninja <targetName>/install

Вы можете только сделать

ninja path/where/targetName/is/install

Если вы не знаете, где находится targetName, вы можете использовать:

ninja -t query <targetName>

чтобы увидеть выходы.

person Erk    schedule 18.12.2017