cargo-raze é atualmente um produto sem manutenção e sem suporte .
crate_universe
em rules_rust
é um produto suportado e mantido com objetivos semelhantes.
Acreditamos que crate_universe
suporta todas as funcionalidades suportadas pelo cargo-raze. Nós encorajamos você a tentar migrar para ele. Se você encontrar suporte ausente ou bugs, registre um problema ou PR contra rules_rust
.
cargo-raze
foi arquivado para refletir a realidade de que é improvável que PRs e questões apresentadas contra ele sejam abordadas.
Entre em contato com #rust no Bazel Slack se estiver interessado em assumir a manutenção deste projeto.
Um plugin Cargo de suporte experimental para destilar um Cargo.toml em nível de espaço de trabalho em alvos BUILD dos quais o código usando regras_rust pode depender diretamente.
Este não é um produto oficial do Google (experimental ou não), é apenas um código que pertence ao Google.
Este projeto sintetiza a lógica de resolução de dependências e algumas das funcionalidades do Cargo, como recursos e scripts de construção em regras executáveis que o Bazel pode executar para compilar caixas Rust. Embora as regras regras_rust padrão possam ser usadas para compilar o código Rust do zero, a fina granularidade do ecossistema de dependências torna onerosa a transformação de árvores de dependências com base nesse ecossistema, mesmo para códigos com poucas dependências.
cargo-raze pode gerar alvos edificáveis em um dos dois modos: Venda ou Não Venda. No modo de venda, os desenvolvedores usam o subcomando comum cargo vendor
para recuperar as dependências indicadas por seu espaço de trabalho Cargo.toml em diretórios que cargo-raze então preenche com arquivos BUILD. No modo sem venda, cargo-raze gera uma lista simples de arquivos BUILD e uma macro em nível de espaço de trabalho que pode ser invocada no arquivo WORKSPACE para baixar as dependências automaticamente de maneira semelhante ao próprio Cargo.
Em ambos os casos, o primeiro passo é decidir onde situar as dependências de Carga no espaço de trabalho. Esta biblioteca foi projetada pensando em monorepos, onde uma organização decide sobre um conjunto de dependências que todos apontam. Pretende-se que as partes interessadas nas dependências colaborem para atualizar as dependências atomicamente e corrigir falhas em sua base de código simultaneamente. Caso isso não seja viável, ainda é possível usar o cargo-raze em um cenário descentralizado, mas é improvável que tais repositórios dissociados interajam bem com a implementação atual.
Independentemente da abordagem escolhida, as Rust_rules devem ser trazidas para o WORKSPACE. Aqui está um exemplo:
load ( "@bazel_tools//tools/build_defs/repo:http.bzl" , "http_archive" )
http_archive (
name = "rules_rust" ,
sha256 = "accb5a89cbe63d55dcdae85938e56ff3aa56f21eb847ed826a28a83db8500ae6" ,
strip_prefix = "rules_rust-9aa49569b2b0dacecc51c05cee52708b7255bd98" ,
urls = [
# Main branch as of 2021-02-19
"https://github.com/bazelbuild/rules_rust/archive/9aa49569b2b0dacecc51c05cee52708b7255bd98.tar.gz" ,
],
)
load ( "@rules_rust//rust:repositories.bzl" , "rust_repositories" )
rust_repositories ( edition = "2018" )
Para projetos apenas do Bazel, os usuários devem primeiro gerar um Cargo.toml padrão com as dependências de interesse. Tome cuidado para incluir uma diretiva [lib]
para que o Cargo não reclame da falta de arquivos de origem para esta caixa simulada. Aqui está um exemplo:
[ package ]
name = " compile_with_bazel "
version = " 0.0.0 "
# Mandatory (or Cargo tooling is unhappy)
[ lib ]
path = " fake_lib.rs "
[ dependencies ]
log = " =0.3.6 "
Assim que o Cargo.toml padrão estiver em vigor, adicione as diretivas [package.metadata.raze]
conforme a próxima seção.
Quase todas as configurações canônicas de carga devem ser capazes de funcionar no local com cargo-raze
. Supondo que o espaço de trabalho Cargo agora esteja aninhado em um espaço de trabalho Bazel, os usuários podem simplesmente adicionar RazeSettings aos seus arquivos Cargo.toml para serem usados para gerar arquivos Bazel
# Above this line should be the contents of your Cargo.toml file
[ package . metadata . raze ]
# The path at which to write output files.
#
# `cargo raze` will generate Bazel-compatible BUILD files into this path.
# This can either be a relative path (e.g. "foo/bar"), relative to this
# Cargo.toml file; or relative to the Bazel workspace root (e.g. "//foo/bar").
workspace_path = " //cargo "
# This causes aliases for dependencies to be rendered in the BUILD
# file located next to this `Cargo.toml` file.
package_aliases_dir = " . "
# The set of targets to generate BUILD rules for.
targets = [
" x86_64-apple-darwin " ,
" x86_64-pc-windows-msvc " ,
" x86_64-unknown-linux-gnu " ,
]
# The two acceptable options are "Remote" and "Vendored" which
# is used to indicate whether the user is using a non-vendored or
# vendored set of dependencies.
genmode = " Remote "
Em projetos que usam espaços de trabalho de carga, os usuários devem organizar todas as suas configurações raze
no campo [workspace.metadata.raze]
no arquivo Cargo.toml
de nível superior que contém a definição [workspace]
. Essas configurações devem ser idênticas às vistas em [package.metadata.raze]
na seção anterior. No entanto, as configurações da caixa ainda podem ser colocadas nos arquivos Cargo.toml
dos membros do espaço de trabalho:
# Above this line should be the contents of your package's Cargo.toml file
# Note that `some-dependency` is the name of an example dependency and
# `<0.3.0` is a semver version for the dependency crate's version. This
# should always be compaitble in some way with the dependency version
# specified in the `[dependencies]` section of the package defined in
# this file
[ package . metadata . raze . crates . some-dependency . '<0 . 3 . 0' ]
additional_flags = [
" --cfg=optional_feature_a " ,
" --cfg=optional_feature_b " ,
]
# This demonstrates that multiple crate settings may be defined.
[ package . metadata . raze . crates . some-other-dependency . '*' ]
additional_flags = [
" --cfg=special_feature " ,
]
No modo Remoto, um diretório semelhante ao modo de venda é selecionado. Neste caso, porém, ele contém apenas arquivos BUILD, uma instrução de venda para o WORKSPACE e apelidos para as dependências explícitas. É necessário um encanamento ligeiramente diferente.
Isso diz ao Raze para não esperar que as dependências sejam vendidas e gerem arquivos diferentes.
Primeiro, instale o cargo-raze.
$ cargo install cargo-raze
Em seguida, execute cargo raze de dentro do diretório cargo
$ cargo raze
Por fim, invoque a função de busca remota de biblioteca em seu WORKSPACE:
load ( "//cargo:crates.bzl" , "raze_fetch_remote_crates" )
# Note that this method's name depends on your gen_workspace_prefix setting.
# `raze` is the default prefix.
raze_fetch_remote_crates ()
Isso informa ao Bazel de onde obter as dependências e como construí-las: usando os arquivos gerados em //cargo
.
Você pode depender de qualquer dependência explícita em qualquer regra Rust dependendo de //cargo:your_dependency_name
.
No modo Vendoring, é selecionada diretamente uma raiz que abrigará as dependências do fornecedor e se tornará a porta de entrada para essas regras de construção. //cargo
é convencional, mas //third_party/cargo
pode ser desejável para satisfazer necessidades organizacionais. A venda direta na raiz não é bem suportada devido a idiossincrasias específicas da implementação, mas pode ser suportada no futuro. De agora em diante, //cargo
será o diretório assumido.
Primeiro, instale as ferramentas necessárias para vender e gerar alvos CONSTRUÍDOS.
$ cargo install cargo-raze
Depois disso, venda suas dependências no diretório cargo/. Isso também atualizará seu arquivo Cargo.lock
.
$ cargo vendor --versioned-dirs
Finalmente, gere seus arquivos BUILD, novamente no diretório cargo/
$ cargo raze
Agora você pode depender de quaisquer dependências explícitas em qualquer regra Rust dependendo de //cargo:your_dependency_name
.
Cargo-raze pode ser construído inteiramente no Bazel e usado sem a necessidade de configurar carga na máquina host. Para fazer isso, basta adicionar o seguinte ao arquivo WORKSPACE do seu projeto:
load ( "@bazel_tools//tools/build_defs/repo:http.bzl" , "http_archive" )
http_archive (
name = "cargo_raze" ,
sha256 = "c664e258ea79e7e4ec2f2b57bca8b1c37f11c8d5748e02b8224810da969eb681" ,
strip_prefix = "cargo-raze-0.11.0" ,
url = "https://github.com/google/cargo-raze/archive/v0.11.0.tar.gz" ,
)
load ( "@cargo_raze//:repositories.bzl" , "cargo_raze_repositories" )
cargo_raze_repositories ()
load ( "@cargo_raze//:transitive_deps.bzl" , "cargo_raze_transitive_deps" )
cargo_raze_transitive_deps ()
Com isso implementado, os usuários podem executar o destino @cargo_raze//:raze
para gerar novos arquivos BUILD. por exemplo:
bazel run @cargo_raze//:raze -- --manifest-path= $( realpath /Cargo.toml )
Observe que os usuários que usam o genmode vendored
ainda terão que vender suas dependências de alguma forma, já que cargo-raze
atualmente não faz isso para você.
Algumas caixas executam um "script de construção", que, embora tecnicamente irrestrito no que pode fazer, geralmente faz uma das poucas coisas comuns.
Todas as opções indicadas abaixo são enumeradas no arquivo src/settings.rs.
Em alguns casos, uma caixa usa apenas informações básicas para gerar um arquivo fonte Rust. Essas regras de scripts de construção podem ser executadas e usadas no Bazel incluindo uma diretiva em seu Cargo.toml antes da geração:
[ package . metadata . raze . crates . clang-sys . '0 . 21 . 1' ]
gen_buildrs = true
Esta configuração diz ao cargo-raze para gerar um destino rust_binary para o script de construção e direcionar suas saídas geradas (estilo OUT_DIR) para a caixa pai.
Alguns scripts de construção emitem diretivas condicionalmente para informar que o Cargo sabe como se propagar. Infelizmente, não é tão simples gerenciar informações de dependência geradas em tempo de construção, portanto, se os sinalizadores forem estaticamente conhecidos (talvez, já que o destino da compilação é estaticamente conhecido), eles poderão ser fornecidos de dentro do Cargo.toml, da seguinte maneira
[ package . metadata . raze . crates . unicase . '2 . 1 . 0' ]
additional_flags = [
# Rustc is 1.15, enable all optional settings
" --cfg=__unicase__iter_cmp " ,
" --cfg=__unicase__defauler_hasher " ,
]
As bandeiras fornecidas desta forma são entregues diretamente ao Rustc. Pode ser útil consultar a seção build-script da documentação para interpretar scripts de construção e diretivas stdout encontradas, disponíveis aqui: https://doc.rust-lang.org/cargo/reference/build-scripts.html
Existem duas maneiras de fornecer bibliotecas de sistema que uma caixa precisa para compilação. A primeira é vender a biblioteca do sistema diretamente, criar uma regra BUILD para ela e adicionar a dependência à caixa -sys
correspondente. Para o openssl, isso pode ser parcialmente parecido com:
[ package . metadata . raze . crates . openssl-sys . '0 . 9 . 24' ]
additional_flags = [
# Vendored openssl is 1.0.2m
" --cfg=ossl102 " ,
" --cfg=version=102 " ,
]
additional_deps = [
" @//third_party/openssl:crypto " ,
" @//third_party/openssl:ssl " ,
]
[ package . metadata . raze . crates . openssl . '0 . 10 . 2' ]
additional_flags = [
# Vendored openssl is 1.0.2m
" --cfg=ossl102 " ,
" --cfg=version=102 " ,
" --cfg=ossl10x " ,
]
Em alguns casos, pode ser preferível conectar diretamente uma dependência do sistema local. Para fazer isso, consulte a seção new_local_repository
da documentação do Bazel. Para uma versão pré-compilada do llvm em um WORKSPACE, isso pode ser algo como:
new_local_repository (
name = "llvm" ,
build_file = "BUILD.llvm.bazel" ,
path = "/usr/lib/llvm-3.9" ,
)
Em alguns casos, a caixa do sistema pode precisar ser totalmente substituída. Isso pode ser facilitado removendo e complementando dependências no Cargo.toml, pré-geração:
[ package . metadata . raze . crates . sdl2 . '0 . 31 . 0' ]
skipped_deps = [
" sdl2-sys-0.31.0 "
]
additional_deps = [
" @//cargo/overrides/sdl2-sys:sdl2_sys "
]
Algumas caixas fornecem binários úteis que podem ser usados como parte de um processo de compilação: Bindgen é um ótimo exemplo. Bindgen produz arquivos de origem Rust processando arquivos C ou C++. Uma diretiva pode ser adicionada ao Cargo.toml para instruir o Bazel a expor esses binários para você:
[ package . metadata . raze . crates . bindgen . '0 . 32 . 2' ]
gen_buildrs = true # needed to build bindgen
extra_aliased_targets = [
" cargo_bin_bindgen "
]
Cargo-raze prefixa alvos binários com cargo_bin_
, pois embora Cargo permita que binários e bibliotecas compartilhem o mesmo nome de destino, o Bazel não permite isso.
Atualmente, cargo não coleta metadados sobre caixas que não fornecem nenhuma biblioteca. Isso significa que especificá-los na seção [dependencies]
do seu arquivo Cargo.toml
não resultará em destinos Bazel gerados. Cargo-raze tem um campo especial para lidar com essas caixas ao usar genmode = "Remote"
:
[ package . metadata . raze . binary_deps ]
wasm-bindgen-cli = " 0.2.68 "
No trecho acima, a caixa wasm-bindgen-cli
é definida como dependência binária e Cargo-raze garantirá que os metadados para esta e qualquer outra caixa definida aqui sejam incluídos no diretório de saída resultante. Lockfiles para destinos especificados em [package.metadata.raze.binary_deps]
serão gerados em um diretório lockfiles
dentro do caminho especificado por workspace_path
.
Observe que o campo binary_deps
pode estar no espaço de trabalho e nos metadados do pacote; no entanto, apenas uma definição de dependência binária pode existir por vez. Se você tiver vários pacotes que dependem de uma única dependência binária, essa definição precisará ser movida para os metadados do espaço de trabalho.
Definir default_gen_buildrs como true fará com que cargo-raze gere scripts de construção para todas as caixas que os exigem:
[ package . metadata . raze ]
workspace_path = " //cargo "
genmode = " Remote "
default_gen_buildrs = true
Essa configuração é uma compensação entre conveniência e correção. Ao ativá-lo, você encontrará muitas caixas funcionando sem precisar especificar nenhum sinalizador explicitamente e sem precisar ativar manualmente scripts de construção individuais. Mas, ao ativá-lo, você permite que todas as caixas que está usando executem código arbitrário no momento da construção, e as ações que elas executam podem não ser herméticas.
Mesmo com esta configuração habilitada, você ainda pode precisar fornecer configurações extras para algumas caixas. Por exemplo, a caixa do anel precisa de acesso à árvore de origem no momento da construção:
[ package . metadata . raze . crates . ring . '*' ]
compile_data_attr = " glob([ " **/*.der " ]) "
Se desejar desabilitar o script de construção em uma caixa individual, você pode fazer o seguinte:
[ package . metadata . raze . crates . some_dependency . '*' ]
gen_buildrs = false
Bazel ("rápido", "correto", escolha dois) é um sistema de compilação testado em batalha usado pelo Google para compilar projetos multilíngues incrivelmente grandes sem duplicar esforço e sem comprometer a correção. Ele consegue isso em parte limitando quais mecanismos um determinado objeto de compilação pode usar para descobrir dependências e forçando unidades compiláveis a expressar o conjunto completo de suas dependências. Ele espera que dois conjuntos idênticos de entradas de destino de construção produzam um resultado final equivalente byte por byte.
Em troca, os usuários são recompensados com um sistema de construção personalizável e extensível que compila qualquer tipo de alvo compilável e permite expressar "dependências não convencionais", como objetos Protobuf, shaders gráficos pré-compilados ou código gerado, mantendo-se rápido e correto.
Também é provável (embora ainda não demonstrado com benchmarks) que grandes aplicativos construídos com os pontos fortes do Bazel em mente: unidades de construção altamente granulares, compilem significativamente mais rápido, pois são capazes de armazenar em cache de forma mais agressiva e evitar a recompilação de tanto código durante a iteração.
Para o bem ou para o mal, o ecossistema Rust depende fortemente de caixas de carga para fornecer funcionalidades que estão frequentemente presentes em bibliotecas padrão. Na verdade, isso é fantástico para a evolução da linguagem, pois descreve um processo estruturado de estabilização (caixa experimental -> caixa 1.0 -> RFC -> inclusão em stdlib), mas significa que as pessoas que não têm acesso a este ecossistema devem reinventar muitas rodas.
Deixando isso de lado, também existem caixas fantásticas que ajudam os desenvolvedores do Rust a interagir com sistemas e bibliotecas padrão da indústria, o que pode acelerar bastante o desenvolvimento da linguagem.
Embora o fardo de emular a funcionalidade do Cargo (quando possível!) seja alto, parece ser a única maneira de manter as garantias (correção, reprodutibilidade) das quais o Bazel depende para manter o desempenho. É possível e provável com RFCs em voo que o Cargo se torne suficientemente flexível para permitir que seja usado diretamente para compilação, mas neste momento parece que manter uma aparência de paridade de recursos é na verdade mais fácil do que evitar todas as arestas vivas introduzidas por tratando Cargo como o compilador Rust.
Com um pouco de esforço é possível construir quase tudo, inclusive projetos que dependem do openssl-sys. Muitas caixas de sistema exigirão a identificação da biblioteca do sistema que eles agrupam e a venda no projeto ou a informação ao Bazel onde ela reside em seu sistema. Alguns podem exigir pequenos ajustes na fonte, como a eliminação de requisitos de variáveis de ambiente de carga codificados. As correções podem não ser triviais em alguns casos, mas um bom número das caixas mais populares foram construídas em um repositório de exemplo, disponível em https://github.com/acmcarther/cargo-raze-crater
Veja estes exemplos de fornecimento de configuração de caixa:
Usando o modo fornecedor :
Usando o modo remoto :
Compilando OpenSSL :
A seção [package.metadata.raze]
é derivada de uma estrutura declarada em impl/src/settings.rs.