이 페이지에서는 Aspect 사용의 기본사항과 이점을 설명하고 간단한 예와 고급 예를 제공합니다.
Aspect를 사용하면 추가 정보 와 작업으로 빌드 종속 항목 그래프를 보강할 수 있습니다. Aspect가 유용할 수 있는 일반적인 시나리오는 다음과 같습니다.
- Bazel을 통합하는 IDE는 Aspect를 사용하여 프로젝트에 관한 정보를 수집할 수 있습니다.
- 코드 생성 도구는 Aspect를 활용하여
대상에 관계없이 입력에 대해 실행할 수 있습니다. 예를 들어
BUILD파일은 protobuf 라이브러리 정의의 계층 구조를 지정할 수 있으며 언어별 규칙은 Aspect를 사용하여 특정 언어의 프로토콜 버퍼 지원 코드를 생성하는 작업을 연결할 수 있습니다.
Aspect 기본사항
BUILD 파일은 프로젝트의 소스 코드에 관한 설명을 제공합니다. 프로젝트에 포함되는 소스
파일, 이러한 파일에서 빌드해야 하는 아티팩트 (대상), 이러한 파일 간의 종속 항목 등을 설명합니다. Bazel은
이 정보를 사용하여 빌드를 실행합니다. 즉, 아티팩트 생성에 필요한 작업 집합 (예: 컴파일러 또는 링커 실행)을 파악하고
이러한 작업을 실행합니다. Bazel은 대상 간의 종속 항목
그래프를 구성하고 이 그래프를 방문하여 이러한 작업을 수집함으로써 이 작업을 수행합니다.
다음 BUILD 파일을 살펴보세요.
java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)
이 BUILD 파일은 다음 그림에 표시된 종속 항목 그래프를 정의합니다.

그림 1. BUILD 파일 종속 항목 그래프
Bazel은 위 예의 모든
대상에 대해 상응하는 규칙 (이 경우 'java_library')의 구현 함수를 호출하여 이 종속 항목 그래프를 분석합니다. 규칙 구현 함수는
.jar 파일과 같은 아티팩트를 빌드하는 작업을 생성하고 이러한 아티팩트의 위치
와 이름과 같은 정보를
제공업체의 이러한 대상의 역종속 항목에 전달합니다.
Aspect는 작업을 생성하고 제공업체를 반환하는 구현 함수가 있다는 점에서 규칙과 유사합니다. 하지만 Aspect의 강력함은 종속 항목 그래프가 Aspect를 위해 빌드되는 방식에서 비롯됩니다. Aspect에는 구현 과 Aspect가 전파하는 모든 속성의 목록이 있습니다. 'deps'라는 속성을 따라 전파되는 Aspect A를 살펴보세요. 이 Aspect는 대상 X에 적용되어 Aspect 애플리케이션 노드 A(X)를 생성할 수 있습니다. Aspect A는 애플리케이션 중에 'deps' 속성 (A의 전파 목록에 있는 모든 속성)에서 X가 참조하는 모든 대상에 재귀적으로 적용됩니다.
따라서 Aspect A를 대상 X에 적용하는 단일 작업은 다음 그림에 표시된 대상의 원래 종속 항목 그래프의 '섀도 그래프'를 생성합니다.

그림 2. Aspect가 포함된 빌드 그래프
섀도 처리되는 유일한 가장자리는 전파 집합의 속성을 따라 있는 가장자리이므로 이
runtime_deps 가장자리는 이
예에서는 섀도 처리되지 않습니다. 그런 다음 Aspect 구현 함수는 규칙 구현이 원래 그래프의 노드에서 호출되는 방식과 유사하게
섀도 그래프의 모든 노드에서 호출됩니다.
간단한 예시
이 예에서는
규칙과 모든 종속 항목의 소스 파일을 재귀적으로 출력하는 방법을 보여줍니다.deps Aspect 구현, Aspect 정의, Bazel 명령줄에서 Aspect를 호출하는 방법을 보여줍니다.
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
예를 부분으로 나누고 각 부분을 개별적으로 살펴보겠습니다.
Aspect 정의
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
Aspect 정의는 규칙 정의와 유사하며
aspect 함수를 사용하여 정의됩니다.
규칙과 마찬가지로 Aspect에는 구현 함수가 있습니다. 이 경우
_print_aspect_impl입니다.
attr_aspects는 Aspect가 전파되는 규칙 속성의 목록입니다.
이 경우 Aspect는 적용되는 규칙의 deps 속성을 따라 전파됩니다.
attr_aspects의 또 다른 일반적인 인수는 ['*']입니다. 이 인수는
Aspect를 규칙의 모든 속성에 전파합니다.
Aspect 구현
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
Aspect 구현 함수는 규칙 구현 함수와 유사합니다. 이 함수는 제공업체를 반환하고 작업을 생성할 수 있으며 다음 두 인수를 사용합니다.
구현 함수는 대상 규칙의 속성에 액세스할 수 있습니다.
ctx.rule.attr 이 함수는 적용되는 대상에서 제공하는 제공업체 (via target 인수)를 검사할 수 있습니다.
Aspect는 제공업체 목록을 반환해야 합니다. 이 예에서 Aspect 는 아무것도 제공하지 않으므로 빈 목록을 반환합니다.
명령줄을 사용하여 Aspect 호출
Aspect를 적용하는 가장 간단한 방법은
--aspects
인수를 사용하여 명령줄에서 적용하는 것입니다. 위의 Aspect가 print.bzl
이라는 파일에 정의되었다고 가정하면 다음이 적용됩니다.
bazel build //MyExample:example --aspects print.bzl%print_aspect
print_aspect를 example 대상과
deps 속성을 통해 재귀적으로 액세스할 수 있는 모든 대상 규칙에 적용합니다.deps
--aspects 플래그는 <extension file label>%<aspect top-level name> 형식의 Aspect 사양인 인수 하나를 사용합니다.
고급 예시
다음 예에서는 대상의 파일을 계산하고 확장 프로그램별로 필터링할 수 있는 대상 규칙에서 Aspect를 사용하는 방법을 보여줍니다. 제공업체를 사용하여 값을 반환하는 방법, 매개변수를 사용하여 인수를 Aspect 구현에 전달하는 방법, 규칙에서 Aspect를 호출하는 방법을 보여줍니다.
file_count.bzl 파일:
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
BUILD.bazel 파일:
load('//:file_count.bzl', 'file_count_rule')
cc_library(
name = 'lib',
srcs = [
'lib.h',
'lib.cc',
],
)
cc_binary(
name = 'app',
srcs = [
'app.h',
'app.cc',
'main.cc',
],
deps = ['lib'],
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
Aspect 정의
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
이 예에서는 Aspect가 deps 속성을 통해 전파되는 방법을 보여줍니다.
attrs는 Aspect의 속성 집합을 정의합니다. 공개 Aspect 속성은 string 유형이며 매개변수라고 합니다. 매개변수에는 values
속성이 지정되어 있어야 합니다. 이 예에는 값이 '*', 'h', 'cc'일 수 있는 extension
이라는 매개변수가 있습니다.
Aspect의 매개변수 값은 Aspect를 요청하는 규칙과 이름이 같은 문자열 속성에서 가져옵니다 (file_count_rule 정의 참고). 매개변수가 있는 Aspect는 매개변수를 정의하는 구문이 없으므로 명령줄을 통해 사용할 수 없습니다.
Aspect는 label 또는
label_list 유형의 비공개 속성을 가질 수도 있습니다. 비공개 라벨 속성은 Aspect에서 생성된 작업에 필요한
도구 또는 라이브러리의 종속 항목을 지정하는 데 사용할 수 있습니다. 이 예에서는 비공개 속성이 정의되어 있지 않지만 다음 코드 스니펫은 도구를 Aspect에 전달하는 방법을 보여줍니다.
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
Aspect 구현
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
규칙 구현 함수와 마찬가지로 Aspect 구현 함수 는 종속 항목에서 액세스할 수 있는 제공업체의 구조체를 반환합니다.
이 예에서 FileCountInfo는 필드 count가 하나인 제공업체로 정의됩니다.
속성을 사용하여 제공업체의 필드를 명시적으로 정의하는 것이 좋습니다.fields
Aspect 애플리케이션 A(X)의 제공업체 집합은 대상 X의 규칙 구현과 Aspect A의
구현에서 제공되는 제공업체의 합집합입니다. 규칙 구현이 전파하는 제공업체는
Aspect가 적용되기 전에 생성되고 고정되며
Aspect에서 수정할 수 없습니다. 대상과 대상에 적용되는 Aspect가 각각
동일한 유형의 제공업체를 제공하는 경우 오류가 발생합니다. 단,
OutputGroupInfo
(규칙과 Aspect가 서로 다른 출력 그룹을 지정하는 한 병합됨) 및
InstrumentedFilesInfo
(Aspect에서 가져옴)는 예외입니다. 즉, Aspect 구현은
반환할 수 없습니다 DefaultInfo.
매개변수와 비공개 속성은
ctx의 속성으로 전달됩니다. 이 예에서는 extension 매개변수를 참조하고
계산할 파일을 결정합니다.
제공업체를 반환하는 경우 Aspect가 전파되는 속성의 값 (attr_aspects 목록에서)
은 Aspect를 적용한 결과로 대체됩니다. 예를 들어 대상
X에 종속 항목에 Y와 Z가 있는 경우 A(X)의 ctx.rule.attr.deps는 [A(Y), A(Z)]가 됩니다.
이 예에서 ctx.rule.attr.deps는 Aspect가 적용된 원래 대상의 'deps'에 Aspect를 적용한 결과인 대상 객체입니다.
이 예에서 Aspect는 대상의 종속 항목에서 FileCountInfo 제공업체에 액세스하여 총 전이 파일 수를 누적합니다.
규칙에서 Aspect 호출
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
규칙 구현은 FileCountInfo
를 통해 ctx.attr.deps에 액세스하는 방법을 보여줍니다.
규칙 정의는 매개변수 (extension)
를 정의하고 기본값 (*)을 지정하는 방법을 보여줍니다. Aspect 정의에서 매개변수에 적용된 제한사항으로 인해 기본값이 'cc', 'h', '*' 중 하나가 아닌 경우 오류가 발생합니다.
대상 규칙을 통해 Aspect 호출
load('//:file_count.bzl', 'file_count_rule')
cc_binary(
name = 'app',
...
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
이 예에서는 규칙을 통해 extension 매개변수를 Aspect
에 전달하는 방법을 보여줍니다. extension 매개변수는
규칙 구현에 기본값이 있으므로 extension은 선택적 매개변수로 간주됩니다.
file_count 대상이 빌드되면 Aspect는 자체적으로 평가되고 deps를 통해 재귀적으로 액세스할 수 있는 모든 대상에 대해 평가됩니다.