Regras de repositório

<ph-0-0>

Nesta página, explicamos como definir regras de repositório e fornecemos exemplos para mais detalhes.

Um repositório externo é uma árvore de diretórios que contém arquivos de origem utilizáveis em um build do Bazel, que é gerado sob demanda executando a regra de repo correspondente. Os repositórios podem ser definidos de várias maneiras, mas cada repo é definido invocando uma regra de repositório, assim como os destinos de build são definidos ao invocar essas regras. Eles podem ser usados para depender de bibliotecas de terceiros, como bibliotecas empacotadas Maven, mas também para gerar arquivos BUILD específicos do host em que o Bazel está sendo executado.

Definição da regra do repositório

Em um arquivo .bzl, use a função repository_rule para definir uma nova regra de repositório e armazená-la em uma variável global. Depois que uma regra de repo é definida, ela pode ser invocada como uma função para definir repositórios. Esta invocação geralmente é realizada dentro de uma função de implementação de extensão de módulo.

Os dois principais componentes de uma definição de regra de repositório são o esquema de atributos e a função de implementação. O esquema de atributos determina os nomes e tipos de atributos transmitidos para uma invocação de regra de repositório, e a função de implementação é executada quando o repositório precisa ser buscado.

Atributos

Atributos são argumentos passados para a invocação da regra do repo. O esquema de atributos aceitos por uma regra de repo é especificado usando o argumento attrs quando essa regra é definida com uma chamada para repository_rule. Um exemplo que define os atributos url e sha256 como strings:

http_archive = repository_rule(
    implementation=_impl,
    attrs={
        "url": attr.string(mandatory=True)
        "sha256": attr.string(mandatory=True)
    }
)

Para acessar um atributo na função de implementação, use repository_ctx.attr.<attribute_name>:

def _impl(repository_ctx):
    url = repository_ctx.attr.url
    checksum = repository_ctx.attr.sha256

Todos os repository_rules têm o atributo name definido implicitamente. Esse é um atributo de string que se comporta de maneira mágica: quando especificado como uma entrada para uma invocação de regra de repo, ele recebe um nome de repositório aparente. Porém, quando lido da função de implementação da regra de repo usando repository_ctx.attr.name, ele retorna o nome de repositório canônico.

Função de implementação

Toda regra de repositório exige uma função implementation. Ela contém a lógica real da regra e é executada estritamente na fase de carregamento.

A função tem exatamente um parâmetro de entrada, repository_ctx. A função retorna None para indicar que a regra pode ser reproduzida com base nos parâmetros especificados, ou um dict com um conjunto de parâmetros para essa regra que a transformaria em uma regra reproduzível que gera o mesmo repositório. Por exemplo, uma regra que rastreia um repositório Git significa retornar um identificador de confirmação específico em vez de uma ramificação flutuante que foi especificada originalmente.

O parâmetro de entrada repository_ctx pode ser usado para acessar valores de atributos e funções não herméticas (encontrar um binário, executar um binário, criar um arquivo no repositório ou fazer o download de um arquivo da Internet). Consulte a documentação da API para mais contexto. Exemplo:

def _impl(repository_ctx):
  repository_ctx.symlink(repository_ctx.attr.path, "")

local_repository = repository_rule(
    implementation=_impl,
    ...)

Quando a função de implementação é executada?

A função de implementação de uma regra repo é executada quando o Bazel precisa de um destino desse repositório, por exemplo, quando outro destino (em outro repo) depende dele ou se ele é mencionado na linha de comando. Espera-se que a função de implementação crie o repositório no sistema de arquivos. Isso é chamado de "buscar" o repositório.

Ao contrário dos destinos normais, os repositórios não são buscados novamente quando algo muda e causa uma diferença no repositório. Isso ocorre porque o Bazel não consegue detectar mudanças em algumas coisas ou pode causar sobrecarga demais em cada build (por exemplo, itens buscados na rede). Portanto, os repositórios serão buscados novamente somente se uma das seguintes coisas mudar:

  • Os atributos transmitidos para a invocação da regra de repositório.
  • O código Starlark que compreende a implementação da regra repo.
  • O valor de qualquer variável de ambiente transmitida ao método getenv() de repository_ctx ou declarada com o atributo environ da repository_rule. Os valores dessas variáveis de ambiente podem ser conectados na linha de comando com a sinalização --repo_env.
  • O conteúdo de qualquer arquivo transmitido para read(), execute() e métodos semelhantes de repository_ctx que é referenciado por um rótulo (por exemplo, //mypkg:label.txt, mas não mypkg/label.txt)
  • Quando bazel fetch --force é executado.

Há dois parâmetros de repository_rule que controlam quando os repositórios são buscados novamente:

  • Se a flag configure for definida, o repositório só vai ser buscado novamente em bazel fetch quando o parâmetro --configure for transmitido para ele. Se o atributo não for definido, esse comando não vai causar uma nova busca.
  • Se a sinalização local estiver definida, além dos casos acima, o repositório também será buscado novamente quando o servidor do Bazel for reiniciado.

Como reiniciar a função de implementação

A função de implementação poderá ser reiniciada enquanto um repositório estiver sendo buscado se uma dependência solicitada estiver ausente. Nesse caso, a execução da função de implementação será interrompida, a dependência ausente será resolvida e a função será executada novamente após a resolução. Para evitar reinicializações desnecessárias (que são caras porque o acesso à rede pode precisar ser repetido), os argumentos de rótulo são pré-buscados, desde que todos eles possam ser resolvidos em um arquivo atual. Observe que resolver um caminho de uma string ou um rótulo criado apenas durante a execução da função ainda pode causar uma reinicialização.

Como forçar uma nova busca de repositórios externos

Às vezes, um repositório externo pode ficar desatualizado sem qualquer mudança na definição ou nas dependências dele. Por exemplo, um repositório que busca origens pode seguir uma ramificação específica de um repositório de terceiros, e novas confirmações estão disponíveis nessa ramificação. Nesse caso, é possível solicitar que o Bazel refaça a busca de todos os repositórios externos incondicionalmente, chamando bazel fetch --force --all.

Além disso, algumas regras de repositório inspecionam a máquina local e podem ficar desatualizadas se a máquina local tiver sido atualizada. Aqui, você pode pedir ao Bazel para rebuscar apenas os repositórios externos em que a definição repository_rule tem o conjunto de atributos configure. Use bazel fetch --all --configure.

Exemplos

  • Conjunto de ferramentas configurado automaticamente para C++: ele usa uma regra repo para criar automaticamente os arquivos de configuração C++ para o Bazel. Para isso, ele procura o compilador C++ local, o ambiente e as flags que o compilador C++ aceita.

  • Os repositórios do Go usam vários repository_rule para definir a lista de dependências necessárias para usar as regras do Go.

  • rules_jvm_external cria um repositório externo chamado @maven por padrão, que gera destinos de build para cada artefato Maven na árvore de dependências transitiva.