O sistema de compilação suporta a geração de ligações bindgen por meio do tipo de módulo rust_bindgen . Bindgen fornece ligações Rust FFI para bibliotecas C (com algum suporte limitado a C++, que requer a configuração da propriedade cppstd ).

Uso básico de Rust_bindgen

O que se segue é um exemplo de como definir um módulo que usa bindgen e como usar esse módulo como uma caixa. Se você precisar usar vinculações bindgen por meio de uma macro include!() , como para código externo, consulte a página Geradores de código-fonte .

Exemplo de biblioteca C para chamar de Rust

Segue um exemplo de biblioteca C que define uma estrutura e uma função para uso em Rust.

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);
}

Defina um módulo Rust_bindgen

Defina um cabeçalho de 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 como usar sinalizadores bindgen, consulte a seção do manual bindgen em Personalizando ligações geradas .

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

Use encadernações como caixa

Crie external/rust/hello_bindgen/Android.bp com o seguinte 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 o seguinte 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) }
}

Finalmente, chame m hello_bindgen para construir o binário.

Testar ligações do Bindgen

As ligações Bindgen normalmente contêm vários testes de layout gerados para evitar incompatibilidades de layout de memória. AOSP recomenda que você tenha um módulo de teste definido para esses testes e que os testes sejam executados como parte do conjunto de testes normal do seu projeto.

Um binário de teste para estes 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 ligação

As ligações geradas são geralmente muito pequenas, pois consistem em definições de tipo, assinaturas de função e constantes relacionadas. Como resultado, geralmente é um desperdício vincular essas bibliotecas de forma dinâmica. Desativamos a ligação dinâmica para esses módulos para que usá-los por meio de rustlibs selecione automaticamente uma variante estática.

Por padrão, os módulos rust_bindgen têm uma propriedade visibility de [":__subpackages__"] , que permitirá apenas que módulos no mesmo arquivo Android.bp ou aqueles abaixo dele na hierarquia de diretórios o vejam. Isso serve a dois propósitos:

  • Isso desencoraja o uso de ligações C brutas em outras partes da árvore.
  • Evita problemas de ligação em diamante com uma mistura de ligação estática e dinâmica.

Normalmente, você deve fornecer uma biblioteca wrapper segura em torno do módulo gerado que você adicionou na mesma árvore de diretórios que as ligações que devem ser usadas por outros desenvolvedores. Se isso não funcionar para o seu caso de uso, você pode adicionar pacotes adicionais aoibility . Ao adicionar escopos de visibilidade adicionais, tome cuidado para não adicionar dois escopos que possam ser vinculados ao mesmo processo no futuro, pois isso pode falhar na vinculação.

Propriedades notáveis ​​de ferrugem_bindgen

As propriedades definidas abaixo são adicionais às propriedades comuns importantes que se aplicam a todos os módulos. Eles são particularmente importantes para os módulos Rust bindgen ou exibem um comportamento exclusivo específico para o tipo de módulo rust_bindgen .

caule, nome, crate_name

rust_bindgen produz variantes de biblioteca, então elas compartilham os mesmos requisitos com os módulos rust_library para as propriedades stem , name e crate_name . Consulte Propriedades notáveis ​​da biblioteca Rust para referência.

wrapper_src

Este é o caminho relativo para um arquivo de cabeçalho wrapper que inclui os cabeçalhos necessários para essas ligações. A extensão do arquivo determina como interpretar o cabeçalho e qual sinalizador -std usar por padrão. Presume-se que seja um cabeçalho C , a menos que a extensão seja .hh ou .hpp . Se o seu cabeçalho C++ precisar ter alguma outra extensão, defina a propriedade cpp_std para substituir o comportamento padrão que assume que o arquivo é um arquivo C.

origem_stem

Este é o nome do arquivo de origem gerado . Este campo deve ser definido, mesmo se você estiver usando as ligações como uma caixa, pois a propriedade stem controla apenas o nome do arquivo de saída para as variantes da biblioteca geradas. Se um módulo depender de vários geradores de origem (como bindgen e protobuf ) como fonte em vez de como caixas por meio de rustlibs , você deverá garantir que todos os geradores de origem que são dependências desse módulo tenham valores source_stem exclusivos. Módulos dependentes copiam fontes de todas as dependências SourceProvider que são definidas em srcs para um diretório OUT_DIR comum, portanto, colisões em source_stem resultariam na substituição dos arquivos de origem gerados no diretório OUT_DIR .

c_std

Esta é uma string que representa qual versão do padrão C usar. Os valores válidos estão listados abaixo:

  • 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 estiverem disponíveis.
  • Unset ou "" , que indica que o padrão do sistema de compilação deve ser usado.

Se isto for definido, a extensão do arquivo será ignorada e o cabeçalho será considerado um cabeçalho 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 C usar. 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 estiverem disponíveis.
  • Unset ou "" , que indica que o padrão do sistema de compilação deve ser usado.

Se estiver definido, a extensão do arquivo será ignorada e o cabeçalho será considerado um cabeçalho C++. Isso não pode ser definido ao mesmo tempo que c_std .

flags

cflags fornece uma lista de strings de sinalizadores Clang necessários para interpretar corretamente os cabeçalhos.

custom_bindgen

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

Este binário personalizado deve esperar argumentos de maneira semelhante a bindgen , como

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

A maior parte disso é tratada pela própria biblioteca bindgen . Para ver um exemplo desse uso, visite external/rust/crates/libsqlite3-sys/android/build.rs .

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