На этой странице представлено общее представление о том, как поддерживается сгенерированный исходный код и как его можно использовать в системе сборки.
Все генераторы исходного кода предоставляют схожую функциональность системы сборки. Три варианта использования генерации исходного кода, поддерживаемые системой сборки, — это создание привязок 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"));
Это представляет собой проблему для Сунга, поскольку выходные данные каждого модуля размещаются в своем собственном каталоге out/
1 . Не существует ни одного OUT_DIR
, куда зависимости выводят сгенерированный источник.
Что касается кода платформы, AOSP предпочитает упаковывать сгенерированный исходный код в контейнер, который можно импортировать, по нескольким причинам:
- Предотвратите столкновение имен сгенерированных исходных файлов.
- Уменьшите количество регистрируемых шаблонных кодов по всему дереву, требующих обслуживания. Любой шаблон, необходимый для компиляции сгенерированного исходного кода в контейнер, может обслуживаться централизованно.
- Избегайте неявных 2 взаимодействий между сгенерированным кодом и окружающим контейнером.
- Уменьшите нагрузку на память и диск за счет динамического связывания часто используемых источников.
В результате все типы модулей генерации исходного кода Rust в Android создают код, который можно скомпилировать и использовать в качестве крейта . Soong по-прежнему поддерживает сторонние крейты без изменений, если все сгенерированные исходные зависимости для модуля копируются в один каталог для каждого модуля, аналогично Cargo. В таких случаях Сунг устанавливает переменную среды OUT_DIR
в этот каталог при компиляции модуля, чтобы можно было найти сгенерированный исходный код. Однако по уже описанным причинам рекомендуется использовать этот механизм в коде платформы только тогда, когда это абсолютно необходимо.
Это не представляет никаких проблем для C/C++ и подобных языков, поскольку путь к сгенерированному исходному коду предоставляется непосредственно компилятору. ↩
С
include!
работает путем текстового включения, он может ссылаться на значения из окружающего пространства имен, изменять пространство имен или использовать такие конструкции, как#![foo]
. Эти неявные взаимодействия может быть трудно поддерживать. Всегда отдавайте предпочтение макросам, когда действительно необходимо взаимодействие с остальной частью ящика. ↩