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

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

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


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

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