LXF111:CMake
(викификация, оформление, иллюстрация) |
Текущая версия на 07:47, 2 октября 2009
|
|
|
- CMake Кроссплатформенная система сборки для ваших приложений
Содержание |
[править] Раздвигая горизонты
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- ЧАСТЬ 3 CMake – не только мощный, но и расширяемый инструмент для сборки ваших приложений. Андрей Боровский покажет, как добавить ему функциональности.
Сегодня мы завершим знакомство с CMake обзором тех возможностей пакета, которым не нашлось места на предыдущих уроках. Из этой статьи вы узнаете, как писать собственные сценарии расширения CMake, научитесь устанавливать ПО и создавать дистрибутивы (нет, не Linux, но тоже полезные).
[править] Нестандартные связи
Давайте условимся понимать под стандартными библиотеками те, для которых уже имеются готовые модули (сценарии) расширения CMake. Если вы пишете новую библиотеку, вполне логично создать для нее и модуль расширения. Этим мы займемся позже, а сейчас рассмотрим действия, необходимые для подключения библиотеки к проекту. Прежде всего, нам потребуется нестандартная (в указанном выше смысле) библиотека.
Структурно проект библиотеки мало чем отличается от проекта программы. Фактически, вся разница сводится к однойединственной команде. На прилагаемом диске вы найдете проект библиотеки demolib, которая экспортирует функцию testfunc(). Последняя, в свою очередь, распечатывает на экране сообщение о своем вызове (вряд ли в природе существует более простая библиотека). Исходный текст нас сейчас не интересует, так что перейдем сразу к файлу CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(demolib C)
add_library(demolib SHARED demolib.c demolib.h)
if(${CMAKE_SYSTEM_NAME} STREQUAL Windows)
set(LIB_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/lib/)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
set(LIB_INSTALL_PATH /usr/lib/)
endif()
install(TARGETS demolib DESTINATION ${LIB_INSTALL_PATH})
find_path(LIB_INCLUDE_PATH string.h)
install(FILES demolib.h DESTINATION ${LIB_INCLUDE_PATH})
Значительная часть команд в этом файле предназначена для обеспечения кроссплатформенности. За обычным заголовком метапроекта CMake следует команда add_library(). Как нетрудно догадаться, она представляет собой аналог уже знакомой нам команды add_executable(), только в качестве цели сборки выступает не исполняемый файл программы, а библиотека. Первым аргументом команды add_library() должно быть ее имя (которое по совместительству является именем соответствующей цели сборки). Имя указывается в кроссплатформенном виде (без префикса lib и расширения so). Далее следует тип создаваемой библиотеки (SHARED – разделяемая, STATIC – статическая, MODULE – динамически загружаемый разделяемый модуль [на большинстве систем эквивалентен SHARED, – прим. ред.]). Затем, как и в команде add_executable(), мы указываем список файлов исходных текстов, необходимых для сборки цели.
Конструкция if(${CMAKE_SYSTEM_NAME} STREQUAL XXX) позволяет определить, выполняется ли сценарий CMake на платформе XXX или на какой-либо другой (LXF110). Допустимо использовать и более краткую запись: if(WIN32), if(UNIX). Переменная UNIX обозначает все Unix-системы, но поскольку Solaris – это все же не HP-UX и не AIX, я предпочитаю более конкретный вариант с проверкой CMAKE_SYSTEM_NAME.
В приведенном выше примере мета-проекта задействована еще одна возможность CMake, с которой мы ранее не встречались – установка собранной цели. CMake предоставляет несколько команд, с помощью которых можно добавить в создаваемый проект средства инсталляции ПО. Самой полезной из них является install(), принимающая три группы аргументов: спецификатор, определяющий, что именно мы устанавливаем, список имен объектов и целевую директорию. Команда install(), вызванная со спецификатором TARGETS, устанавливает файлы, являющиеся целями (т.е. результатом сборки). В качестве аргументов ей передаются имя цели и каталог, в который должны быть помещены файлы. Чтобы сгенерировать инструкции установки файла, не являющегося целью сборки (например, demolib.h), используется команда install() со спецификатором FILES. Есть и другие опции: можно, например, указать, какую из конфигураций сборки (RELEASE, DEBUG и т.д.) следует использовать для инсталляции (если вы думаете, что никому не понадобится устанавливать DEBUG-проект, то ошибаетесь: многие библиотеки, модули расширения, да и программы можно отлаживать только после полной установки). В команде install() можно также указывать права доступа для устанавливаемого файла. Спецификатор SCRIPTS команды install() позволяет выполнять сценарии CMake до и после установки. Это может оказаться полезным в тех случаях, когда для корректной инсталляции необходимо не только скопировать файл, но и выполнить некоторые дополнительные действия – запустить утилиты, настроить файлы конфигурации, добавить записи в реестр (ой, о чем это я?..).
Корректная установка файлов возможна только при правильном выборе целевых директорий. В Linux и других *nix проблем обычно не возникает (таким образом, мы еще раз убеждаемся в технической рациональности идеи файловой системы с единым корнем). На платформе Windows все гораздо сложнее. Мало того, что разные важные директории (точнее – каталоги) могут быть расположены на разных дисках; в Windows вообще не существует единых правил относительно установки библиотек и разделяемых файлов. Например, популярные динамические библиотеки копируются в каталоги %WINDIR%/system/, %WINDIR%/system32/ и т.п., однако это касается только DLL; lib-файлы, необходимые для компоновки программ с разделяемыми библиотеками, устанавливаются в директории сред разработки. В то же время, команда install() по умолчанию копирует DLL- и LIB-файлы в один каталог.
В нашем мета-проекте мы сохраняем полное имя директории для установки библиотеки в переменной LIB_INSTALL_PATH. На платформе Windows мы записываем сюда значение ${CMAKE_INSTALL_PREFIX}/lib/ (которое разрешается, например, в C:\Program Files\demolib\lib), а под Linux используем жестко заданное /usr/lib/. Обратите внимание на важную особенность работы install(): если указанная в этой команде директория не существует, она будет создана. С одной стороны, это удобно, с другой – в случае опечатки в системе могут появиться странные каталоги. В ходе моих экспериментов с Windows и Linux были случайно созданы директории C:\usr\local\lib и /usr/lib/;/.
Для определения имени директории, в которую следует установить заголовочный файл, мы пользуемся довольно распространенным при работе в CMake методом интроспекции (LXF110): с помощью команды find_path() определяем каталог, в котором располагается какой-либо общераспространенный файл того же типа (в нашем примере – string.h), и устанавливаем demiolib.h в него же. В Linux искомой директорией почти непременно окажется /usr/include/ (можно было бы и не напрягаться), а вот при работе под Windows все будет сложнее. Путь к директории заголовочных файлов зависит от того, какое средство разработки мы используем и где оно установлено. Замечено, что под Windows данный метод интроспекции не всегда может корректно определить требуемый каталог с первого раза – нужно выйти из программы CMake GUI и запустить ее снова. Альтернативный вариант – указать расположение заголовочного файла вручную в окне CMake GUI (само наличие такой опции является признанием того, что интроспекция под Windows работает хуже, чем хотелось бы).
Как изменится сгенерированный проект от того, что в метапроект была добавлена команда install()? На каждой ОС это будет выглядеть по-своему. Если целевой платформой является Linux, в make-файл добавляется цель install, так что установить нашу библиотеку можно командой
sudo make install
При работе под Windows (среда Microsoft Visual C++) в решение (Solution) Visual Studio добавляется специальный проект INSTALL. Выглядит это несколько неуклюже, но другого универсального способа установки проектов под Windows, по-видимому, пока что не существует.
[править] Создание модуля CMake
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Если разделяемую библиотеку планируется использовать во многих
проектах, целесообразно написать для нее собственный модуль, в
котором будет выполняться поиск связанных с библиотекой файлов. Мы проделаем все это для библиотеки demolib (хотя, честно
говоря, ее широкое распространение не предвидится). О том, что
именно делают модули расширений CMake, говорилось в LXF110,
поэтому перейдем сразу к начинке (файл FindDemoLib.cmake):
include(FindPackageHandleStandardArgs)
if(DEMOLIB_INCLUDE_DIR AND DEMOLIB_LIBRARIES)
set(DemoLib_FIND_QUIETLY TRUE)
endif(DEMOLIB_INCLUDE_DIR AND DEMOLIB_LIBRARIES)
find_path(DEMOLIB_INCLUDE_HINT string.h)
find_path(DEMOLIB_INCLUDE_DIR demolib.h HINTS ${DEMOLIB_INCLUDE_HINT})
find_library(DEMOLIB_LIBRARIES demolib HINTS $ENV{PROGRAMFILES}/demolib/lib/ /usr/lib)
find_package_handle_standard_args(DemoLib DEFAULT_MSG DEMOLIB_LIBRARIES DEMOLIB_INCLUDE_DIR)
mark_as_advanced(DEMOLIB_LIBRARIES)
Минимум, что должен делать модуль загрузки разделяемой библиотеки XXX – это записывать в переменные XXX_INCLUDE_DIR и XXX_LIBRARIES пути к заголовочным файлам и самой библиотеке, соответственно. Кроме того, должны быть инициализированы служебные переменные, например, XXX_FOUND. Вдобавок модуль может предоставлять специальные команды, дополнительные пере- менные и многое другое; но в нашем примере мы ограничимся малым. Модуль FindDemoLib записывает путь к библиотеке demolib в переменную DEMOLIB_LIBRARIES, а путь к файлу demolib.h – в переменную DEMOLIB_INCLUDE_DIR.
В первой строчке FindDemoLib.cmake мы загружаем модуль FindPackageHandleStandardArgs, который содержит полезную вспомогательную команду. Далее мы проверяем, не установлены ли уже значения переменных DEMOLIB_INCLUDE_DIR и DEMOLIB_LIBRARIES. Если обе переменные инициализированы, значит, они уже присутствуют в кэше (LXF110). Если кэш обновлять не нужно, мы присваиваем значение TRUE переменной DemoLib_FIND_QUIETLY.
Обратите внимание на префикс DemoLib, который соответствует основе имени файла модуля. В процессе обработки модуля система CMake проверяет значение этой и еще нескольких подобных служебных переменных. Дальше мы выполняем ту самую интроспекцию, ради которой все и затевалось. В команде find_path() используется новый элемент – спецификатор HINTS. Он позволяет нам делать среде CMake «подсказки», упрощающие поиск файлов. За спецификатором HINTS обычно следует список директорий, в которых может находиться (а может и не находиться) искомый файл. Если он не будет найден в «подсказанных» директориях, система выполнит стандартный поиск. Спецификатор HINTS не следует путать со спецификатором PATHS, с помощью которого мы можем жестко указать список директорий для поиска. Отметим, что даже с подсказкой система не всегда может найти директорию заголовочных файлов на платформе Windows. В этом случае придется вводить имя директории вручную в окне графической утилиты CMake.
Команда find_package_handle_standard_args(), предоставляемая загруженным модулем, выполняет рутинные действия по инициализации служебных переменных. Ее первый аргумент – основа имени модуля, которая используется, например, для генерации имен. Второй аргумент определяет, что именно программа должна сказать в том случае, если библиотека demolib не будет найдена. Вместо значения DEFAULT_MSG можно указать свой собственный текст. Далее следуют имена переменных, в которых содержаться путь к библиотеке и заголовочным файлам соответственно.
Завершающая команда mark_as_advanced() помечает переменные, переданные ей в качестве аргумента, как «продвинутые» (advanced). Продвинутые переменные обычно не отображаются в окне графического инструмента CMake.
Файл FindDemoLib.cmake следует скопировать в директорию Modules (здесь хранятся расширения CMake). В результате в метапроекте для сборки libtest мы сможем обойтись без интроспекции:
cmake_minimum_required(VERSION 2.6)
project(libtest)
find_package(DemoLib REQUIRED)
include_directories(${DEMOLIB_INCLUDE_DIR})
add_executable(libtest libtest.c)
target_link_libraries(libtest ${DEMOLIB_LIBRARIES})
[править] Подготовка к распространению
Помимо пакета Cmake, компания Kitware выпускает еще несколько полезных утилит, в том числе CPack – средство создания дистрибутивов. CPack входит в состав пакета Cmake, и им можно управлять из сценариев CMake, так что будет уместно рассмотреть его здесь. Чтобы задействовать CPack в сценарии CMake, достаточно подгрузить модуль:
include(CPack)
Если теперь мы запустим утилиту cmake, то в результирующем Make-файле появятся цели package и package_source. Первая предназначена для создания двоичного пакета, вторая – для дистрибутива исходных текстов. Если теперь мы наберем
sudo make package
(эту команду необходимо выполнять от имени суперпользователя), то в результате получим файл сценария оболочки с расширением .sh, а также архивы .tar.gz, tar.Z и tar.bz2. Имена файлов сконструированы из имени проекта, номера версии и названия платформы. Например, для проекта demolib, на примере которого мы изучаем CPack, все они будут называться demolib-0.1.1-Linux. Перечисленные файлы представляют собой двоичные пакеты в разных форматах (по умолчанию CPack создает сразу несколько пакетов). Файл с расширением .sh – это сценарий оболочки с встроенным архивом tar.gz. Если мы запустим его на выполнение, он задаст нам несколько вопросов по поводу согласия с лицензией и путей установки, после чего (если наши ответы его устроят) распакует содержимое встроенного архива в заданную директорию. Файлы с расширениями .tar.* в комментариях не нуждаются.
Если изложенное выше навело вас на мысль, что создавать двоичные пакеты с помощью CPack очень просто, то вы почти правы. На практике, однако, можно столкнуться с некоторыми сложностями. Механизм генерации пакетов CMake-CPack использует инструкции, заданные нами для генерации цели install (установки проекта). Попросту говоря, для создания цели package умолчательное значение переменной CMAKE_INSTALL_PREFIX (напомню, что она содержит путь к корневой директории инсталляции) заменяется на путь к некому временному каталогу. Далее вызывается цель install, в результате чего выполняется «холостая» установка проекта во временную директорию, содержимое которой упаковывается в архивы. Этот факт имеет несколько последствий. Во-первых, вы можете создать двоичный пакет только в том случае, если ваш метапроект содержит инструкции для генерации цели install. Во-вторых, поскольку процесс создания пакета использует подмену значения CMAKE_INSTALL_PREFIX, генерация может пройти успешно лишь в том случае, когда команды install() ее используют. Если вы выполняете нестандартные действия с директориями, будьте готовы к неожиданным проблемам с генерацией пакетов. Чтобы CMake мог задействовать переменную CMAKE_INSTALL_PREFIX, в команде install() следует указывать относительные, а не абсолютные пути (например, lib, а не /usr/lib). Наконец, в-третьих, выполнение процесса установки в ходе генерации пакета может вызвать побочные эффекты в том случае, когда установка включает в себя какие-то действия помимо простого копирования файлов.
Настройка CPack из мета-проектов CMake выполняется с помощью переменных, которые использует модуль CPack. Перечислим
наиболее интересные из них:
- CPACK_BINARY_DEB, CPACK_BINARY_RPM, CPACK_BINARY_STGZ – указывают, нужно ли создавать пакет в формате Debian, RPM или .sh.
- CPACK_BINARY_TGZ, CPACK_BINARY_TZ, CPACK_BINARY_TBZ2 – управляет созданием архивов tar.gz, tar.Z или tar.bz2. Последним четырем переменным по умолчанию присвоено значение ON, первым двум – OFF.
- CPACK_INSTALL_PREFIX – переменная, в которой сохраняется полное имя корневой директории для установки проекта.
- CPACK_PACKAGE_DESCRIPTION_FILE – путь к файлу с развернутым описанием собираемого пакета.
- CPACK_PACKAGE_DESCRIPTION_SUMMARY – краткое описание собираемого пакета.
- CPACK_PACKAGE_FILE_NAME – основа имени файла пакета.
- CPACK_PACKAGE_INSTALL_DIRECTORY – директория, в которую по умолчанию извлекается содержимое пакета.
- CPACK_PACKAGE_VENDOR – имя сборщика пакета.
- CPACK_PACKAGE_VERSION_MAJOR, CPACK_PACKAGE_VERSION_MINOR, CPACK_PACKAGE_VERSION_RELEASE – эти переменные содержат три цифры номера версии распространяемого ПО – старшую, младшую и номер релиза соответственно (используется, в том числе, при конструировании имени файла пакета).
- CPACK_RESOURCE_FILE_LICENSE – путь к файлу с текстом лицензии.
- CPACK_RESOURCE_FILE_README – путь к файлу README.
- CPACK_SYSTEM_NAME – имя системы (используется, в том числе, при конструировании имени файла пакета).
Если результатом сборки является скрипт или пакет RPM, информация из файлов лицензии, README и WELCOME становится его частью. Чтобы изменить настройки CPack, заданные по умолчанию, нужно отредактировать значения соответствующих переменных перед вызовом include(). Например, если мы хотим создать пакет RPM и привести его имя к классическому виду, можно написать:
set(CPACK_BINARY_RPM ON) set(CPACK_SYSTEM_NAME i686) include (CPack)
С учетом всего вышеизложенного вариант сценарий сборки demolib с дополнительной целью package выглядит так:
cmake_minimum_required(VERSION 2.6) project(demolib C) if(UNIX) set(CMAKE_INSTALL_PREFIX /usr) set(CPACK_BINARY_RPM ON) set(CPACK_SYSTEM_NAME i686) endif(UNIX) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Demo Library Project") set(CPACK_PACKAGE_VERSION 1.0.0) include(CPack) add_library(demolib SHARED demolib.c demolib.h) install(TARGETS demolib DESTINATION lib) install(FILES demolib.h DESTINATION include)
На платформе Windows для создания двоичных пакетов можно использовать Nullsoft NSIS (что выходит за рамки этой статьи) и zip (что не очень удобно с точки зрения Windows-пользователя).
Как уже говорилось, с помощью CPack можно создавать не только двоичные пакеты, содержащие собранное ПО, но и дистрибутивы исходных текстов. По умолчанию, вызов
make package_source
приводит к тому, что все содержимое корневой директории проекта и всех ее поддиректорий (в том числе, с двоичными файлами) упаковывается в архив. Более того, поскольку сам файл пакета исходников по умолчанию сохраняется в той же корневой директории, может возникнуть ситуация, при которой упаковщик будет пытаться заархивировать файл сам в себя. Управление настройкой генератора пакетов исходных текстов также выполняется с помощью переменных, имена которых начинаются с префикса CPACK_SOURCE_. Как и в случае с CMake, вы можете узнать много полезного о переменных CPack, ознакомившись с файлами CPackConfig.cmake и CPackSourceConfig.cmake. Некоторые переменные из этих файлов попадают в кэш 'CMake.
Надеюсь, что после всего сказанного о CMake вы придете к тем же выводам, к которым пришел и я – этот пакет не только является средством кроссплатформенной сборки, но и упрощает жизнь программиста, работающего исключительно в Linux. LXF
[править] Подключение библиотеки
Простой библиотеке – простая программа. На диске вы найдете приложение libtests, которое вызывает функцию testfunc() из библиотеки demolib. Само подключение выполняется с помощью уже знакомой нам команды target_link_libraries(). Ниже следует файл CMakeLists.txt для сборки программы libtest.
cmake_minimum_required(VERSION 2.6)
project(libtest)
find_path(DEMOLIB_INCLUDE_DIR demolib.h)
include_directories(${DEMOLIB_INCLUDE_DIR})
add_executable(libtest libtest.c)
if(${CMAKE_SYSTEM_NAME} STREQUAL Windows)
target_link_libraries(libtest $ENV{PROGRAMFILES}/demolib/lib/
demolib.lib)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
target_link_libraries(libtest demolib)
endif()

