소스 생성기

이 페이지에서는 생성된 소스가 지원되는 방식과 빌드 시스템에서 소스를 사용하는 방법을 개략적으로 설명합니다.

모든 소스 생성기는 비슷한 빌드 시스템 기능을 제공합니다. 빌드 시스템에서 지원되는 세 가지 소스 생성 사용 사례는 bindgen, AIDL 인터페이스 및 protobuf 인터페이스를 사용하여 C 바인딩을 생성하는 것입니다.

생성된 소스를 크레이트로 사용

소스 코드를 생성하는 모든 Rust 모듈은 모듈이 rust_library로서 정의된 경우와 똑같은 방식으로 크레이트로 사용할 수 있습니다. (즉, rustlibs, rlibs, dylibs 속성에서 종속 항목으로 정의할 수 있습니다.) 플랫폼 코드의 가장 좋은 사용 패턴은 생성된 소스를 크레이트로 사용하는 것입니다. include! 매크로는 생성된 소스에서 지원되지만, 주된 목적은 external/에 있는 타사 코드를 지원하는 것입니다.

플랫폼 코드가 include!() 매크로를 통해 생성된 소스를 사용하는 사례도 있습니다(예: genrule 모듈을 사용하여 고유한 방식으로 소스를 생성하는 경우).

생성된 소스를 include!()를 사용하여 포함

생성된 소스를 크레이트로 사용하는 방법은 각 모듈 페이지에 예로 나와 있습니다. 이 섹션에서는 생성된 소스를 include!() 매크로를 통해 참조하는 방법을 보여줍니다. 이 프로세스는 모든 소스 생성기에서 비슷합니다.

사전 요구 사항

이 예에서는 rust_bindgen 모듈(libbuzz_bindgen)을 정의했고 include!() 매크로를 사용하기 위해 생성된 소스를 포함하는 방법으로 넘어갈 준비가 되었다고 가정합니다. 준비가 되지 않았다면 Rust bindgen 모듈 정의로 이동하여 libbuzz_bindgen을 만든 후 다시 돌아오세요.

이 예의 build-file 부분은 모든 소스 생성기에 적용됩니다.

생성된 소스를 포함하는 방법

다음 콘텐츠로 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) }
}

생성된 소스를 크레이트로 사용하는 이유

rustc는 C/C++ 컴파일러와 달리 바이너리 또는 라이브러리의 진입점을 나타내는 단일 소스 파일만 허용합니다. 소스 트리는 필요한 모든 소스 파일을 자동으로 검색할 수 있도록 구성되어 있어야 합니다. 즉, 생성된 소스는 소스 트리에 배치하거나 소스에서 include 지시어를 통해 제공해야 합니다.

include!("/path/to/hello.rs");

Rust 커뮤니티는 이 차이를 해결하기 위해 build.rs 스크립트와 Cargo 빌드 환경에 관한 가정을 사용합니다. cargo 명령어는 빌드 시점에 OUT_DIR 환경 변수를 설정하는데, 여기에 build.rs 스크립트가 생성된 소스 코드를 배치해야 합니다. 다음 명령어를 사용하여 소스 코드를 포함합니다.

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

위 명령어의 경우 각 모듈의 출력이 자체 out/ 디렉터리1에 배치되므로 이는 Soong에 문제가 됩니다. 종속 항목이 생성된 소스를 출력하는 단일 OUT_DIR은 없습니다.

플랫폼 코드의 경우 AOSP는 생성된 소스를 가져오기가 가능한 크레이트로 패키징하는 것을 선호하는데, 그 이유는 다음과 같습니다.

  • 생성된 소스 파일 이름의 충돌을 방지합니다.
  • 트리에 유지 관리가 필요한 상용구 코드가 체크인되는 양을 줄입니다. 생성된 소스가 크레이트로 컴파일되도록 하는 모든 상용구는 중앙에서 유지 관리가 가능합니다.
  • 생성된 코드와 관련 크레이트 간의 암시적2 상호작용을 방지합니다.
  • 흔히 사용되는 생성된 코드를 동적으로 링크하여 메모리와 디스크의 부담을 줄입니다.

결과적으로, Android의 모든 Rust 소스 생성 모듈 유형은 크레이트로서 컴파일되고 사용될 수 있는 코드를 생성합니다. Soong은 모듈의 모든 생성된 소스 종속 항목이 Cargo에서처럼 모듈당 하나의 디렉터리로 복사된 경우에는 여전히 타사 크레이트를 수정 없이 지원합니다. 이 경우 Soong은 모듈을 컴파일할 때 생성된 소스를 찾을 수 있도록 OUT_DIR 환경 변수를 이 디렉터리로 설정합니다. 그러나 위에서 설명한 이유로 인해, 플랫폼 코드에서는 반드시 필요한 경우에만 이 메커니즘을 사용하는 것이 좋습니다.


  1. C/C++와 기타 유사 언어에서는 생성된 소스의 경로가 컴파일러에 직접 제공되므로 이러한 문제가 발생하지 않습니다. 

  2. include!는 텍스트 입력 방식의 포함을 사용하기 때문에 둘러싸고 있는 네임스페이스의 값을 참조하거나 네임스페이스를 변경하거나 #![foo]와 같은 구성을 사용하게 될 수 있습니다. 이와 같은 암시적 상호작용은 유지 관리하기 어려울 수 있습니다. 크레이트와의 상호작용이 반드시 필요한 경우에만 매크로를 사용하세요.