Допустим, у меня есть проект, состоящий из нескольких подпроектов 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 следует понять, что НЕОБЯЗАТЕЛЬНЫЙ должен быть указан как последний вариант.