BazelCon 2022는 11월 16~17일에 뉴욕과 온라인에서 개최됩니다.
지금 등록하기

규칙 가이드

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

Starlark는 원래 Bazel에서 사용하기 위해 개발되었으며 이후 다른 도구에 의해 채택된 Python과 유사한 구성 언어입니다. Bazel의 BUILD.bzl 파일은 '빌드 언어'로 알려진 Starlark 방언으로 작성되지만 특히 'Starlark'라고 지칭하는 경우가 많습니다. 특히 기능은 Bazel의 기본 제공 또는 '네이티브' 부분이 아닌 빌드 언어로 표현됩니다. Bazel은 glob, genrule, java_binary 등의 여러 빌드 관련 기능으로 핵심 언어를 강화합니다.

자세한 내용은 BazelStarlark 문서를, 규칙 SIG 템플릿을 새로운 규칙 세트의 시작 지점입니다

빈 규칙

첫 번째 규칙을 만들려면 foo.bzl 파일을 만듭니다.

def _foo_binary_impl(ctx):
    pass

foo_binary = rule(
    implementation = _foo_binary_impl,
)

rule 함수를 호출할 때 콜백 함수를 정의해야 합니다. 로직은 그대로 유지되지만 지금은 함수를 비워 둘 수 있습니다. ctx 인수는 타겟에 관한 정보를 제공합니다.

규칙을 로드하여 BUILD 파일에서 사용할 수 있습니다.

같은 디렉터리에 BUILD 파일을 만듭니다.

load(":foo.bzl", "foo_binary")

foo_binary(name = "bin")

이제 타겟을 빌드할 수 있습니다.

$ bazel build bin
INFO: Analyzed target //:bin (2 packages loaded, 17 targets configured).
INFO: Found 1 target...
Target //:bin up-to-date (nothing to build)

규칙은 아무것도 하지 않지만, 다른 규칙처럼 동작합니다. 즉, 필수 이름이 있지만 visibility, testonly, tags와 같은 일반적인 속성을 지원합니다.

평가 모델

더 진행하기 전에 코드가 평가되는 방식을 이해하는 것이 중요합니다.

foo.bzl을 print 문으로 업데이트합니다.

def _foo_binary_impl(ctx):
    print("analyzing", ctx.label)

foo_binary = rule(
    implementation = _foo_binary_impl,
)

print("bzl file evaluation")

빌드:

load(":foo.bzl", "foo_binary")

print("BUILD file")
foo_binary(name = "bin1")
foo_binary(name = "bin2")

ctx.label은 분석 중인 대상의 라벨에 상응합니다. ctx 객체에는 유용한 필드와 메서드가 많이 있습니다. API 참조에서 전체 목록을 확인할 수 있습니다.

코드를 쿼리합니다.

$ bazel query :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
//:bin2
//:bin1

몇 가지 사항을 살펴봅니다.

  • 'bzl 파일 평가'가 먼저 출력됩니다. BUILD 파일을 평가하기 전에 Bazel은 로드하는 모든 파일을 평가합니다. 여러 개의 BUILD 파일이 foo.bzl을 로드하는 경우 Bazel이 평가 결과를 캐시하기 때문에 'bzl 파일 평가'가 한 번만 발생합니다.
  • 콜백 함수 _foo_binary_impl이 호출되지 않았습니다. Bazel 쿼리는 BUILD 파일을 로드하지만 대상을 분석하지는 않습니다.

대상을 분석하려면 cquery ('구성된 쿼리') 또는 build 명령어를 사용합니다.

$ bazel build :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin1
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin2
INFO: Analyzed 2 targets (0 packages loaded, 0 targets configured).
INFO: Found 2 targets...

보시다시피 이제 _foo_binary_impl가 각 타겟에 대해 한 번씩, 모두 두 번 호출됩니다.

일부 판독기는 'bzl 파일 평가'가 다시 출력되지만 foo.bzl 평가는 bazel query 호출 후에 캐시됩니다. Bazel은 코드를 재평가하지 않으며 인쇄 이벤트만 재생합니다. 캐시 상태와 관계없이 동일한 출력을 얻습니다.

파일 만들기

보다 유용한 규칙을 만들려면 파일을 생성하도록 업데이트하세요. 먼저 파일을 선언하고 이름을 지정합니다. 이 예에서는 타겟과 동일한 이름을 가진 파일을 만듭니다.

ctx.actions.declare_file(ctx.label.name)

지금 bazel build :all을 실행하면 오류가 발생합니다.

The following files have no generating action:
bin2

파일을 선언할 때마다 작업을 생성하여 파일을 생성하는 방법을 Bazel에 알려야 합니다. ctx.actions.write을 사용하여 주어진 내용으로 파일을 만듭니다.

def _foo_binary_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = "Hello\n",
    )

코드는 유효하지만 아무 작업도 하지 않습니다.

$ bazel build bin1
Target //:bin1 up-to-date (nothing to build)

ctx.actions.write 함수는 Bazel에 파일 생성 방법을 학습한 작업을 등록했습니다. 그러나 Bazel은 파일이 실제로 요청될 때까지 파일을 만들지 않습니다. 따라서 마지막으로 할 일은 파일이 규칙 구현 내에서 사용되는 임시 파일이 아니라 규칙의 출력이라는 것을 Bazel에 알립니다.

def _foo_binary_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = "Hello!\n",
    )
    return [DefaultInfo(files = depset([out]))]

나중에 DefaultInfodepset 함수를 살펴봅니다. 지금은 마지막 줄이 규칙의 출력을 선택하는 방법이라고 가정합니다.

이제 Bazel을 실행합니다.

$ bazel build bin1
INFO: Found 1 target...
Target //:bin1 up-to-date:
  bazel-bin/bin1

$ cat bazel-bin/bin1
Hello!

파일을 생성했습니다.

속성

규칙을 더 유용하게 만들려면 attr 모듈을 사용하여 새 속성을 추가하고 규칙 정의를 업데이트하세요.

username라는 문자열 속성을 추가합니다.

foo_binary = rule(
    implementation = _foo_binary_impl,
    attrs = {
        "username": attr.string(),
    },
)

그런 다음 BUILD 파일에서 다음을 설정합니다.

foo_binary(
    name = "bin",
    username = "Alice",
)

콜백 함수의 값에 액세스하려면 ctx.attr.username를 사용합니다. 예:

def _foo_binary_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.write(
        output = out,
        content = "Hello {}!\n".format(ctx.attr.username),
    )
    return [DefaultInfo(files = depset([out]))]

참고로 속성을 필수로 설정하거나 기본값을 설정할 수 있습니다. attr.string 문서를 참고하세요. 부울 또는 정수 목록과 같은 다른 유형의 속성도 사용할 수 있습니다.

종속 항목

종속 항목 속성(예: attr.label, attr.label_list)은 속성을 소유한 대상에서 종속 항목을 선언합니다. 101}라벨이 속성 값에 표시됩니다. 이러한 종류의 속성은 타겟 그래프의 기반이 됩니다.

BUILD 파일에서 타겟 라벨은 문자열 객체(예: //pkg:name)로 표시됩니다. 구현 함수에서 타겟은 Target 객체로 액세스할 수 있습니다. 예를 들어, Target.files를 사용하여 타겟에서 반환한 파일을 확인합니다.

여러 파일

기본적으로 규칙에 의해 생성된 타겟만 종속 항목으로 표시될 수 있습니다 (예: foo_library() 타겟). 속성이 입력 파일(예: 저장소의 소스 파일)인 타겟을 수락하도록 하려면 다음을 사용하면 됩니다.allow_files 허용되는 파일 확장자 목록을 지정하거나True 파일 확장자를 허용합니다.

"srcs": attr.label_list(allow_files = [".java"]),

파일 목록은 ctx.files.<attribute name>에서 확인할 수 있습니다. 예를 들어 srcs 속성의 파일 목록은 다음을 통해 액세스할 수 있습니다.

ctx.files.srcs

단일 파일

파일이 하나만 필요하면 다음과 같이 allow_single_file를 사용합니다.

"src": attr.label(allow_single_file = [".java"])

그러면 ctx.file.<attribute name>에서 이 파일에 액세스할 수 있습니다.

ctx.file.src

템플릿으로 파일 만들기

템플릿을 기반으로 .cc 파일을 생성하는 규칙을 만들 수 있습니다. 또한 ctx.actions.write를 사용하여 규칙 구현 함수에서 구성된 문자열을 출력할 수 있지만, 여기에는 두 가지 문제가 있습니다. 첫째, 템플릿이 커지면 분석 파일을 처리하는 동안 별도의 파일로 저장하고 큰 문자열을 구성하지 않는 것이 메모리 효율성이 높아집니다. 둘째, 별도의 파일을 사용하는 것이 사용자에게 더 편리합니다. 대신 템플릿 파일에서 대체를 실행하는 ctx.actions.expand_template를 사용하세요.

template 속성을 만들어 템플릿 파일의 종속 항목을 선언합니다.

def _hello_world_impl(ctx):
    out = ctx.actions.declare_file(ctx.label.name + ".cc")
    ctx.actions.expand_template(
        output = out,
        template = ctx.file.template,
        substitutions = {"{NAME}": ctx.attr.username},
    )
    return [DefaultInfo(files = depset([out]))]

hello_world = rule(
    implementation = _hello_world_impl,
    attrs = {
        "username": attr.string(default = "unknown person"),
        "template": attr.label(
            allow_single_file = [".cc.tpl"],
            mandatory = True,
        ),
    },
)

사용자는 다음과 같은 규칙을 사용할 수 있습니다.

hello_world(
    name = "hello",
    username = "Alice",
    template = "file.cc.tpl",
)

cc_binary(
    name = "hello_bin",
    srcs = [":hello"],
)

템플릿을 최종 사용자에게 노출하지 않고 항상 동일한 템플릿을 사용하려면 기본값을 설정하고 속성을 비공개로 설정하면 됩니다.

    "_template": attr.label(
        allow_single_file = True,
        default = "file.cc.tpl",
    ),

밑줄로 시작하는 속성은 비공개이며 BUILD 파일에서 설정할 수 없습니다. 템플릿이 이제 암시적 종속 항목이 되었습니다. 모든 hello_world 대상은 이 파일에 종속됩니다. 이 파일은 BUILD 파일을 업데이트하고 exports_files를 사용하여 다른 패키지에 표시하는 것을 잊지 마세요.

exports_files(["file.cc.tpl"])

추가 정보