O sistema de build possibilita gerar vinculações bindgen usando o tipo de módulo rust_bindgen. A bindgen fornece vinculações de FFI do Rust às bibliotecas C com suporte para código C++ limitado, o que exige a definição da propriedade cppstd.

Uso básico do rust_bindgen

Veja abaixo um exemplo de como definir um módulo que usa a bindgen e aprenda a usar esse módulo como uma caixa. Se você precisar usar vinculações bindgen com uma macro include!(), como para código externo, consulte a página Geradores de origem.

Exemplo de uma biblioteca C para chamadas do Rust

Confira um exemplo de biblioteca C que define uma estrutura e uma função para uso com o Rust abaixo.

external/rust/libbuzz/libbuzz.h

typedef struct foo {
    int x;
} foo;

void fizz(int i, foo* cs);

external/rust/libbuzz/libbuzz.c

#include <stdio.h>
#include "libbuzz.h"

void fizz(int i, foo* my_foo){
    printf("hello from c! i = %i, my_foo->x = %i\n", i, my_foo->x);
}

Definir um módulo rust_bindgen

Defina um cabeçalho do wrapper external/rust/libbuzz/libbuzz_wrapper.h, que inclui todos os cabeçalhos relevantes:

// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"

Defina o arquivo Android.bp como external/rust/libbuzz/Android.bp:

cc_library {
    name: "libbuzz",
    srcs: ["libbuzz.c"],
}

rust_bindgen {
     name: "libbuzz_bindgen",

     // Crate name that's used to generate the rust_library variants.
     crate_name: "buzz_bindgen",

     // Path to the wrapper source file.
     wrapper_src: "libbuzz_wrapper.h",

     // 'source_stem' controls the output filename.
     // This is the filename that's used in an include! macro.
     //
     // In this case, we just use "bindings", which produces
     // "bindings.rs".
     source_stem: "bindings",

     // Bindgen-specific flags and options to customize the bindings.
     // See the bindgen manual for more information.
     bindgen_flags: ["--verbose"],

     // Clang flags to be used when generating the bindings.
     cflags: ["-DSOME_FLAG"],

     // Shared, static, and header libraries which export the necessary
     // include directories must be specified.
     //
     // These libraries will also be included in the crate if static,
     // or propagated to dependents if shared.
     // static_libs: ["libbuzz"]
     // header_libs: ["libbuzz"]
     shared_libs: ["libbuzz"],
}

Para saber mais sobre o uso de sinalizações bindgen, consulte a seção do manual da bindgen em Como personalizar vinculações geradas (link em inglês).

Se você usou esta seção para definir um módulo rust_bindgen como pré-requisito para usar a macro include!(), volte para a seção Pré-requisito na página de Geradores de origem. Caso contrário, prossiga para as próximas seções.

Usar vinculações como uma caixa

Crie external/rust/hello_bindgen/Android.bp com este conteúdo:

rust_binary {
   name: "hello_bindgen",
   srcs: ["main.rs"],

   // Add the rust_bindgen module as if it were a rust_library dependency.
   rustlibs: ["libbuzz_bindgen"],
}

Crie external/rust/hello_bindgen/src/main.rs com este conteúdo:

//! Example crate for testing bindgen bindings

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

Por fim, chame m hello_bindgen para criar o binário.

Testar vinculações bindgen

As vinculações bindgen geralmente contêm vários testes de layout gerados para evitar incompatibilidades de layout na memória. O AOSP recomenda que você defina um módulo para esses testes e que eles sejam executados como parte do pacote normal do projeto.

Um binário para esses testes pode ser facilmente produzido definindo um módulo rust_test em external/rust/hello_bindgen/Android.bp:

rust_test {
    name: "bindings_test",
    srcs: [
        ":libbuzz_bindgen",
    ],
    crate_name: "buzz_bindings_test",
    test_suites: ["general-tests"],
    auto_gen_config: true,

    // Be sure to disable lints as the generated source
    // is not guaranteed to be lint-free.
    clippy_lints: "none",
    lints: "none",
}

Visibilidade e vinculação

As vinculações geradas tendem a ser muito pequenas, já que consistem em definições de tipo, assinaturas de funções e constantes relacionadas. Como resultado, não vale a pena vincular essas bibliotecas dinamicamente. Desativamos a vinculação dinâmica para esses módulos. Assim, o uso com rustlibs vai selecionar automaticamente uma variante estática.

Por padrão, os módulos rust_bindgen têm uma propriedade visibility de [":__subpackages__"], que só vai permitir módulos no mesmo arquivo Android.bp ou abaixo dele na hierarquia do diretório. Isso tem dois propósitos:

  • Evitar o uso de vinculações C brutas em outros lugares da árvore.
  • Evitar problemas de vinculação de diamantes com uma combinação de vinculações estáticas e dinâmicas.

Normalmente, você precisa fornecer uma biblioteca de wrapper segura em torno do módulo gerado que foi adicionado na mesma árvore de diretórios das vinculações, destinadas ao uso por outros desenvolvedores. Se isso não funcionar no seu caso de uso, adicione outros pacotes à visibilidade. Ao adicionar outros escopos de visibilidade, tome cuidado para não adicionar dois escopos que podem ser vinculados ao mesmo processo no futuro, porque isso pode causar uma falha.

Propriedades rust_bindgen importantes

As propriedades definidas abaixo são adicionadas a propriedades comuns importantes que se aplicam a todos os módulos. Elas são particularmente importantes para os módulos bindgen do Rust ou têm um comportamento único específico para o tipo de módulo rust_bindgen.

stem, name, crate_name

A rust_bindgen produz variantes de biblioteca. Portanto, elas compartilham os mesmos requisitos com os módulos rust_library para as propriedades stem, name e crate_name. Consulte as propriedades Rust importantes da biblioteca para referência.

wrapper_src

Esse é o caminho relativo para um arquivo de cabeçalho do wrapper que inclui os cabeçalhos necessários para essas vinculações. A extensão de arquivo determina como interpretar o cabeçalho e qual sinalização -std usar por padrão. Ela é considerada um cabeçalho C, a menos que a extensão seja .hh ou .hpp. Se o cabeçalho C++ precisar de outra extensão, defina a propriedade cpp_std para substituir o comportamento padrão que pressupõe que o arquivo é um arquivo C.

source_stem

Esse é o nome do arquivo de origem gerado. Esse campo precisa ser definido, mesmo se você estiver usando as vinculações como uma caixa, já que a propriedade stem controla apenas o nome do arquivo de saída das variantes de biblioteca geradas. Se um módulo depende de vários geradores de origem (por exemplo, bindgen e protobuf) como origem, e não como caixas usando rustlibs, você precisa garantir que todos os geradores de origem que são dependências desse módulo tenham valores source_stem exclusivos. Módulos dependentes copiam origens de todas as dependências SourceProvider (definidas em srcs) para um diretório OUT_DIR comum. Portanto, as colisões em source_stem resultariam nos arquivos da origem gerados sendo substituídos no diretório OUT_DIR.

c_std

Essa é uma string que representa a versão padrão do C que será usada. Veja abaixo os valores válidos:

  • Uma versão específica, como "gnu11".
  • "experimental", que é um valor definido pelo sistema de compilação em build/soong/cc/config/global.go, pode usar versões de rascunho, como C++1z, quando disponíveis.
  • Valor não definido ou "", o que indica que o padrão do sistema de compilação vai ser usado.

Se um valor não for definido, a extensão de arquivo vai ser ignorada e o cabeçalho será considerado como C. Isso não pode ser definido ao mesmo tempo que cpp_std.

cpp_std

cpp_std é uma string que representa qual versão padrão do C será usada. Valores válidos:

  • Uma versão específica, como "gnu++11".
  • "experimental", que é um valor definido pelo sistema de compilação em build/soong/cc/config/global.go, pode usar versões de rascunho, como C++1z, quando disponíveis.
  • Valor não definido ou "", o que indica que o padrão do sistema de compilação vai ser usado.

Se um desses valores for definido, a extensão de arquivo vai ser ignorada e o cabeçalho será considerado como C++. Isso não pode ser definido ao mesmo tempo que c_std.

cflags

cflags fornece uma lista de strings de sinalizações do Clang necessárias para interpretar corretamente os cabeçalhos.

custom_bindgen

Para casos de uso avançados, a bindgen pode ser usada como uma biblioteca, fornecendo uma API que pode ser manipulada como parte de um binário personalizado do Rust. O campo custom_bindgen recebe o nome de um módulo rust_binary_host, que usa a API bindgen em vez do binário bindgen normal.

Esse binário personalizado precisa esperar argumentos de forma parecida à bindgen, como

$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]

A maior parte desse processo ocorre na própria biblioteca bindgen. Para conferir um exemplo desse uso, acesse external/rust/crates/libsqlite3-sys/android/build.rs.

Além disso, o conjunto completo de propriedades de biblioteca está disponível para controlar a compilação, embora elas raramente precisem ser definidas ou mudadas.

handle_static_inline e static_inline_library

Essas duas propriedades precisam ser usadas juntas e permitem a produção de wrappers para funções inline estáticas, que podem ser incluídas nas vinculações bindgen exportadas.

Configure handle_static_inline: true para usar essas propriedades e static_inline_library para uma cc_library_static correspondente, que define o módulo rust_bindgen como entrada de origem.

Exemplo de uso:

    rust_bindgen {
        name: "libbindgen",
        wrapper_src: "src/any.h",
        crate_name: "bindgen",
        stem: "libbindgen",
        source_stem: "bindings",

        // Produce bindings for static inline fucntions
        handle_static_inline: true,
        static_inline_library: "libbindgen_staticfns"
    }

    cc_library_static {
        name: "libbindgen_staticfns",

        // This is the rust_bindgen module defined above
        srcs: [":libbindgen"],
        include_dirs: ["src/"],
    }