빌드 시스템은 rust_bindgen 모듈 유형을 통해 bindgen 바인딩의 생성을 지원합니다. Bindgen은 C 라이브러리에 Rust FFI 바인딩을 제공합니다(제한적인 C++ 지원 포함, cppstd 속성을 설정해야 함).

기본적인 rust_bindgen 사용법

다음은 bindgen을 사용하는 모듈을 정의하는 방법과 모듈을 크레이트로 사용하는 방법을 보여주는 예입니다. 외부 코드의 경우와 같이 bindgen 바인딩을 include!() 매크로를 통해 사용해야 하는 경우 소스 생성기 페이지를 참고하세요.

Rust에서 호출할 C 라이브러리 예

다음은 Rust에서 사용할 수 있도록 구조체와 함수를 정의하는 C 라이브러리의 예입니다.

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

rust_bindgen 모듈 정의

모든 관련 헤더를 포함하는 래퍼 헤더 external/rust/libbuzz/libbuzz_wrapper.h를 정의합니다.

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

Android.bp 파일을 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"],
}

bindgen 플래그를 사용하는 방법을 자세히 알아보려면 생성된 바인딩 맞춤설정에 관한 bindgen 설명서 섹션을 참고하세요.

이 섹션을 참고하여 include!() 매크로를 사용하기 위한 사전 요구 사항으로서 rust_bindgen 모듈을 정의했다면 소스 생성기 페이지의 사전 요구 사항으로 돌아가세요. 그 외의 경우에는 다음 섹션으로 넘어가세요.

바인딩을 크레이트로 사용

다음 콘텐츠로 external/rust/hello_bindgen/Android.bp를 만듭니다.

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

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

다음 콘텐츠로 external/rust/hello_bindgen/src/main.rs를 만듭니다.

//! 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) }
}

마지막으로, m hello_bindgen을 호출하여 바이너리를 빌드합니다.

Bindgen 바인딩 테스트

Bindgen 바인딩은 메모리 레이아웃 불일치를 방지하기 위해 일반적으로 몇 개의 생성된 레이아웃 테스트를 포함합니다. AOSP에서는 이러한 테스트용으로 테스트 모듈을 정의하고 프로젝트의 정규 테스트 모음의 일부로 이 테스트를 실행할 것을 권장합니다.

테스트 바이너리는 external/rust/hello_bindgen/Android.bprust_test 모듈을 정의하여 쉽게 생성할 수 있습니다.

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",
}

공개 상태 및 연결

생성된 바인딩은 유형 정의, 함수 서명, 관련 상수로 이루어지기 때문에 일반적으로 매우 작습니다. 따라서 일반적으로 이러한 라이브러리를 동적으로 연결하는 것은 낭비가 됩니다. rustlibs를 통해 이러한 모듈을 사용하면 자동으로 정적 변형이 선택되도록 모듈의 동적 연결이 사용 중지되었습니다.

기본적으로rust_bindgen 모듈에는 [":__subpackages__"]visibility 속성이 있습니다. 이 속성은 동일한 Android.bp 파일에 있는 모듈과 디렉터리 계층에서 그 아래에 있는 모듈만 볼 수 있습니다. 여기에는 두 가지 목적이 있습니다.

  • 트리의 다른 곳에서 원시 C 바인딩을 사용할 수 없게 됩니다.
  • 정적 링크와 동적 연결이 혼합된 다이아몬드 연결 문제가 발생하지 않습니다.

일반적으로, 다른 개발자가 사용할 수 있는 바인딩과 동일한 디렉터리 트리에서 생성된 모듈 앞뒤에 안전한 래퍼 라이브러리를 제공해야 합니다. 이 방식이 자신의 사용 사례에 맞지 않는다면 공개 상태에 패키지를 추가할 수 있습니다. 공개 범위를 추가할 때는 나중에 동일한 프로세스에 연결될 수 있는 두 범위는 추가하지 마세요. 이렇게 하면 연결이 실패할 수 있습니다.

주목할 만한 rust_bindgen 속성

모든 모듈에 적용되는 주요 공통 속성 외에도 아래에 정의된 속성이 있습니다. 이들은 Rust bindgen 모듈에서 특히 중요한 속성이거나 rust_bindgen 모듈 유형의 고유한 동작을 나타내는 속성입니다.

stem, name, crate_name

rust_bindgen은 라이브러리 변형을 생성하므로 이들 속성에도 rust_library 모듈의 stem, name, crate_name 속성에 적용되는 동일한 요구사항이 적용됩니다. 자세한 내용은 주목할 만한 Rust 라이브러리 속성을 참고하세요.

wrapper_src

이러한 바인딩에 필요한 헤더를 포함하는 래퍼 헤더 파일의 상대 경로입니다. 파일 확장자는 헤더를 해석하는 방법과 기본적으로 사용할 -std 플래그를 결정합니다. 확장자가 .hh 또는 .hpp가 아닌 이상 C 헤더라고 가정됩니다. C++ 헤더에 다른 확장자를 사용해야 하는 경우, cpp_std 속성을 설정하여 이 파일이 C 파일이라고 가정하는 기본 동작을 재정의하세요.

source_stem

생성된 소스 파일의 파일 이름입니다. stem 속성은 생성된 라이브러리 변형의 출력 파일 이름만 제어하므로, 바인딩을 크레이트로 사용하는 경우에도 반드시 정의해야 합니다. 모듈에서 rustlibs를 통해 크레이트를 사용하는 대신 여러 소스 생성기(예: bindgenprotobuf)에 소스가 종속된 경우, 이 모듈의 종속 항목인 모든 소스 생성기가 고유한 source_stem 값을 가져야 합니다. 종속 모듈은 srcs에 정의된 모든 SourceProvider 종속 항목에서 공통 OUT_DIR 디렉터리로 소스를 복사하므로, source_stem에서 충돌이 발생하면 생성된 소스 파일이 OUT_DIR 디렉터리에서 덮어쓰이게 됩니다.

c_std

사용할 C 기본 버전을 나타내는 문자열입니다. 유효한 값은 다음과 같습니다.

  • 특정 버전(예: "gnu11")
  • "experimental"(빌드 시스템이 build/soong/cc/config/global.go에 정의한 값). 해당하는 경우 C++1z와 같은 초안 버전을 사용할 수 있습니다.
  • 값을 설정하지 않거나 ""(빌드 시스템 기본값을 사용해야 함)

이 속성을 설정하면 파일 확장자가 무시되고 헤더가 C 헤더라고 가정됩니다. 이 속성은 cpp_std와 동시에 설정할 수 없습니다.

cpp_std

cpp_std는 사용할 C 기본 버전을 나타내는 문자열입니다. 유효한 값은 다음과 같습니다.

  • 특정 버전(예: "gnu++11")
  • "experimental"(빌드 시스템이 build/soong/cc/config/global.go에 정의한 값). 해당하는 경우 C++1z와 같은 초안 버전을 사용할 수 있습니다.
  • 값을 설정하지 않거나 ""(빌드 시스템 기본값을 사용해야 함)

이 속성을 설정하면 파일 확장자가 무시되고 헤더가 C++ 헤더라고 가정됩니다. 이 속성은 c_std와 동시에 설정할 수 없습니다.

cflags

cflags는 헤더를 올바르게 해석하는 데 필요한 Clang 플래그로 구성된 문자열 목록을 제공합니다.

custom_bindgen

고급 사용 사례에서는 bindgen을 라이브러리로 사용하여 맞춤 Rust 바이너리의 일부로 조작할 수 있는 API를 제공할 수 있습니다. custom_bindgen 필드는 일반적인 bindgen 바이너리 대신 bindgen API를 사용하는 rust_binary_host 모듈의 모듈 이름을 받습니다.

이 맞춤 바이너리는 bindgen과 비슷한 방식으로 인수를 받습니다.

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

인수의 대부분은 bindgen 라이브러리 자체에서 처리됩니다. 이 사용법의 예를 보려면 external/rust/crates/libsqlite3-sys/android/build.rs를 참고하세요.

또한, 라이브러리 속성 전체를 사용하여 라이브러리 컴파일을 제어할 수 있습니다. 단, 속성 전체를 정의하거나 변경해야 하는 경우는 드뭅니다.