매크로

이 페이지에서는 매크로 사용의 기본사항을 다루며 일반적인 사용 사례, 디버깅, 규칙을 포함합니다.

매크로는 규칙을 인스턴스화할 수 있는 BUILD 파일에서 호출되는 함수입니다. 매크로는 주로 기존 규칙 및 기타 매크로의 캡슐화 및 코드 재사용에 사용됩니다.

매크로는 이 페이지에 설명된 기호 매크로와 기존 매크로의 두 가지 버전으로 제공됩니다. 가능하면 코드의 명확성을 위해 기호 매크로를 사용하는 것이 좋습니다.

기호 매크로는 유형이 지정된 인수 (매크로가 호출된 위치를 기준으로 문자열을 라벨로 변환)와 생성된 타겟의 공개 상태를 제한하고 지정하는 기능을 제공합니다. 이는 지연 평가 (향후 Bazel 출시에서 추가될 예정)를 수용할 수 있도록 설계되었습니다. 기호 매크로는 Bazel 8에서 기본적으로 사용할 수 있습니다. 이 문서에서 macros를 언급하는 경우 기호 매크로를 의미합니다.

사용

매크로는 필수 매개변수 attrsimplementation를 사용하여 macro() 함수를 호출하여 .bzl 파일에 정의됩니다.

속성

attrs는 매크로의 인수를 나타내는 속성 이름과 속성 유형의 사전을 허용합니다. 두 가지 공통 속성(namevisibility)은 모든 매크로에 암시적으로 추가되며 attrs에 전달된 사전에 포함되지 않습니다.

# macro/macro.bzl
my_macro = macro(
    attrs = {
        "deps": attr.label_list(mandatory = True, doc = "The dependencies passed to the inner cc_binary and cc_test targets"),
        "create_test": attr.bool(default = False, configurable = False, doc = "If true, creates a test target"),
    },
    implementation = _my_macro_impl,
)

속성 유형 선언은 매개변수 mandatory, default, doc를 허용합니다. 대부분의 속성 유형은 속성이 select를 허용하는지 여부를 결정하는 configurable 매개변수도 허용합니다. 속성이 configurable인 경우 select가 아닌 값을 구성할 수 없는 select로 파싱합니다. "foo"select({"//conditions:default": "foo"})이 됩니다. selects에서 자세히 알아보세요.

속성 상속

중요: 속성 상속은 --experimental_enable_macro_inherit_attrs 플래그로 사용 설정되는 실험용 기능입니다. 이 섹션에 설명된 일부 동작은 기능이 기본적으로 사용 설정되기 전에 변경될 수 있습니다.

매크로는 규칙 (또는 다른 매크로)을 래핑하기 위한 경우가 많으며, 매크로 작성자는 래핑된 기호의 대부분의 속성을 변경하지 않고 **kwargs를 사용하여 매크로의 기본 대상 (또는 기본 내부 매크로)에 전달하려고 합니다.

이 패턴을 지원하기 위해 매크로는 macro()inherit_attrs 인수에 규칙 또는 매크로 기호를 전달하여 규칙 또는 다른 매크로에서 속성을 상속할 수 있습니다. 규칙이나 매크로 기호 대신 특수 문자열 "common"을 사용하여 모든 Starlark 빌드 규칙에 정의된 공통 속성을 상속할 수도 있습니다. 공개 속성만 상속되며 매크로 자체 attrs 사전의 속성은 동일한 이름의 상속된 속성을 재정의합니다. attrs 사전에서 None를 값으로 사용하여 상속된 속성을 삭제할 수도 있습니다.

# macro/macro.bzl
my_macro = macro(
    inherit_attrs = native.cc_library,
    attrs = {
        # override native.cc_library's `local_defines` attribute
        local_defines = attr.string_list(default = ["FOO"]),
        # do not inherit native.cc_library's `defines` attribute
        defines = None,
    },
    ...
)

필수가 아닌 상속된 속성의 기본값은 원래 속성 정의의 기본값과 관계없이 항상 None로 재정의됩니다. 상속된 비필수 속성을 검사하거나 수정해야 하는 경우(예: 상속된 tags 속성에 태그를 추가하려는 경우) 매크로의 구현 함수에서 None 사례를 처리해야 합니다.

# macro/macro.bzl
_my_macro_implementation(name, visibility, tags, **kwargs):
    # Append a tag; tags attr is an inherited non-mandatory attribute, and
    # therefore is None unless explicitly set by the caller of our macro.
    my_tags = (tags or []) + ["another_tag"]
    native.cc_library(
        ...
        tags = my_tags,
        **kwargs,
    )
    ...

구현

implementation는 매크로의 로직이 포함된 함수를 허용합니다. 구현 함수는 하나 이상의 규칙을 호출하여 타겟을 만드는 경우가 많으며 일반적으로 비공개입니다 (이름 앞에 밑줄이 표시됨). 일반적으로 매크로와 동일한 이름을 사용하지만 앞에 _이, 뒤에 _impl이 붙습니다.

속성에 대한 참조가 포함된 단일 인수 (ctx)를 사용하는 규칙 구현 함수와 달리 매크로 구현 함수는 각 인수의 매개변수를 허용합니다.

# macro/macro.bzl
def _my_macro_impl(name, visibility, deps, create_test):
    cc_library(
        name = name + "_cc_lib",
        deps = deps,
    )

    if create_test:
        cc_test(
            name = name + "_test",
            srcs = ["my_test.cc"],
            deps = deps,
        )

매크로가 속성을 상속하는 경우 구현 함수에는 상속된 규칙 또는 하위 매크로를 호출하는 호출로 전달할 수 있는 **kwargs 잔여 키워드 매개변수가 있어야 합니다. 이렇게 하면 상속하는 규칙 또는 매크로에 새 속성이 추가되어도 매크로가 손상되지 않습니다.

선언

매크로는 BUILD 파일에서 정의를 로드하고 호출하여 선언됩니다.


# pkg/BUILD

my_macro(
    name = "macro_instance",
    deps = ["src.cc"] + select(
        {
            "//config_setting:special": ["special_source.cc"],
            "//conditions:default": [],
        },
    ),
    create_tests = True,
)

그러면 //pkg:macro_instance_cc_lib//pkg:macro_instance_test 타겟이 생성됩니다.

규칙 호출과 마찬가지로 매크로 호출의 속성 값이 None로 설정되면 해당 속성은 매크로 호출자가 생략한 것처럼 취급됩니다. 예를 들어 다음 두 매크로 호출은 동일합니다.

# pkg/BUILD
my_macro(name = "abc", srcs = ["src.cc"], deps = None)
my_macro(name = "abc", srcs = ["src.cc"])

이는 일반적으로 BUILD 파일에서는 유용하지 않지만 매크로를 프로그래매틱 방식으로 다른 매크로 내에 래핑할 때는 유용합니다.

세부정보

생성된 타겟의 이름 지정 규칙

기호 매크로로 만든 타겟 또는 하위 매크로의 이름은 매크로의 name 매개변수와 일치하거나 name 뒤에 _ (권장), . 또는 -가 오는 프리픽스를 사용해야 합니다. 예를 들어 my_macro(name = "foo")foo라는 이름의 파일이나 타겟만 만들거나 foo_, foo- 또는 foo. 접두사가 있는 파일이나 타겟(예: foo_bar)만 만들 수 있습니다.

매크로 이름 지정 규칙을 위반하는 타겟 또는 파일은 선언할 수 있지만 빌드할 수 없으며 종속 항목으로 사용할 수 없습니다.

매크로 인스턴스와 동일한 패키지 내에 있는 매크로가 아닌 파일과 타겟은 잠재적인 매크로 타겟 이름과 충돌하는 이름을 가져서는 안 됩니다. 단, 이 배타성은 시행되지 않습니다. Google에서는 이름 지정 스키마를 위반하는 패키지에서 성능이 저하되는 기호 매크로의 성능 개선으로 지연 평가를 구현하는 중입니다.

제한사항

기호 매크로에는 기존 매크로에 비해 몇 가지 추가 제한사항이 있습니다.

기호 매크로

  • name 인수와 visibility 인수를 사용해야 합니다.
  • implementation 함수가 있어야 합니다.
  • 값을 반환하지 않을 수 있습니다.
  • 인수를 변경하지 않을 수 있습니다.
  • 특수 finalizer 매크로가 아닌 한 native.existing_rules()를 호출하지 않을 수 있습니다.
  • native.package()를 호출하지 않을 수 있음
  • glob()를 호출하지 않을 수 있음
  • native.environment_group()를 호출하지 않을 수 있음
  • 이름이 이름 지정 스키마를 준수하는 타겟을 만들어야 합니다.
  • 선언되지 않았거나 인수로 전달되지 않은 입력 파일을 참조할 수 없습니다(자세한 내용은 공개 상태 및 매크로 참고).

공개 상태 및 매크로

Bazel의 공개 상태에 관한 자세한 내용은 공개 상태를 참고하세요.

타겟 공개 상태

기본적으로 기호 매크로로 생성된 타겟은 매크로를 정의하는 .bzl 파일이 포함된 패키지에서만 볼 수 있습니다. 특히 호출자가 매크로의 .bzl 파일과 동일한 패키지에 있지 않는 한 기호 매크로의 호출자에게는 표시되지 않습니다.

기호 매크로의 호출자에게 타겟을 표시하려면 visibility = visibility를 규칙 또는 내부 매크로에 전달합니다. 더 넓은 공개 범위 또는 공개 범위를 지정하여 추가 패키지에 타겟을 표시할 수도 있습니다.

패키지의 기본 공개 상태(package()에 선언됨)는 기본적으로 가장 바깥쪽 매크로의 visibility 매개변수에 전달되지만, 그 후에는 매크로가 그 visibility를 인스턴스화하는 타겟에 전달할지 여부를 결정합니다.

종속 항목 공개 상태

매크로의 구현에서 참조되는 타겟은 해당 매크로의 정의에 표시되어야 합니다. 공개 상태는 다음 방법 중 하나로 지정할 수 있습니다.

  • 타겟은 라벨, 라벨 목록 또는 라벨 키 또는 값 사전 속성을 통해 매크로에 명시적으로 전달되는 경우 매크로에 표시됩니다.

# pkg/BUILD
my_macro(... deps = ["//other_package:my_tool"] )
  • 또는 속성 기본값으로 지정할 수 있습니다.
# my_macro:macro.bzl
my_macro = macro(
  attrs = {"deps" : attr.label_list(default = ["//other_package:my_tool"])},
  ...
)
  • 매크로를 정의하는 .bzl 파일이 포함된 패키지에 표시되도록 선언된 경우에도 매크로에 타겟이 표시됩니다.
# other_package/BUILD
# Any macro defined in a .bzl file in //my_macro package can use this tool.
cc_binary(
    name = "my_tool",
    visibility = "//my_macro:\\__pkg__",
)

선택

속성이 configurable (기본값)이고 값이 None이 아닌 경우 매크로 구현 함수에는 속성 값이 사소한 select로 래핑된 것으로 표시됩니다. 이렇게 하면 매크로 작성자가 속성 값이 select일 수 있다고 예상하지 못한 버그를 더 쉽게 포착할 수 있습니다.

예를 들어 다음 매크로를 고려해 보세요.

my_macro = macro(
    attrs = {"deps": attr.label_list()},  # configurable unless specified otherwise
    implementation = _my_macro_impl,
)

my_macrodeps = ["//a"]로 호출되면 _my_macro_impldeps 매개변수가 select({"//conditions:default": ["//a"]})로 설정된 상태로 호출됩니다. 이로 인해 구현 함수가 실패하면 (예: 코드가 select에 허용되지 않는 deps[0]와 같이 값에 색인을 생성하려고 시도함) 매크로 작성자는 select와 호환되는 작업만 사용하도록 매크로를 다시 작성하거나 속성을 구성 불가능한 것으로 표시 (attr.label_list(configurable = False))할 수 있습니다. 후자의 경우 사용자가 select 값을 전달할 수 없습니다.

규칙 타겟은 이 변환을 역으로 적용하고 사소한 select를 무조건 값으로 저장합니다. 위 예에서 _my_macro_impl가 규칙 타겟 my_rule(..., deps = deps)를 선언하면 해당 규칙 타겟의 deps["//a"]로 저장됩니다. 이렇게 하면 select 래핑으로 인해 매크로로 인스턴스화된 모든 타겟에 사소한 select 값이 저장되지 않습니다.

구성 가능한 속성의 값이 None이면 select로 래핑되지 않습니다. 이렇게 하면 my_attr == None와 같은 테스트가 계속 작동하고 속성이 계산된 기본값이 있는 규칙으로 전달될 때 규칙이 올바르게 동작합니다 (즉, 속성이 전달되지 않은 것처럼). 속성이 항상 None 값을 가질 수 있는 것은 아니지만 attr.label() 유형과 상속된 비필수 속성의 경우 가능합니다.

종료자

규칙 최종 처리기는 BUILD 파일의 문법적 위치와 관계없이 모든 최종 처리기가 아닌 타겟이 정의된 후 패키지 로드의 마지막 단계에서 평가되는 특수한 기호 매크로입니다. 일반 기호 매크로와 달리 종료자는 native.existing_rules()를 호출할 수 있으며, 여기서 종료자는 기존 매크로와 약간 다르게 작동합니다. 종료자가 아닌 규칙 타겟 집합만 반환합니다. finalizer는 해당 세트의 상태를 어설션하거나 새 타겟을 정의할 수 있습니다.

finalizer를 선언하려면 finalizer = True를 사용하여 macro()를 호출합니다.

def _my_finalizer_impl(name, visibility, tags_filter):
    for r in native.existing_rules().values():
        for tag in r.get("tags", []):
            if tag in tags_filter:
                my_test(
                    name = name + "_" + r["name"] + "_finalizer_test",
                    deps = [r["name"]],
                    data = r["srcs"],
                    ...
                )
                continue

my_finalizer = macro(
    attrs = {"tags_filter": attr.string_list(configurable = False)},
    implementation = _impl,
    finalizer = True,
)

게으름

중요: 지연된 매크로 확장 및 평가를 구현하는 중입니다. 이 기능은 아직 사용할 수 없습니다.

현재 모든 매크로는 BUILD 파일이 로드되는 즉시 평가되므로 비용이 많이 들고 관련 없는 매크로가 있는 패키지의 타겟 성능에 부정적인 영향을 미칠 수 있습니다. 향후 종료자가 아닌 기호 매크로는 빌드에 필요한 경우에만 평가됩니다. 접두사 이름 지정 스키마는 요청된 타겟을 기준으로 Bazel에서 확장할 매크로를 결정하는 데 도움이 됩니다.

이전 문제 해결

다음은 일반적인 이전 문제와 해결 방법입니다.

  • 기존 매크로 호출 glob()

glob() 호출을 BUILD 파일 (또는 BUILD 파일에서 호출된 기존 매크로)로 이동하고 label-list 속성을 사용하여 glob() 값을 기호 매크로에 전달합니다.

# BUILD file
my_macro(
    ...,
    deps = glob(...),
)
  • 기존 매크로에 유효한 Starlark attr 유형이 아닌 매개변수가 있습니다.

중첩된 기호 매크로로 최대한 많은 로직을 가져오되 최상위 매크로는 기존 매크로로 유지합니다.

  • 기존 매크로가 이름 지정 스키마를 위반하는 타겟을 만드는 규칙을 호출함

괜찮습니다. '오해의 소지가 있는' 타겟에 의존하지 마세요. 이름 지정 검사는 자동으로 무시됩니다.