Интеграция пользовательских систем сборки C/C++ с помощью Ninja (экспериментальная версия)

Если вы не используете CMake или ndk-build, но хотите полностью интегрировать сборку C/C++ плагина Android Gradle (AGP) и Android Studio, вы можете создать собственную систему сборки C/C++, создав скрипт оболочки, который записывает информацию о сборке в формате файла сборки Ninja .

Экспериментальная поддержка пользовательских систем сборки C/C++ была добавлена ​​в Android Studio и AGP. Эта функция доступна начиная с Android Studio Dolphin | 2021.3.1 Canary 4.

Обзор

Распространенным шаблоном для проектов C/C++, особенно тех, которые нацелены на несколько платформ, является генерация проектов для каждой из этих платформ из некоторого базового представления. Ярким примером этого шаблона является CMake . CMake может генерировать проекты для Android, iOS и других платформ из одного базового представления, сохраненного в файле CMakeLists.txt .

Хотя CMake напрямую поддерживается AGP, существуют и другие генераторы проектов, которые напрямую не поддерживаются:

Эти типы генераторов проектов либо поддерживают Ninja как внутреннее представление сборки C/C++, либо могут быть адаптированы для генерации Ninja как внутреннего представления.

При правильной настройке проект AGP с интегрированным генератором систем проектов C/C++ позволяет пользователям:

  • Сборка из командной строки и Android Studio.

  • Редактируйте исходные тексты с полной поддержкой языковых служб (например, определение перехода) в Android Studio.

  • Используйте отладчики Android Studio для отладки собственных и смешанных процессов.

Как изменить сборку для использования пользовательского скрипта конфигурации сборки C/C++

В этом разделе рассматриваются шаги по использованию пользовательского скрипта конфигурации сборки C/C++ из AGP.

Шаг 1: Измените файл build.gradle на уровне модуля, чтобы он ссылался на скрипт конфигурации.

Чтобы включить поддержку Ninja в AGP, настройте experimentalProperties в файле build.gradle на уровне модуля:

android {
  defaultConfig {
    externalNativeBuild {
      experimentalProperties["ninja.abiFilters"] = [ "x86", "arm64-v8a" ]
      experimentalProperties["ninja.path"] = "source-file-list.txt"
      experimentalProperties["ninja.configure"] = "configure-ninja"
      experimentalProperties["ninja.arguments"] = [
            "\${ndk.moduleMakeFile}",
            "--variant=\${ndk.variantName}",
            "--abi=Android-\${ndk.abi}",
            "--configuration-dir=\${ndk.configurationDir}",
            "--ndk-version=\${ndk.moduleNdkVersion}",
            "--min-sdk-version=\${ndk.minSdkVersion}"
       ]
     }
   }

Свойства интерпретируются AGP следующим образом:

  • ninja.abiFilters — это список ABI для сборки. Допустимые значения: x86 , x86-64 , armeabi-v7a и arm64-v8a .

  • ninja.path — это путь к файлу проекта C/C++. Формат этого файла может быть любым. Изменения в этом файле вызовут запрос на синхронизацию Gradle в Android Studio.

  • ninja.configure — это путь к файлу скрипта, который будет выполнен Gradle, когда необходимо настроить проект C/C++. Проект настраивается при первой сборке, во время синхронизации Gradle в Android Studio или при изменении одного из входов скрипта конфигурации.

  • ninja.arguments — это список аргументов, которые будут переданы скрипту, определенному ninja.configure. Элементы в этом списке могут ссылаться на набор макросов, значения которых зависят от текущего контекста конфигурации в AGP:

    • ${ndk.moduleMakeFile} — это полный путь к файлу ninja.configure . Так что в примере это будет C:\path\to\configure-ninja.bat .

    • ${ndk.variantName} — это имя текущего варианта AGP, который собирается. Например, debug или release.

    • ${ndk.abi} — это имя текущего AGP ABI, который собирается. Например, x86 или arm64-v8a .

    • ${ndk.buildRoot} — это имя папки, сгенерированной AGP, в которую скрипт записывает свой вывод. Подробности будут объяснены в Шаге 2: Создание скрипта конфигурации .

    • ${ndk.ndkVersion} — версия NDK, которая будет использоваться. Обычно это значение, переданное в android.ndkVersion в файле build.gradle , или значение по умолчанию, если его нет.

    • ${ndk.minPlatform} — минимальная целевая платформа Android, запрашиваемая AGP.

  • ninja.targets — это список конкретных целей ниндзя, которые следует построить.

Шаг 2: Создание скрипта настройки

Минимальная ответственность скрипта конфигурации ( configure-ninja.bat в предыдущем примере) — сгенерировать файл build.ninja , который при сборке с помощью Ninja скомпилирует и свяжет все собственные выходные данные проекта. Обычно это файлы .o (Object), .a (Archive) и .so (Shared Object).

Скрипт конфигурации может записать файл build.ninja в два разных места в зависимости от ваших потребностей.

  • Если AGP может выбрать местоположение, то скрипт конфигурации записывает build.ninja в местоположение, указанное в макросе ${ndk.buildRoot} .

  • Если скрипту конфигурации необходимо выбрать местоположение файла build.ninja , то он также записывает файл с именем build.ninja.txt в местоположении, указанном в макросе ${ndk.buildRoot} . Этот файл содержит полный путь к файлу build.ninja , который написал скрипт конфигурации.

Структура файла build.ninja

Как правило, большинство структур, которые точно представляют сборку Android C/C++, будут работать. Ключевые элементы, необходимые AGP и Android Studio:

  • Список исходных файлов C/C++ вместе с флагами, необходимыми Clang для их компиляции.

  • Список выходных библиотек. Обычно это файлы .so (общий объект), но также могут быть .a (архив) или исполняемыми (без расширения).

Если вам нужны примеры того, как сгенерировать файл build.ninja , вы можете посмотреть на вывод CMake при использовании генератора build.ninja .

Вот пример минимального шаблона build.ninja .

rule COMPILE
   command = /path/to/ndk/clang -c $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o

Лучшие практики

В дополнение к требованиям (список исходных файлов и выходных библиотек) ниже приведены некоторые рекомендуемые практики.

Объявляйте именованные выходы с phony правилами

Когда это возможно, рекомендуется, чтобы структура build.ninja использовала phony правила, чтобы дать выводам сборки понятные для человека имена. Так, например, если у вас есть вывод с именем c:/path/to/lib.so , вы можете дать ему понятное для человека имя следующим образом.

build curl: phony /path/to/lib.so

Преимущество этого в том, что вы можете указать это имя как цель сборки в файле build.gradle . Например,

android {
  defaultConfig {
    externalNativeBuild {
      ...
      experimentalProperties["ninja.targets"] = [ "curl" ]

Укажите цель «все»

При указании цели all это будет набор библиотек по умолчанию, созданный AGP, если в файле build.gradle явно не указаны цели.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build foo.o : COMPILE foo.cpp
build bar.o : COMPILE bar.cpp
build libfoo.so : LINK foo.o
build libbar.so : LINK bar.o
build all: phony libfoo.so libbar.so

Укажите альтернативный метод сборки (необязательно)

Более продвинутый вариант использования — обернуть существующую систему сборки, которая не основана на Ninja. В этом случае вам все равно нужно представить все источники с их флагами вместе с библиотеками вывода, чтобы Android Studio могла представить надлежащие функции языковой службы, такие как автозаполнение и определение перехода. Однако вы хотите, чтобы AGP подчинялся базовой системе сборки во время фактической сборки.

Для этого можно использовать вывод сборки Ninja со специальным расширением .passthrough .

В качестве более конкретного примера, предположим, что вы хотите обернуть MSBuild. Ваш скрипт конфигурации сгенерирует build.ninja как обычно, но также добавит цель passthrough, которая определяет, как AGP будет вызывать MSBuild.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

rule MBSUILD_CURL
  command = /path/to/msbuild {flags to build curl with MSBuild}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o
build curl : phony lib.so
build curl.passthrough : MBSUILD_CURL

Дайте отзыв

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

  • Для общего отзыва добавьте комментарий к этой ошибке .

  • Чтобы сообщить об ошибке, откройте Android Studio и нажмите Help > Submit Feedback . Обязательно укажите ссылку "Custom C/C++ Build Systems", чтобы помочь направить ошибку.

  • Чтобы сообщить об ошибке, если у вас не установлена ​​Android Studio, отправьте сообщение об ошибке, используя этот шаблон .