매크로를 사용하여 맞춤 동사 만들기

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

Bazel과의 일상적인 상호작용은 주로 몇 가지 명령어를 통해 이루어집니다. build, test, run입니다. 하지만 경우에 따라 제약이 느껴질 수 있습니다. 패키지를 저장소로 푸시하거나, 최종 사용자를 위한 문서를 게시하거나, Kubernetes를 사용하여 애플리케이션을 배포해야 할 수 있습니다. 그러나 Bazel에는 publish 또는 deploy 명령어가 없습니다. 이러한 작업은 어디에 적합한가요?

bazel 실행 명령어

Bazel이 밀폐성, 재현성, 성과 증분에 중점을 두기 때문에 buildtest 명령어는 위의 작업에 유용하지 않습니다. 이러한 작업은 네트워크 액세스가 제한된 샌드박스에서 실행될 수 있으며 모든 bazel build에서 다시 실행된다는 보장은 없습니다.

대신 부작용을 내고자 하는 작업인 bazel run를 사용하면 됩니다. Bazel 사용자는 실행 파일을 만드는 규칙에 익숙하며, 규칙 작성자는 일반적인 패턴 집합을 따라 이를 '커스텀 동사'로 확장할 수 있습니다.

실제: 규칙_k8s

예를 들어 Bazel의 Kubernetes 규칙인 rules_k8s를 생각해 보세요. 다음과 같은 타겟이 있다고 가정해 보겠습니다.

# BUILD file in //application/k8s
k8s_object(
    name = "staging",
    kind = "deployment",
    cluster = "testing",
    template = "deployment.yaml",
)

staging 대상에 bazel build가 사용될 때 k8s_object 규칙은 표준 Kubernetes YAML 파일을 빌드합니다. 그러나 staging.apply:staging.delete와 같은 이름을 사용하는 k8s_object 매크로에서도 추가 타겟이 생성됩니다. 이러한 작업을 실행하는 빌드 스크립트는 bazel run staging.apply로 실행하면 자체 bazel k8s-apply 또는 bazel k8s-delete 명령어처럼 동작합니다.

또 다른 예: ts_api_guardian_test

이 패턴은 Angular 프로젝트에서도 확인할 수 있습니다. ts_api_guardian_test 매크로는 타겟 두 개를 생성합니다. 첫 번째는 생성된 nodejs_test 타겟으로, 일부 생성된 출력을 '골든' 파일 (예상된 출력을 포함하는 파일)과 비교합니다. 일반 bazel test 호출을 사용하여 빌드하고 실행할 수 있습니다. angular-cli에서 bazel test //etc/api:angular_devkit_core_api를 사용하여 해당 대상 중 하나를 실행할 수 있습니다.

시간이 지남에 따라 적법한 사유로 이 골든 파일을 업데이트해야 할 수 있습니다. 이를 수동으로 업데이트하는 것은 지루하고 오류가 발생하기 쉽습니다. 따라서 이 매크로는 골든 파일을 비교하는 대신 골든 파일을 업데이트하는 nodejs_binary 타겟도 제공합니다. 동일한 테스트 스크립트는 호출 방식에 따라 '확인' 또는 '수락' 모드로 실행되도록 작성할 수 있습니다. 이는 이미 알아본 동일한 패턴을 따릅니다. 네이티브 bazel test-accept 명령어는 없지만 bazel run //etc/api:angular_devkit_core_api.accept를 사용하여 동일한 효과를 달성할 수 있습니다.

이 패턴은 매우 강력할 수 있으며 인식하면 학습을 진행하는 것이 일반적입니다.

자체 규칙 조정

매크로는 이 패턴의 핵심입니다. 매크로는 규칙처럼 사용되지만 타겟을 여러 개 만들 수 있습니다. 일반적으로 기본 빌드 작업을 실행하는 지정된 이름으로 대상을 만듭니다. 이 빌드는 일반 바이너리, Docker 이미지 또는 소스 코드 보관 파일을 빌드할 수 있습니다. 이 패턴에서는 기본 바이너리의 출력에 따라 부수 효과를 실행하는 스크립트를 생성하기 위해 추가 타겟이 생성됩니다(예: 결과 바이너리 게시 또는 예상 테스트 출력 업데이트).

이를 설명하기 위해, 매크로가 준비되면 Sphinx로 웹사이트를 생성하는 가상의 규칙을 래핑하여 사용자가 준비되었을 때 이를 게시할 수 있는 추가 타겟을 만듭니다. Sphinx를 사용하여 웹사이트를 생성하는 다음과 같은 기존 규칙을 생각해 보세요.

_sphinx_site = rule(
     implementation = _sphinx_impl,
     attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)

그런 후 실행 시 생성된 페이지를 게시하는 스크립트를 빌드하는 다음과 같은 규칙을 살펴보겠습니다.

_sphinx_publisher = rule(
    implementation = _publish_impl,
    attrs = {
        "site": attr.label(),
        "_publisher": attr.label(
            default = "//internal/sphinx:publisher",
            executable = True,
        ),
    },
    executable = True,
)

마지막으로 다음 규칙을 정의하여 위의 두 규칙에 대한 타겟을 함께 만듭니다.

def sphinx_site(name, srcs = [], **kwargs):
    # This creates the primary target, producing the Sphinx-generated HTML.
    _sphinx_site(name = name, srcs = srcs, **kwargs)
    # This creates the secondary target, which produces a script for publishing
    # the site generated above.
    _sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)

BUILD 파일에서 마치 기본 타겟을 만드는 것처럼 매크로를 사용합니다.

sphinx_site(
    name = "docs",
    srcs = ["index.md", "providers.md"],
)

이 예에서는 매크로가 표준의 단일 Bazel 규칙인 것처럼 'docs' 대상이 생성됩니다. 규칙이 생성되면 규칙이 몇 가지 구성을 생성하고, Sphinx를 실행하여 수동으로 검사할 수 있는 HTML 사이트를 생성합니다. 그러나 추가 'docs.publish' 대상도 생성되어 사이트를 게시하기 위한 스크립트가 빌드됩니다. 기본 대상의 출력을 확인하고 나면 가상의 bazel publish 명령어와 같이 bazel run :docs.publish를 사용하여 공개 소비용으로 게시할 수 있습니다.

_sphinx_publisher 규칙의 구현이 어떻게 표시되는지 즉시 명확히 알 수는 없습니다. 이러한 작업은 대개 런처 셸 스크립트를 작성합니다. 이 메서드에는 일반적으로 ctx.actions.expand_template를 사용하여 매우 간단한 셸 스크립트가 포함됩니다. 이 경우에는 기본 바이너리의 출력 경로를 사용하여 게시자 바이너리를 호출합니다. 이렇게 하면 게시자 구현이 일반적일 수 있고, _sphinx_site 규칙은 HTML을 생성할 수 있으며, 이 작은 스크립트만 있으면 둘을 조합할 수 있습니다.

rules_k8s에서 이는 실제로 .apply의 작업입니다. expand_templateapply.sh.tpl에 기반하여 기본 타겟의 출력과 함께 kubectl를 실행하는 매우 간단한 Bash 스크립트를 작성합니다. 그런 다음 bazel run :staging.apply로 이 스크립트를 빌드하고 실행하여 k8s_object 대상에 k8s-apply 명령어를 효과적으로 제공할 수 있습니다.