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

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

В Android Studio и AGP добавлена ​​экспериментальная поддержка пользовательских систем сборки C/C++. Эта функция доступна начиная с Android Studio Dolphin | 2021.3.1 Канарейки 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. Например, отладка или выпуск.

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

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

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

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

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

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

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

Сценарий настройки может записать файл 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 как обычно, но он также добавит цель транзитной передачи, которая определяет, как 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 и нажмите «Справка» > «Отправить отзыв» . Обязательно обратитесь к «Пользовательским системам сборки C/C++», чтобы помочь устранить ошибку.

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