레거시 매크로

기존 매크로는 타겟을 만들 수 있는 BUILD 파일에서 호출되는 비구조화 함수입니다. 로드 단계가 끝나면 기존 매크로는 더 이상 존재하지 않으며 Bazel은 인스턴스화된 규칙의 구체적인 집합만 봅니다.

기존 매크로를 사용하지 말고 대신 기호 매크로를 사용해야 하는 이유

가능한 경우 기호 매크로를 사용해야 합니다.

기호 매크로

  • 원격으로 작업 방지
  • 세분화된 공개 범위를 통해 구현 세부정보를 숨길 수 있도록 합니다.
  • 입력된 속성을 사용합니다. 즉, 자동 라벨 및 전환 선택을 의미합니다.
  • 더 읽기 쉬워야 합니다.
  • 지연 평가가 제공됩니다.

사용

매크로의 일반적인 사용 사례는 규칙을 재사용하려는 경우입니다.

예를 들어 BUILD 파일의 genrule은 명령어에 하드코딩된 some_arg 인수와 함께 //:generator를 사용하여 파일을 생성합니다.

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

다른 인수로 더 많은 파일을 생성하려면 이 코드를 매크로 함수로 추출하는 것이 좋습니다. namearg 매개변수가 있는 file_generator라는 매크로를 만들려면 genrule을 다음과 같이 대체하면 됩니다.

load("//path:generator.bzl", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)

file_generator(
    name = "file-two",
    arg = "some_arg_two",
)

file_generator(
    name = "file-three",
    arg = "some_arg_three",
)

여기서는 //path 패키지에 있는 .bzl 파일에서 file_generator 기호를 로드합니다. 매크로 함수 정의를 별도의 .bzl 파일에 배치하면 BUILD 파일을 깔끔하고 선언적으로 유지할 수 있습니다. .bzl 파일은 작업공간의 모든 패키지에서 로드할 수 있습니다.

마지막으로 path/generator.bzl에서 원래 genrule 정의를 캡슐화하고 매개변수화하는 매크로 정의를 작성합니다.

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location //:generator) %s > $@" % arg,
    tools = ["//:generator"],
    visibility = visibility,
  )

매크로를 사용하여 규칙을 연결할 수도 있습니다. 이 예에서는 genrule이 이전 genrule의 출력을 입력으로 사용하는 체이닝된 genrule을 보여줍니다.

def chained_genrules(name, visibility=None):
  native.genrule(
    name = name + "-one",
    outs = [name + ".one"],
    cmd = "$(location :tool-one) $@",
    tools = [":tool-one"],
    visibility = ["//visibility:private"],
  )

  native.genrule(
    name = name + "-two",
    srcs = [name + ".one"],
    outs = [name + ".two"],
    cmd = "$(location :tool-two) $< $@",
    tools = [":tool-two"],
    visibility = visibility,
  )

이 예에서는 두 번째 genrule에만 공개 상태 값을 할당합니다. 이를 통해 매크로 작성자는 중간 규칙의 출력을 워크스페이스의 다른 타겟에서 종속되지 않도록 숨길 수 있습니다.

확장 매크로

매크로의 작동 방식을 조사하려면 query 명령어와 --output=build를 사용하여 확장된 형식을 확인합니다.

$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location //:generator) some_arg > $@",
)

네이티브 규칙 인스턴스화

네이티브 규칙 (load() 문이 필요하지 않은 규칙)은 네이티브 모듈에서 인스턴스화할 수 있습니다.

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

패키지 이름 (예: 매크로를 호출하는 BUILD 파일)을 알아야 하는 경우 native.package_name() 함수를 사용하세요. nativeBUILD 파일이 아닌 .bzl 파일에서만 사용할 수 있습니다.

매크로의 라벨 해상도

기존 매크로는 로드 단계에서 평가되므로 기존 매크로에 발생하는 "//foo:bar"와 같은 라벨 문자열은 매크로가 정의된 .bzl 파일이 아닌 매크로가 사용되는 BUILD 파일에 대해 상대적으로 해석됩니다. 게시된 Starlark 규칙의 일부이므로 다른 저장소에서 사용하도록 설계된 매크로의 경우 이러한 동작은 일반적으로 바람직하지 않습니다.

Starlark 규칙과 동일한 동작을 얻으려면 라벨 문자열을 Label 생성자로 래핑합니다.

# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
  native.cc_library(
    name = name,
    deps = deps + select({
      # Due to the use of Label, this label is resolved within @my_ruleset,
      # regardless of its site of use.
      Label("//config:needs_foo"): [
        # Due to the use of Label, this label will resolve to the correct target
        # even if the canonical name of @dep_of_my_ruleset should be different
        # in the main repo, such as due to repo mappings.
        Label("@dep_of_my_ruleset//tools:foo"),
      ],
      "//conditions:default": [],
    }),
    **kwargs,
  )

디버깅

  • bazel query --output=build //my/path:all에는 평가 후 BUILD 파일의 모양이 표시됩니다. 모든 기존 매크로, 글롭, 루프가 확장됩니다. 알려진 제한사항: select 표현식이 출력에 표시되지 않습니다.

  • generator_function (규칙을 생성한 함수) 또는 generator_name (매크로의 이름 속성)을 기준으로 출력을 필터링할 수 있습니다. bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'

  • BUILD 파일에서 규칙 foo가 정확히 생성되는 위치를 찾으려면 다음 트릭을 시도해 보세요. BUILD 파일 상단에 다음 줄을 삽입합니다. cc_library(name = "foo") Bazel을 실행합니다. 이름 충돌로 인해 규칙 foo가 생성될 때 예외가 발생하며 전체 스택 트레이스가 표시됩니다.

  • 디버깅을 위해 print를 사용할 수도 있습니다. 로드 단계 중에 메시지를 DEBUG 로그 행으로 표시합니다. 드물지만 예외적인 경우를 제외하고 코드를 저장소에 제출하기 전에 print 호출을 삭제하거나 기본값이 Falsedebugging 매개변수 아래에서 조건부로 만듭니다.

오류

오류를 발생시키려면 fail 함수를 사용하세요. 사용자에게 문제가 무엇인지, BUILD 파일을 수정하는 방법을 명확하게 설명합니다. 오류를 포착할 수 없습니다.

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

규칙

  • 규칙을 인스턴스화하는 모든 공개 함수 (밑줄로 시작하지 않는 함수)에는 name 인수가 있어야 합니다. 이 인수는 선택사항이 아니어야 합니다 (기본값을 제공하지 않음).

  • 공개 함수는 Python 규칙에 따라 docstring을 사용해야 합니다.

  • BUILD 파일에서 매크로의 name 인수는 위치 인수가 아닌 키워드 인수여야 합니다.

  • 매크로에 의해 생성된 규칙의 name 속성에는 이름 인수가 접두사로 포함되어야 합니다. 예를 들어 macro(name = "foo")cc_library foo 및 genrule foo_gen를 생성할 수 있습니다.

  • 대부분의 경우 선택적 매개변수의 기본값은 None여야 합니다. None는 네이티브 규칙에 직접 전달할 수 있으며, 이 경우 인수를 전달하지 않은 것과 동일하게 처리됩니다. 따라서 이 목적으로 0, False 또는 []로 대체할 필요가 없습니다. 대신 매크로는 만드는 규칙에 위임해야 합니다. 기본값이 복잡하거나 시간이 지남에 따라 변경될 수 있기 때문입니다. 또한 기본값으로 명시적으로 설정된 매개변수는 쿼리 언어 또는 빌드 시스템 내부에서 액세스할 때 설정되지 않은 매개변수 (또는 None로 설정된 매개변수)와 다르게 표시됩니다.

  • 매크로에는 선택적 visibility 인수가 있어야 합니다.