Grupos de execução

Os grupos de execução permitem várias plataformas de execução em um único destino. Cada grupo de execução tem dependências próprias de cadeia de ferramentas e realiza a própria resolução de cadeia de ferramentas.

Contexto

Os grupos de execução permitem que o autor da regra defina conjuntos de ações, cada um com uma plataforma de execução potencialmente diferente. Várias plataformas de execução podem permitir que as ações sejam executadas de maneira diferente, por exemplo, compilando um app iOS em um worker remoto (Linux) e vinculando/assinando o código em um worker Mac local.

A capacidade de definir grupos de ações também ajuda a aliviar o uso de mnemônicos de ação como um proxy para especificar ações. Não há garantia de que os mnemônicos sejam exclusivos e só podem referenciar uma única ação. Isso é especialmente útil para alocar recursos extras para ações específicas de memória e processamento intensivo, como a vinculação em builds C++, sem alocar demais para tarefas menos exigentes.

Definir grupos de execução

Durante a definição de regras, os autores podem declarar um conjunto de grupos de execução. Em cada grupo de execução, o autor da regra pode especificar tudo o que é necessário para selecionar uma plataforma de execução para esse grupo, ou seja, restrições via exec_compatible_with e tipos de conjunto de ferramentas via toolchain.

# foo.bzl
my_rule = rule(
    _impl,
    exec_groups = {
        link: exec_group(
            exec_compatible_with = [ "@platforms//os:linux" ]
            toolchains = ["//foo:toolchain_type"],
        ),
        test: exec_group(
            toolchains = ["//foo_tools:toolchain_type"],
        ),
    },
    attrs = {
        "_compiler": attr.label(cfg = config.exec("link"))
    },
)

No snippet de código acima, você pode ver que as dependências de ferramentas também podem especificar a transição de um grupo de execução usando o cfg parâmetro de atributo e o config módulo. O módulo expõe uma função exec que recebe um único parâmetro de string, que é o nome do grupo de execução para o qual a dependência precisa ser criada.

Como nas regras nativas, o grupo de execução test está presente por padrão nas regras de teste do Starlark.

Herança de grupo de execução

Além de definir as próprias restrições e cadeias de ferramentas, um novo grupo de execução pode declarar que quer herdar do grupo de execução padrão da regra transmitindo o parâmetro copy_from_rule = True. É um erro definir copy_from_rule como verdadeiro e também transmitir exec_compatible_with ou toolchains.

Um grupo de execução que herda do grupo de execução padrão copia restrições, cadeias de ferramentas e propriedades de execução do padrão. Isso inclui restrições e propriedades de execução definidas no nível do destino, não apenas aquelas especificadas pela própria regra. Em outras palavras, considerando o seguinte:

# foo.bzl
my_rule = rule(
    _impl,
    exec_groups = {
        copied: exec_group(
            copy_from_rule = True,
            # This will inherit exec_compatible_with and toolchains.
            # Setting them here directly would be an error, however.
        ),
    },
    toolchains = ["//foo_tools:toolchain_type"],
    exec_compatible_with = ["@platforms//os:linux"],
)

# BUILD

my_rule(
    name = "demo",
    exec_compatible_with = [":local_constraint"],
)

O grupo de execução copied para o destino configurado demo vai incluir todos os seguintes elementos: - //fool_tools:toolchain_type - @platforms//os:linux - :local_constraint

Acessar grupos de execução

Na implementação da regra, você pode declarar que as ações precisam ser executadas na plataforma de execução de um grupo de execução. Para isso, use o exec_group parâmetro de métodos de geração de ações, especificamente ctx.actions.run e ctx.actions.run_shell.

# foo.bzl
def _impl(ctx):
  ctx.actions.run(
     inputs = [ctx.attr._some_tool, ctx.srcs[0]]
     exec_group = "compile",
     # ...
  )

Os autores de regras também poderão acessar os conjuntos de ferramentas resolvidos de grupos de execução, da mesma forma que você pode acessar o conjunto de ferramentas resolvido de um destino:

# foo.bzl
def _impl(ctx):
  foo_info = ctx.exec_groups["link"].toolchains["//foo:toolchain_type"].fooinfo
  ctx.actions.run(
     inputs = [foo_info, ctx.srcs[0]]
     exec_group = "link",
     # ...
  )

Usar grupos de execução para definir propriedades de execução

Os grupos de execução são integrados ao exec_properties atributo que existe em cada regra e permite que o gravador de destino especifique um dicionário de strings de propriedades que é transmitido para o mecanismo de execução. Por exemplo, se você quiser definir alguma propriedade, digamos, memória, para o destino e dar a determinadas ações uma alocação de memória maior, escreva uma entrada exec_properties com uma chave aumentada do grupo de execução, como:

# BUILD
my_rule(
    name = 'my_target',
    exec_properties = {
        'mem': '12g',
        'link.mem': '16g'
    }
    
)

Todas as ações com exec_group = "link" vão considerar o dicionário de propriedades de execução como {"mem": "16g"}. Como você pode ver aqui, as configurações no nível do grupo de execução substituem as configurações no nível do destino.

Grupos de execução para regras nativas

Os seguintes grupos de execução estão disponíveis para ações definidas por regras nativas:

  • test: ações do executor de testes.
  • cpp_link: ações de vinculação C++.

Criar grupos de execução para definir propriedades de execução

Às vezes, você quer usar um grupo de execução para dar ações específicas a diferentes propriedades de execução, mas não quer cadeias de ferramentas ou restrições diferentes da regra. Para essas situações, você pode criar grupos de execução usando o parâmetro copy_from_rule:

# foo.bzl

# Creating an exec group with `copy_from_rule=True` is the same as explicitly
# setting the exec group's toolchains and constraints to the same values as the
# rule's respective parameters.
my_rule = rule(
    _impl,
    exec_compatible_with = ["@platforms//os:linux"],
    toolchains = ["//foo:toolchain_type"],
    exec_groups = {
        # The following two groups have the same toolchains and constraints:
        foo: exec_group(copy_from_rule = True),
        "bar": exec_group(
            exec_compatible_with = ["@platforms//os:linux"],
            toolchains = ["//foo:toolchain_type"],
        ),
    },
)

#

Grupos de execução e propriedades de execução da plataforma

É possível definir exec_properties para grupos de execução arbitrários em destinos de plataforma (ao contrário de exec_properties definidos diretamente em um destino, em que as propriedades de grupos de execução desconhecidos são rejeitadas). Os destinos herdam as exec_properties da plataforma de execução que afetam o grupo de execução padrão e outros grupos de execução relevantes.

Por exemplo, suponha que a execução de um teste C++ exija que algum recurso esteja disponível, mas não seja necessário para compilação e vinculação. Isso pode ser modelado da seguinte maneira:

constraint_setting(name = "resource")
constraint_value(name = "has_resource", constraint_setting = ":resource")

platform(
    name = "platform_with_resource",
    constraint_values = [":has_resource"],
    exec_properties = {
        "test.resource": "...",
    },
)

cc_test(
    name = "my_test",
    srcs = ["my_test.cc"],
    exec_compatible_with = [":has_resource"],
)

exec_properties definidos diretamente nos destinos têm precedência sobre aqueles que são herdados da plataforma de execução.