Arquivo de bloqueio do Bazel

Informar um problema Acessar fonte

O recurso de arquivo de bloqueio no Bazel permite a gravação de versões ou dependências específicas de bibliotecas ou pacotes de software exigidos por um projeto. Para isso, armazena o resultado da resolução do módulo e da avaliação da extensão. O arquivo de bloqueio promove builds reproduzíveis, garantindo ambientes de desenvolvimento consistentes. Além disso, ele aumenta a eficiência do build, permitindo que o Bazel pule o processo de resolução quando não houver mudanças nas dependências do projeto. Além disso, o arquivo de bloqueio melhora a estabilidade ao impedir atualizações inesperadas ou alterações interruptivas em bibliotecas externas, reduzindo o risco de introduzir bugs.

Geração de arquivo de bloqueio

O arquivo de bloqueio é gerado na raiz do espaço de trabalho com o nome MODULE.bazel.lock. Ele é criado ou atualizado durante o processo de build, especificamente após a resolução do módulo e a avaliação da extensão. Ele captura o estado atual do projeto, incluindo o arquivo MODULE, sinalizações, substituições e outras informações relevantes. É importante ressaltar que ele inclui apenas as dependências incluídas na invocação atual do build.

Quando ocorrem mudanças no projeto que afetam as dependências, o arquivo de bloqueio é atualizado automaticamente para refletir o novo estado. Isso garante que o arquivo de bloqueio permaneça focado no conjunto específico de dependências necessárias para o build atual, fornecendo uma representação precisa das dependências resolvidas do projeto.

Uso do arquivo de bloqueio

O arquivo de bloqueio pode ser controlado pela sinalização --lockfile_mode para personalizar o comportamento do Bazel quando o estado do projeto for diferente do arquivo de bloqueio. Os modos disponíveis são:

  • update (padrão): se o estado do projeto corresponder ao arquivo de bloqueio, o resultado da resolução será imediatamente retornado do arquivo de bloqueio. Caso contrário, a resolução será executada e o arquivo de bloqueio será atualizado para refletir o estado atual.
  • error: se o estado do projeto corresponder ao arquivo de bloqueio, o resultado da resolução será retornado do arquivo de bloqueio. Caso contrário, o Bazel gera um erro indicando as variações entre o projeto e o arquivo de bloqueio. Esse modo é particularmente útil quando você quer garantir que as dependências do projeto permaneçam inalteradas e que todas as diferenças sejam tratadas como erros.
  • off: o arquivo de bloqueio não é marcado.

Benefícios do Lockfile

O arquivo de bloqueio oferece vários benefícios e pode ser usado de várias maneiras:

  • Builds reproduzíveis. Ao capturar as versões ou dependências específicas das bibliotecas de software, o arquivo de bloqueio garante que os builds sejam reproduzíveis em diferentes ambientes e ao longo do tempo. Os desenvolvedores podem confiar em resultados consistentes e previsíveis ao criar projetos.

  • Pular resolução eficiente. O arquivo de bloqueio permite que o Bazel pule o processo de resolução se não houver mudanças nas dependências do projeto desde a última compilação. Isso melhora significativamente a eficiência do build, especialmente em cenários em que a resolução pode ser demorada.

  • Estabilidade e redução de riscos. O arquivo de bloqueio ajuda a manter a estabilidade, impedindo atualizações inesperadas ou alterações interruptivas em bibliotecas externas. Ao bloquear as dependências para versões específicas, o risco de introduzir bugs devido a atualizações incompatíveis ou não testadas é reduzido.

Conteúdo do arquivo de bloqueio

O arquivo de bloqueio contém todas as informações necessárias para determinar se o estado do projeto foi alterado. Ele também inclui o resultado da criação do projeto no estado atual. O arquivo de bloqueio consiste em duas partes principais:

  1. Entradas da resolução do módulo, como moduleFileHash, flags e localOverrideHashes, bem como a saída da resolução, que é moduleDepGraph.
  2. Para cada extensão de módulo, o arquivo de bloqueio inclui entradas que o afetam, representadas por transitiveDigest, e a saída da execução dessa extensão chamada de generatedRepoSpecs.

Este é um exemplo que demonstra a estrutura do arquivo de bloqueio com explicações para cada seção:

{
  "lockFileVersion": 1,
  "moduleFileHash": "b0f47b98a67ee15f9.......8dff8721c66b721e370",
  "flags": {
    "cmdRegistries": [
      "https://bcr.bazel.build/"
    ],
    "cmdModuleOverrides": {},
    "allowedYankedVersions": [],
    "envVarAllowedYankedVersions": "",
    "ignoreDevDependency": false,
    "directDependenciesMode": "WARNING",
    "compatibilityMode": "ERROR"
  },
  "localOverrideHashes": {
    "bazel_tools": "b5ae1fa37632140aff8.......15c6fe84a1231d6af9"
  },
  "moduleDepGraph": {
    "<root>": {
      "name": "",
      "version": "",
      "executionPlatformsToRegister": [],
      "toolchainsToRegister": [],
      "extensionUsages": [
        {
          "extensionBzlFile": "extension.bzl",
          "extensionName": "lockfile_ext"
        }
      ],
      ...
    }
  },
  "moduleExtensions": {
    "//:extension.bzl%lockfile_ext": {
      "general": {
        "transitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      }
    },
    "//:extension.bzl%lockfile_ext2": {
      "os:macos": {
        "transitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      },
      "os:linux": {
        "transitiveDigest": "eWDzxG/aLsyY3Ubrto....+Jp4maQvEPxn0pLK=",
        "generatedRepoSpecs": {
          "hello": {
            "bzlFile": "@@//:extension.bzl",
            ...
          }
        }
      }
    }
  }
}

Hash de arquivo do módulo

O moduleFileHash representa o hash do conteúdo do arquivo MODULE.bazel. Se o arquivo mudar, o valor de hash será diferente.

Sinalizações

O objeto Flags armazena todas as sinalizações que podem afetar o resultado da resolução.

Hashes de substituição locais

Se o módulo raiz incluir local_path_overrides, esta seção armazenará o hash do arquivo MODULE.bazel no repositório local. Ela permite rastrear mudanças nessa dependência.

Gráfico de dependência do módulo

O moduleDepGraph representa o resultado do processo de resolução usando as entradas mencionadas acima. Ele forma o gráfico de dependência de todos os módulos necessários para executar o projeto.

Extensões do módulo

A seção moduleExtensions é um mapa que inclui apenas as extensões usadas na invocação atual ou anteriormente invocadas, excluindo as extensões que não são mais usadas. Em outras palavras, se uma extensão não estiver mais sendo usada no gráfico de dependência, ela será removida do mapa moduleExtensions.

Se uma extensão for independente do sistema operacional ou do tipo de arquitetura, esta seção apresentará apenas uma única entrada "geral". Caso contrário, várias entradas serão incluídas, nomeadas com base no SO, na arquitetura ou em ambas, cada uma correspondente ao resultado da avaliação da extensão nesses detalhes.

Cada entrada no mapa de extensão corresponde a uma extensão usada e é identificada pelo arquivo e pelo nome dela. O valor correspondente para cada entrada contém as informações relevantes associadas a essa extensão:

  1. O transitiveDigest, o resumo da implementação de extensão e os respectivos arquivos .bzl transitivos.
  2. O generatedRepoSpecs é o resultado da execução dessa extensão com a entrada atual.

Outro fator que pode afetar os resultados da extensão é o uso delas. Embora não sejam armazenados no arquivo de bloqueio, os usos são considerados ao comparar o estado atual da extensão com o que está no arquivo de bloqueio.

Práticas recomendadas

Para maximizar os benefícios do recurso de arquivo de bloqueio, siga estas práticas recomendadas:

  • Atualizar regularmente o arquivo de bloqueio para refletir as alterações nas dependências ou na configuração do projeto. Isso garante que os builds futuros sejam baseados no conjunto de dependências mais atualizado e preciso.

  • Inclua o arquivo de bloqueio no controle de versões para facilitar a colaboração e garantir que todos os membros da equipe tenham acesso ao mesmo arquivo de bloqueio, promovendo ambientes de desenvolvimento consistentes em todo o projeto.

  • Use bazelisk para executar o Bazel e inclua um arquivo .bazelversion no controle de versões que especifique a versão do Bazel correspondente ao arquivo de bloqueio. Como o próprio Bazel é uma dependência da sua compilação, o arquivo de bloqueio é específico para a versão do Bazel e muda mesmo entre versões compatíveis com versões anteriores do Bazel. O uso de bazelisk garante que todos os desenvolvedores estejam usando uma versão do Bazel que corresponda ao arquivo de bloqueio.

Seguindo estas práticas recomendadas, é possível utilizar efetivamente o recurso de arquivo de bloqueio no Bazel, levando a fluxos de trabalho de desenvolvimento de software mais eficientes, confiáveis e colaborativos.