Всем снова привет. Сегодня я хотел бы продолжить с того места, на котором остановился в первой части этого поста.

В первой части мы увидели, как добавить исполняемый файл и зависимость. Сегодня я более подробно расскажу, как управлять базовыми зависимостями в Meson. А именно, как управлять внутренними и внешними зависимостями. Я ограничу объем сообщения библиотеками, исполняемыми файлами и использованием dependency и declare_dependency.

В этом посте я отвечаю на вопрос о том, как основные цели с зависимостями управляются со стороны пользователя в Meson.

В Meson вы можете иметь следующие основные цели:

  • исполняемый файл
  • shared_library
  • static_library
  • библиотека
  • both_libraries

Все эти цели являются ярлыками для build_target.

Есть и другие цели, такие как run_target и custom_target, но они останутся за рамками этой публикации.

В Meson все эти цели представляют собой достаточно документированные функции, которые должны быть просты в использовании для всех, кто знаком с Python или любым основным языком.

Функции в meson имеют имя, принимают ноль или более позиционных аргументов и необязательные аргументы ключевого слова. Функции возвращают либо void, либо четко определенный объект со своими собственными методами, которые также описаны в справочнике. Забудьте о струнной гимнастике, как это принято в CMake: вы можете объявить цель и получить такой результат с помощью обычного синтаксиса переменных:

startingmeson_lib = library('startingmeson', sources : ['myroutine.cpp', 'myroutine.hpp'])

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

Обратите внимание, что то, что идет справа от аргумента ключевого слова, не аргумент ключевого слова (выделено жирным шрифтом), а обычная функция, даже если у нее такое же имя:

startingmeson2_lib = library('startingmeson', sources : ['myroutine2.cpp'], include_directories : include_directories('include'))

Шаблон «ключевое слово аргумент сопоставления имени функции» распространен в Meson.

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

Целевой объект простой библиотеки предпочитает разделяемые библиотеки, поэтому при этом будет создан .dll в Windows, .so в Linux и .dylib в Mac, если вы не измените параметр библиотеки по умолчанию с помощью -Ddefault_library = static. Вы также можете исправить результат вывода с помощью любой из static_library, shared_library или both_libraries, которые создают только статические, только общие или обе одновременно, соответственно.

Цели сборки имеют множество аргументов ключевых слов, таких как include_directories, cpp_args, link_with и dependencies, которые задокументированы в справочнике. Просто найдите слова static_library, shared_library, build_target и т. Д. Вы также можете найти примеры использования в руководстве.

Создание библиотеки с внешними зависимостями и исполняемого файла, потребляющего внутреннюю библиотеку.

Теперь давайте создадим небольшой проект, который будет состоять из библиотеки и исполняемого файла. Это будет намеренно использовать рекурсивные файлы meson.build. Вы можете адаптировать это для использования одного meson.build на верхнем уровне, если вы так склонны. В любом случае, не думайте об этом как о рекурсивном make: даже если файлы meson.build рекурсивны, все вещи будут сгенерированы одновременно во время настройки в одном файле build.ninja, что делает рассуждения таких эссе, как Рекурсивное создание, считаться вредными »Спорный.

Проект будет иметь следующую структуру:

startingmeson2/
   meson.build
   src/
      mylib/
        meson.build
        mylib.cpp
        mylib.hpp
      myexe/
        meson.build
        startingmeson2.cpp

Проект имеет следующие характеристики:

  • mylib использует Boost.Filesystem как частную зависимость
  • mylib использует Boost.Optional как общедоступную зависимость (просто для демонстрации транзитивной зависимости, передаваемой исполняемому файлу)
  • myexe зависит от mylib

В Meson есть разница между библиотекой и зависимостью, и она явная. В CMake, когда вы создаете библиотеку, вы будете использовать их атрибуты PRIVATE, INTERFACE и PUBLIC для транзитивной пересылки зависимостей между целями. В Meson, если у вас есть библиотека и в ней есть некоторые include_directories, эти include_directories не будут унаследованы. Правильный способ перенаправить другую библиотеку в Meson со всеми ее зависимостями - использовать declare_dependency, как мы увидим в примерах. Вы можете рассматривать библиотеки как нечто внутреннее в Meson, а не то, что вы обычно хотите использовать (хотя их можно использовать, и есть некоторые варианты использования), даже внутри проекта.

Кроме того, зависимости в Meson очень гибкие: независимо от того, были ли они заполнены с помощью специального обнаружения модуля (например, это происходит с Boost или Qt), через pkg-config (наиболее стандартный способ, но не обязательно) или что вы нашли его с помощью компилятора .find_library (…) выполняет собственные обнаружения и добавляет некоторые включения, используя mydep = declare_dependency (…). Кроме того, начиная с версии 0.49, в Meson есть функция обнаружения пакетов CMake. С точки зрения потребителя, вы просто передаете зависимость аргументу dependencies, и он просто работает.

«Правильный» способ найти зависимости в Meson - это использовать зависимость как можно дольше и возвращаться к другим методам, если они вам нужны. Для внутренних библиотек с некоторыми добавляемыми флагами, включениями и т. Д. Используйте declare_dependency.

Meson "из коробки" поддерживает pkg-config для решения зависимостей, и наиболее распространенные модули, которые не работают с pkg-config, такие как Google Test, Qt, Wxwidgets и Boost, уже были решены за вас. Вы можете посмотреть список поддерживаемых модулей.

Пачкаем руки

Здесь мы собираемся реализовать описанный выше проект (для ленивых в конце статьи есть ссылка на репозиторий Github). Сначала создадим структуру:

mkdir startingmeson2
cd startingmeson2
mkdir -p src/mylib
mkdir -p src/myexe
touch meson.build src/mylib/meson.build src/myexe/meson.build src/myexe/startingmeson2.cpp src/mylib/mylib.hpp src/mylib/mylib.cpp

А теперь по частям:

  1. мы создадим библиотеку и скомпилируем ее как проект
  2. мы удалим объявление проекта, когда проверим, что он компилируется и работает.
  3. мы склеим все вместе с верхним уровнем meson.build с помощью subdir, когда все будет сделано.
  4. создадим исполняемый проект

Пачкаем руки: компиляция библиотеки

Вот как должен выглядеть ваш src / mylib / meson.build, поскольку у нас есть библиотека как автономный проект (шаг 1):

project('mylib', 'cpp', default_options : ['cpp_std=c++14'],
        version : '0.1')
boost_dep = dependency('boost')
boost_filesystem_dep = dependency('boost', modules : ['filesystem', 'system'])
mylib = library('mylib', sources : ['mylib.hpp', 'mylib.cpp'],
                dependencies : [boost_dep, boost_filesystem_dep])
mylib_dep = declare_dependency(dependencies: [boost_dep], include_directories : include_directories('.'), link_with : mylib)

Если у вас не установлен Boost в стандартном каталоге или вы хотите найти альтернативную версию Boost, передайте переменную среды BOOST_ROOT с верхним уровнем Boost, где ваши библиотеки include / и lib / находятся во время настройки, и все будет работать. Есть более тонкие способы сделать это, если я не ошибаюсь, с BOOST_INCLUDEDIR и BOOST_LIBRARYDIR, если это то, что вам нужно.

По сути, мы ищем зависимости Boost и пересылаем их в mylib для использования. Поскольку зависимости являются внешними, мы должны передать эти зависимости аргументу ключевого слова dependencies. Не передавайте его в аргумент ключевого слова link_with библиотечной функции. Это зарезервировано только для внутренних целей, и я думаю, что в любом случае это не сработает.

Обратите внимание на объявление declare_dependency. Вот как вы пересылаете зависимости между целями в Meson от других целей: вы сами объявляете зависимость с помощью включений и т. Д., Которые будут перенаправлены другим целям. Использование mylib напрямую с аргументом link_with и добавление некоторых включений в вашу исполняемую цель для поиска заголовков mylib неверно. Объявление declare_dependency делает использование mylib прозрачным для пользователей, не беспокоясь о том, откуда берутся включения или что связать или определить: это то, для чего нужна declare_dependency, чтобы скрыть все предположения. Исполняемый файл просто должен будет напрямую использовать зависимость, и все должно работать. Как видите, declare_dependency пересылает boost_dep (для Boost.Optional), но никогда частную часть boost_filesystem_dep, которая используется только для внутренних целей.

Вот как выглядит ваш src / mylib / mylib.hpp:

#ifndef MYLIB_API_HPP_
#define MYLIB_API_HPP_
#include <boost/optional.hpp>
#include <string>
boost::optional<std::string> myLibApi();
#endif

Теперь для src / mylib / mylib.cpp:

#include "mylib.hpp"
#include <boost/filesystem.hpp>
boost::optional<std::string> myLibApi() {
    return boost::filesystem::current_path().string();
}

Теперь просто зайдите в startmeson2 / src / mylib для небольшого теста:

mkdir build
meson build
ninja -C build

Все должно работать нормально. Теперь вы можете удалить объявление своего проекта из проекта библиотеки, поскольку это больше не будет отдельным проектом.

Пачкаем руки: используем нашу библиотеку в исполняемом файле и склеиваем все вместе

Давайте склеим все вместе. Отредактируйте файл meson.build верхнего уровня: объявите проект и скажите meson выполнить рекурсию:

project('startingmeson2', 'cpp', default_options : ['cpp_std=c++14'],
        version : '0.1')
subdir('src/mylib')
subdir('src/myexe')

Теперь давайте создадим наш исполняемый код в src / myexe / startmeson2.cpp:

#include "mylib.hpp"
#include <iostream>
int main() {
    std::cout << myLibApi().value() << std::endl;
}

Теперь объявите свой исполняемый файл и используйте зависимость в исполняемом файле в src / myexe / meson.build:

executable('startingmeson2', sources : ['startingmeson2.cpp'],
          dependencies : mylib_dep)

А теперь попробуем проект. Перейдите в корень вашего проекта:

mkdir build
meson build
ninja -C build

И это все! Теперь у вас есть исполняемый файл startmeson2 (или startmeson2.exe в Windows, если это то, что вы использовали) в build / src / myexe. Кстати, в Meson есть возможность сгладить результаты и поместить их на верхний уровень, если вложенные артефакты вас раздражают: просто введите meson configure build и найдите вариант самостоятельно :)

Как видите, Meson может получать доступ к переменным из других подкаталогов обычным способом: поэтому вы можете использовать mylib_dep непосредственно в своем исполняемом файле. Это верно для всего проекта. Вы также можете использовать переменные подпроекта (это не то же самое, что функция subdir, которую мы использовали здесь). Ваш исполняемый файл просто должен быть связан с mylib_dep, и все решается внутри Meson.

Вывод

Использование Meson совсем несложно: возвращаемые объекты - это обычные объекты, которые мы используем в большинстве языков программирования. Результаты имеют тип, который не может быть несовместим при передаче вещей, условные выражения следуют обычным соглашениям, поддерживает создание любого вида артефактов, который может вам понадобиться для ваших нужд сборки, управляет транзитивными зависимостями, настройка rpath как во время сборки, так и во время установки, может контролировать, где ваши вещи установлен, имеет прямую поддержку кросс-компиляции, DESTDIR для сопровождающих пакетов и многое, многое другое.

Это лишь малая часть того, что можно сделать: в следующей части серии я покажу, как вы можете установить уже созданные цели.

Вы можете найти все учебники в репозитории, который я настроил для вас, чтобы вы проверили, если чувствуете себя супер ленивым.

Вы можете найти следующую часть этой серии здесь.

Спасибо за чтение!