Генераторы источников

На этой странице представлен общий обзор того, как поддерживается сгенерированный исходный код и как его можно использовать в системе сборки.

Все генераторы исходного кода предоставляют схожую функциональность системы сборки. Три поддерживаемых системой сборки варианта использования генерации исходного кода — это генерация привязок C с помощью bindgen, интерфейсов AIDL и интерфейсов protobuf.

Ящики из сгенерированного исходного кода

Каждый модуль Rust, генерирующий исходный код, может использоваться как крейт, точно так же, как если бы он был определен как rust_library . (Это означает, что его можно определить как зависимость в свойствах rustlibs , rlibs и dylibs .) Наилучший шаблон использования платформенного кода — это использование сгенерированного исходного кода в качестве крейта. Хотя макрос include! поддерживается для сгенерированного исходного кода, его основное назначение — поддержка стороннего кода, находящегося во external/ .

В некоторых случаях код платформы может по-прежнему использовать сгенерированный исходный код через макрос include!() , например, когда вы используете модуль genrule для генерации исходного кода уникальным образом.

Используйте include!() для включения сгенерированного исходного кода.

Использование сгенерированного исходного кода в качестве библиотеки рассматривается в примерах на страницах каждого конкретного модуля. В этом разделе показано, как ссылаться на сгенерированный исходный код с помощью макроса include!() . Обратите внимание, что этот процесс аналогичен для всех генераторов исходного кода.

Предварительное условие

Этот пример основан на предположении, что вы определили модуль rust_bindgen ( libbuzz_bindgen ) и можете перейти к шагам по включению сгенерированного исходного кода для использования макроса include!() . Если вы этого не сделали, перейдите к разделу «Определение модуля rust bindgen» , создайте libbuzz_bindgen , а затем вернитесь сюда.

Обратите внимание, что разделы, касающиеся файлов сборки, применимы ко всем генераторам исходного кода.

Шаги по включению сгенерированного исходного кода

Создайте файл external/rust/hello_bindgen/Android.bp со следующим содержимым:

rust_binary {
   name: "hello_bzip_bindgen_include",
   srcs: [
         // The primary rust source file must come first in this list.
         "src/lib.rs",

         // The module providing the bindgen bindings is
         // included in srcs prepended by ":".
         ":libbuzz_bindgen",
    ],

    // Dependencies need to be redeclared when generated source is used via srcs.
    shared_libs: [
        "libbuzz",
    ],
}

Создайте файл external/rust/hello_bindgen/src/bindings.rs со следующим содержимым:

#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]

// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));

Создайте файл external/rust/hello_bindgen/src/lib.rs со следующим содержимым:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

Зачем нужны ящики для сгенерированного исходного кода?

В отличие от компиляторов C/C++, rustc принимает только один исходный файл, представляющий собой точку входа в бинарный файл или библиотеку. Он ожидает, что дерево исходного кода будет структурировано таким образом, чтобы все необходимые исходные файлы могли быть обнаружены автоматически. Это означает, что сгенерированный исходный код должен быть либо помещен в дерево исходного кода, либо предоставлен через директиву include в файле source:

include!("/path/to/hello.rs");

Сообщество Rust использует скрипты build.rs и предположения о среде сборки Cargo для работы с этим различием . При сборке команда cargo устанавливает переменную среды OUT_DIR , в которую скрипты build.rs должны помещать сгенерированный исходный код. Используйте следующую команду для включения исходного кода:

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

Это создает проблему для Сунга, поскольку выходные данные каждого модуля размещаются в отдельной директории out/ 1 . Нет единой OUT_DIR , куда бы зависимости выводили сгенерированный исходный код.

Что касается кода платформы, AOSP предпочитает упаковывать сгенерированный исходный код в крейт, который можно импортировать, по нескольким причинам:

  • Предотвратите конфликты имен сгенерированных исходных файлов.
  • Сократите количество шаблонного кода, требующего обслуживания и включенного в репозиторий по всему дереву зависимостей. Любой шаблонный код, необходимый для компиляции сгенерированного исходного кода в крейт, может поддерживаться централизованно.
  • Избегайте неявных взаимодействий между сгенерированным кодом и окружающим его крейтом.
  • Снизьте нагрузку на память и дисковое пространство за счет динамического связывания часто используемых сгенерированных исходных файлов.

В результате все типы модулей генерации исходного кода Rust в Android создают код, который можно скомпилировать и использовать в качестве крейта . Soong по-прежнему поддерживает сторонние крейты без изменений, если все сгенерированные зависимости исходного кода для модуля копируются в одну директорию для каждого модуля, аналогично Cargo. В таких случаях Soong устанавливает переменную среды OUT_DIR в эту директорию при компиляции модуля, чтобы сгенерированный исходный код можно было найти. Однако по уже описанным причинам рекомендуется использовать этот механизм в коде платформы только тогда, когда это абсолютно необходимо.


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

  2. Поскольку include! работает по принципу текстового включения, он может ссылаться на значения из окружающего пространства имен, изменять пространство имен или использовать конструкции типа #![foo] . Такие неявные взаимодействия могут быть сложны в поддержке. Всегда отдавайте предпочтение макросам, когда действительно требуется взаимодействие с остальной частью крейта.