Starlark는 Python과 비슷합니다.
원래 Bazel에서 사용하기 위해 개발되었으며 이후
다른 도구들에 의해 결정됩니다. Bazel의 BUILD
및 .bzl
파일은 다음과 같은 언어로 작성됩니다.
Starlark는 '빌드 언어'로 적절하게 알려져 있지만
'Starlark'이라고도 하며, 특히 특정 특성이
기본 제공 또는 '네이티브'가 아닌 빌드 언어로 표현됩니다. 부품
있습니다. Bazel은 수많은 빌드 관련 기능으로 핵심 언어를 보강합니다
glob
, genrule
, java_binary
등입니다.
자세한 내용은 Bazel 및 Starlark 문서 자세한 내용을 살펴보고 규칙 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
입니다.
평가 모델
계속 진행하기 전에 코드가 평가되는 방식을 이해하는 것이 중요합니다.
몇 가지 print 문으로 foo.bzl
를 업데이트합니다.
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을 사용하는 경우 'bzl file Assessment'가 한 번만 나타납니다. 왜냐하면 Bazel은 평가 결과를 캐시합니다. - 콜백 함수
_foo_binary_impl
는 호출되지 않습니다. Bazel 쿼리 로드BUILD
파일을 지원하지만 대상을 분석하지는 않습니다.
대상을 분석하려면 cquery
('구성됨')를 사용합니다.
쿼리') 또는 build
명령어를 사용합니다.
$ bazel build :all
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 file 평가'도 아님을 알 수 있습니다. 'BUILD file'도 없습니다. 다시 인쇄되면
foo.bzl
의 평가가 bazel query
호출 후에 캐시되기 때문입니다.
Bazel은 실제로 실행될 때만 print
문을 내보냅니다.
파일 만들기
규칙을 보다 유용하게 만들려면 규칙을 업데이트하여 파일을 생성하세요. 먼저 파일에 이름을 지정합니다 이 예에서는 대상:
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]))]
DefaultInfo
및 depset
함수는 나중에 살펴보겠습니다. 지금은
마지막 행이 규칙의 출력을 선택하는 방법이라고 가정합니다.
이제 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
문서
boolean과 같은 다른 유형의 속성을 사용할 수도 있습니다.
또는 정수 목록일 수 있습니다.
종속 항목
종속 항목 속성(예: attr.label
)
및 attr.label_list
,
특성을 소유한 타겟에서 종속 항목을
라벨이 속성 값에 표시됩니다. 이러한 종류의 속성은
타겟 그래프의 각 부분에 해당합니다.
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"])
추가 정보
- 규칙에 대한 참고 문서를 확인하세요.
- depset를 숙지합니다.
- 예시 저장소 확인 여기에는 규칙의 추가 예시가 포함됩니다.