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

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

Все генераторы исходного кода предоставляют схожие функциональные возможности системы сборки. Три варианта использования генерации исходного кода, поддерживаемые системой сборки, — это создание привязок 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 в исходном коде:

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

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

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

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

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

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

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


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

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