Esta página explica os conceitos básicos e os benefícios de usar os aspectos e oferece exemplos simples e avançados.
Os aspectos permitem aumentar os gráficos de dependência do build com mais informações. e ações. Alguns cenários típicos em que os aspectos podem ser úteis:
- Os ambientes de desenvolvimento integrado que integram o Bazel podem usar aspectos para coletar informações sobre os projeto.
- As ferramentas de geração de código podem aproveitar aspectos para executar suas entradas
independente de destino. Por exemplo, os arquivos
BUILD
podem especificar uma hierarquia da biblioteca protobuf definições e regras específicas de uma linguagem podem usar aspectos para anexar ações que geram o código de suporte protobuf para uma linguagem específica.
Conceitos básicos do aspecto
Os arquivos BUILD
fornecem uma descrição do código-fonte de um projeto: qual código-fonte
arquivos que fazem parte do projeto, quais artefatos (destinos) precisam ser criados
esses arquivos, quais são as dependências entre eles etc. O Bazel usa
essas informações para executar um build, ou seja, descobrir o conjunto de ações
necessárias para produzir os artefatos (como a execução do compilador ou vinculador) e
executa essas ações. O Bazel faz isso construindo uma dependência
gráfico entre destinos e acessar esse gráfico para coletar essas ações.
Considere o seguinte arquivo 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'], ...)
Esse arquivo BUILD
define um gráfico de dependência mostrado na figura a seguir:
Figura 1. Gráfico de dependências do arquivo BUILD
.
O Bazel analisa esse gráfico de dependência chamando uma função de implementação de
a rule correspondente (neste caso, "java_library") para cada
no exemplo acima. As funções de implementação de regras geram ações
criar artefatos, como arquivos .jar
, e transmitir informações, como locais.
e nomes desses artefatos, até as dependências reversas desses destinos em
provedores de serviços.
Os aspectos são semelhantes às regras, porque têm uma função de implementação que gera ações e retorna provedores. No entanto, seu poder vem a maneira como o gráfico de dependências é criado para elas. Um aspecto tem uma implementação e uma lista de todos os atributos propagados. Considere um aspecto A que é propagada ao longo de atributos denominados "deps". Este aspecto pode ser aplicado um alvo X, produzindo um nó de aplicativo de aspecto A(X). Durante a aplicação, o aspecto A é aplicado recursivamente a todos os destinos aos quais X se refere nas "dependências" (todos os atributos na lista de propagação de A).
Assim, um único ato de aplicar o aspecto A a um destino X resulta em um "gráfico sombra" de o gráfico de dependência original dos destinos mostrado na figura abaixo:
Figura 2. Criar um gráfico com aspectos.
As únicas bordas que estão sombreadas são as bordas ao longo dos atributos na
o conjunto de propagação, fazendo com que a borda runtime_deps
não seja ocultada
exemplo. Uma função de implementação de aspecto é invocada em todos os nós
no gráfico de sombra de maneira semelhante a como as implementações de regras são invocadas nos nós
do gráfico original.
Exemplo simples
Este exemplo demonstra como imprimir os arquivos de origem de uma
e todas as dependências dela com um atributo deps
. Mostra
uma implementação de aspecto, uma definição de aspecto e como invocá-lo
na linha de comando do Bazel.
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'],
)
Vamos dividir o exemplo nas partes e analisar cada uma individualmente.
Definição de aspecto
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
As definições de aspectos são semelhantes às de regras e são definidas com
a função aspect
.
Assim como uma regra, um aspecto tem uma função de implementação que, neste caso, é
_print_aspect_impl
:
attr_aspects
é uma lista de atributos de regra em que o aspecto é propagado.
Nesse caso, o aspecto será propagado pelo atributo deps
da
em que ela é aplicada.
Outro argumento comum para attr_aspects
é ['*']
, que propagaria a
aspecto a todos os atributos de uma regra.
Implementação de aspectos
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 []
As funções de implementação de aspectos são semelhantes às de implementação de regras . Eles retornam provedores, podem gerar actions e receba dois argumentos:
target
: o destino a que o aspecto está sendo aplicado.ctx
: objetoctx
que pode ser usado para acessar atributos. e gerar saídas e ações.
A função de implementação pode acessar os atributos da regra de destino usando
ctx.rule.attr
Ele pode examinar provedores que estão
fornecidos pelo destino a que é aplicado (por meio do argumento target
).
Os aspectos são necessários para retornar uma lista de provedores. Neste exemplo, o aspecto não fornece nada, então retorna uma lista vazia.
Como invocar o aspecto usando a linha de comando
A maneira mais simples de aplicar um aspecto é pela linha de comando, usando o
--aspects
. Considerando que o aspecto acima foi definido em um arquivo chamado print.bzl
isso:
bazel build //MyExample:example --aspects print.bzl%print_aspect
aplicaria o print_aspect
ao example
de destino e a todos os
regras de destino que são acessíveis recursivamente pelo atributo deps
.
A flag --aspects
usa um argumento, que é uma especificação do aspecto
no formato <extension file label>%<aspect top-level name>
.
Exemplo avançado
O exemplo a seguir demonstra o uso de um aspecto de uma regra de destino que conta arquivos em destinos, potencialmente filtrando-os por extensão. Ele mostra como usar um provedor para retornar valores e como usar parâmetros para transmitir um argumento em uma implementação de aspecto e como invocar um aspecto de uma regra.
Arquivo 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 = '*'),
},
)
Arquivo 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',
)
Definição de aspecto
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
Este exemplo mostra como o aspecto se propaga pelo atributo deps
.
attrs
define um conjunto de atributos para um aspecto. Atributos de aspecto público
são do tipo string
e são chamados de parâmetros. Os parâmetros precisam ter um values
especificado neles. Este exemplo tem um parâmetro chamado extension
que podem ter "*
", "h
" ou "cc
" como um valor.
Os valores de parâmetros do aspecto são extraídos do atributo da string com o mesmo
nome da regra que solicita o aspecto (consulte a definição de file_count_rule
).
Aspectos com parâmetros não podem ser usados por meio da linha de comando porque não há
para definir os parâmetros.
Os aspectos também podem ter atributos particulares dos tipos label
ou
label_list
. Os atributos de rótulo particular podem ser usados para especificar dependências
ferramentas ou bibliotecas necessárias para ações geradas por aspectos. Não há
um atributo privado definido neste exemplo, mas o snippet de código a seguir
demonstra como passar uma ferramenta para um aspecto:
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
Implementação de aspectos
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)]
Assim como uma função de implementação de regras, uma função de implementação de aspectos retorna uma estrutura de provedores que podem ser acessados pelas dependências.
Neste exemplo, o FileCountInfo
é definido como um provedor que tem um
campo count
. É uma prática recomendada definir explicitamente os campos de um
provedor usando o atributo fields
.
O conjunto de provedores para um aplicativo de aspecto A(X) é a união de provedores
da implementação de uma regra para a meta X e da
implementação do aspecto A. Os provedores que uma implementação de regra propaga
são criados e congelados antes de os aspectos serem aplicados e não podem ser modificados
importante. É um erro se um destino e um aspecto aplicado a ele
fornecem um provedor do mesmo tipo, com exceções
OutputGroupInfo
(que é mesclado, desde que o
regra e aspecto especificam diferentes grupos de saída) e
InstrumentedFilesInfo
(retirado do aspecto). Isso significa que as implementações de aspectos podem
nunca retorna DefaultInfo
.
Os parâmetros e atributos particulares são passados nos atributos da
ctx
: Este exemplo faz referência ao parâmetro extension
e determina
quais arquivos contar.
Para provedores que retornam, os valores dos atributos nos quais
o aspecto é propagado (da lista attr_aspects
) são substituídos por
os resultados da aplicação do aspecto a elas. Por exemplo, se target
X tem Y e Z nas suas dependências, ctx.rule.attr.deps
para A(X) será [A(Y), A(Z)].
Neste exemplo, ctx.rule.attr.deps
são objetos de destino
resultados de aplicar o aspecto às dependências do destino original
o aspecto foi aplicado.
No exemplo, o aspecto acessa o provedor FileCountInfo
do
das dependências do destino para acumular o número transitivo total de arquivos.
Como invocar o aspecto de uma regra
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 = '*'),
},
)
A implementação da regra demonstra como acessar o FileCountInfo
.
via ctx.attr.deps
.
A definição da regra demonstra como definir um parâmetro (extension
)
e definir um valor padrão (*
). Ter um valor padrão que
não era "cc
", "h
" ou "*
" é um erro devido à
restrições ao parâmetro na definição do aspecto.
Como invocar um aspecto por meio de uma regra de destino
load('//:file_count.bzl', 'file_count_rule')
cc_binary(
name = 'app',
...
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
Isso demonstra como transmitir o parâmetro extension
ao aspecto
por meio da regra. Como o parâmetro extension
tem um valor padrão no
implementação de regra, extension
será considerado um parâmetro opcional.
Quando o destino file_count
é criado, nosso aspecto é avaliado para
em si e todos os destinos acessíveis recursivamente por deps
.