매크로

이 페이지에서는 매크로 사용의 기본사항을 설명하고 일반적인 사용 사례, 디버깅, 규칙을 설명합니다.

매크로는 규칙을 인스턴스화할 수 있는 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,
  )

이 예에서는 2genrule에만 공개 상태 값을 할당합니다. 따라서 매크로 작성자가 중간 규칙의 출력이 작업공간의 다른 대상에 종속되지 않도록 숨길 수 있습니다.

확장 매크로

매크로의 기능을 조사하려면 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() 함수를 사용합니다. native.bzl 파일에서만 사용할 수 있고 WORKSPACE 또는 BUILD 파일에는 사용할 수 없습니다.

매크로의 라벨 해상도

매크로는 로드 단계에서 평가되므로 매크로에서 발생하는 "//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 workspace, such as due to repo mappings.
        Label("@dep_of_my_ruleset//tools:foo"),
      ],
      "//conditions:default": [],
    }),
    **kwargs,
  )

디버깅

  • bazel query --output=build //my/path:all는 평가 후 BUILD 파일이 어떻게 표시되는지 보여줍니다. 모든 매크로, glob, 루프가 확장됩니다. 알려진 제한사항: 현재 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 인수가 있어야 합니다.