На этой странице представлен общий обзор того, как поддерживается сгенерированный исходный код и как его можно использовать в системе сборки.
Все генераторы исходного кода предоставляют схожие функциональные возможности системы сборки. Три варианта использования генерации исходного кода, поддерживаемые системой сборки, — это создание привязок 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
в этот каталог при компиляции модуля, чтобы можно было найти сгенерированный исходный код. Однако по уже описанным причинам рекомендуется использовать этот механизм в коде платформы только тогда, когда это абсолютно необходимо.
Это не представляет никаких проблем для C/C++ и подобных языков, так как путь к сгенерированному исходному коду предоставляется непосредственно компилятору. ↩
С
include!
работает путем текстового включения, он может ссылаться на значения из окружающего пространства имен, изменять пространство имен или использовать такие конструкции, как#![foo]
. Эти неявные взаимодействия может быть трудно поддерживать. Всегда предпочитайте макросы, когда действительно требуется взаимодействие с остальной частью ящика. ↩