building system/bazel

dependency attribute란?

opencpp 2024. 1. 27. 12:41
반응형

개요

이번 게시글에서는 이전 게시글 예제를 기반으로 dependency attribute 의미를 알아보자. 의존성이 설정된 파일이 변경되면, 의존하고 있는 target는 다시 빌드한다. 파일이 변경된 여부는 파일 생성 시점이 아닌 파일의 해쉬 데이터으로 확인한다.

make_md5_binary.bzl 파일 다시 보기

make_md5_binary.bzl 코딩은 아래와 같다. 참고로 _ 로 시작한 이름은 private 접근 권한을 갖는다.

# make_md5_binary.bzl
def _make_md5_binary_impl(ctx):
    ctx.actions.run_shell(
        outputs = [ctx.outputs.out],
        inputs = [ctx.file.src],
        command = "md5sum {} > {}".format(ctx.file.src.path, ctx.outputs.out.path),
    )
    return DefaultInfo(files = depset([ctx.outputs.out]))

make_md5_binary = rule(
    implementation = _make_md5_binary_impl,
    attrs = {
        "src": attr.label(mandatory = True, allow_single_file = True),
        "out": attr.output(),
    },
)

여기에서 주목해야 할 부분은 일단 다음 코딩이다. src 속성은 attr.label(또는 attr.label_list, label_keyed_string_dict ,output,output_list)로 생성되는데, 의존성을 부여할 때 label 키워드를 사용한다. 이러한 이유로 att.label,attr.label_list, label_keyed_string_dict, output,output_list에 대해 dependency attribute 라 부른다. 이들 attr 함수는 dependency과 생성한다.

label 자체는 의존성 객체에 부여한 변수 이름 정도로 이해하면 되겠다. Label 객체에 문자열 인자를 전달하면, 문자열로부터 Label를 추적하는 label constructor가 호출된다. label 객체는 target 타입으로 변환할 수 있다. 실제 rule 정의 구문에서 label로 정의되지만 implemenation function의 대응 ctx.attr로 맵핑될 때, target으로 변환되어 전달된다. target 타입을 통해 target의 provider를 접근할 수 있다.

attr는 top level module 중 하나이고, attr의 각 함수가 하나 attribute 스카마를 표현한 객체를 반환하는데, 실제 rule이나 aspect 객체에서 사용할 때, attr를 맵구조로 정의한 attrs를 통해 사용한다.

bazel 기반으로 새로운 컴파일러 지원할 때, label에 컴파일러 실행 파일을 설정해주고, 전용 rule, 예를 들어 cc_binary 내부에서 label를 암묵적으로 설정함으로 구현한다. 즉 tool 역시 일반전인 의존 파일처럼 취급한다.

# make_md5_binary.bzl
        "src": attr.label(mandatory = True, allow_single_file = True),

따라서 아래 코딩의 "main.cc" 파일에 test 타겟이 의존한다. 따라서 main.cc가 변경되면 test 타겟도 다시 빌드한다.

#BUILD.bazel
load(":make_md5_binary.bzl","make_md5_binary")

make_md5_binary(
    name ="test",
    src = "main.cc",
    out ="test.md5"
)

다음으로 살펴 볼 코딩은 다음과 같다.
make_m5_binary rule를 분석하면, 반환값으로 의존할 수 있는 의존 집합을 반환한다.

# make_md5_binary.bzl
        return DefaultInfo(files = depset([ctx.outputs.out]))

따라서 아래의 test가 다른 곳에서 의존하는 대상으로 사용되면, make_md5_binary 결과물이 변경되면, 의존하는 대상도 다시 빌드한다.

#BUILD.bazel
load(":make_md5_binary.bzl","make_md5_binary")

make_md5_binary(
    name ="test",
    src = "main.cc",
    out ="test.md5"
)

결론

결국 bazel에서 어떤 대상을 새로 빌드해야 하는데 여부는 의존성 그래프를 통해 추적된다. 의존하는 대상은 파일이 변경된 여부를 해시 데이터로 캐쉬한다. 예를 들어 make_md5_binary.bzl 파일이 BUILD.bazel 파일에 사용되면 make_md5_binary.bzl에 BUILD.bazel 파일이 의존하고, make_md5_binary.bzl의 일부 데이터를 변경하면 BUILD.bazel도 다시 분석한다.

의존성 그래프는 파일 집합에 의존한다. 오해하지 말아야 하는 이유는 폴더에 의존성을 설정할 수 없다. 따라서 include directory를 컴파일 과정에서 전파하기 위해서, 의존성 그래프가 아닌, target의 속성 정보를 전파하는 개별 코딩을 작성해줌으로써 실제 구현한다..

반응형