build.rs
O que é o build.rs
? O Cargo o executa automaticamente?
Se você colocar um arquivo chamado build.rs
na raiz do seu projeto (no mesmo nível que o Cargo.toml
), o Cargo irá executá-lo automaticamente antes de compilar sua crate.
Sim, o Cargo executa qualquer
build.rs
, é uma convenção.
O Cargo segue algumas convenções de nomenclatura e organização para entender a estrutura do seu projeto:
Arquivo | Propósito |
---|---|
src/main.rs |
Binário principal |
src/lib.rs |
Lib principal |
tests/*.rs |
Testes de integração |
examples/*.rs |
Binários de exemplos de uso |
benches/*.rs |
Benchmarks (*) |
build.rs |
Script de pré-compilação |
O Cargo procura por um script build.rs
na raiz do projeto. Se existir, ele:
build.rs
em um executável standalone.Ele é chamado de custom build script, e lhe dá maior controle sobre a compilação do seu projeto.
build.rs
?Você pode:
OUT_DIR
Os arquivos gerados devem ser gravados na pasta especificada pela variável de ambiente OUT_DIR
.
Aqui está um exemplo:
build.rs
use std::env;
use std::fs;
use std::path::Path;
use chrono::Utc;
fn main() {
// Obtém a pasta de saída (criada pelo Cargo)
let dir_saida = env::var("OUT_DIR").expect("Pasta de saída não criada pelo Cargo");
let caminho_saida = Path::new(&dir_saida).join("saudacao.rs");
let now = Utc::now();
let nome = "Fulano";
// Gera código Rust com a saudação
let saudacao = format!(
r#"
pub fn saudar() -> &'static str {{
"Ola, {}! Este código foi criado em {}." }}
"#,
nome,
now.format("%d/%m/%Y %H:%M:%S")
);
// Grava na saída
fs::write(&caminho_saida, saudacao)
.expect("Falhou ao gerar o codigo de saudaçao");
// Avisa o Cargo para recompilar se build.rs mudar
println!("cargo:rerun-if-changed=build.rs");
}
src/main.rs
include!(concat!(env!("OUT_DIR"), "/saudacao.rs"));
fn main() {
println!("{}", saudar());
}
Isto mantém o código gerado na pasta
target/
, fora da sua pastasrc
, evitando problemas com controle de versão e assegurando compilações limpas.
Se você rodar cargo run
verá esse resultado:
Olá, Fulano! Este código foi criado em 05/08/2025 19:09:45.
Esta mensagem foi criada pela função saudar()
, gerada pelo build.rs
e incluída no main.rs
.
Cargo, o poderoso sistema de build e gerenciador de pacotes do Rust, não é apenas para compilar e testar. Uma das suas funcionalidades mais subestimadas é a extensibilidade: você pode criar comandos customizados para o Cargo utilizando convenções simples de nomenclatura e scripts.
Isso permite que equipes e desenvolvedores automatizem tarefas comuns, como formatação, linting, lançamento de versões ou execução de servidores de desenvolvimento, utilizando uma interface limpa e consistente: cargo <seu-comando>
.
O Cargo procura por executáveis no seu PATH
que sigam essa convenção de nomenclatura:
cargo-<subcomando>
Quando você digita no terminal:
cargo meucomando
O Cargo procura por um executável cargo-meucomando
nos caminhos da variável PATH
. Se encontrar, ele executa como se você tivesse digitado cargo-meucomando
.
Isto significa que você pode escrever comandos em qualquer linguagem, como: Rust, Bash, Python etc, desde que o executável siga a convenção de nomenclatura.
cargo-sauda
Crie um comando chamado cargo sauda
que mostra uma saudação.
cargo-sauda
sem extensão):#!/bin/bash
echo "Olá, do seu projeto Cargo!"
chmod +x cargo-sauda
PATH
, por exemplo: ~/.cargo/bin/
(recomendado):mv cargo-sauda ~/.cargo/bin/
~/.cargo/bin
é adicionado aoPATH
quando você instala Rust comrustup
.
cargo sauda
Olá, do seu projeto Cargo!
cargo-checklist
Vamos criar uma ferramenta mais avançada para validar as coisas mais comuns em qualquer projeto.
cargo new cargo-checklist --bin
cd cargo-checklist
src/main.rs
:use std::process::Command;
fn main() {
println!("Executando a validação do projeto...\n");
// Verifica a formatação do código
let fmt = Command::new("cargo").args(["fmt", "--check"]).output().unwrap();
if fmt.status.success() {
println!("OK - Código formatado corretamente");
} else {
println!("ERRO - Código não formatado corretamente");
}
// Executa o Clippy para verificar problemas de lint
let clippy = Command::new("cargo").args(["clippy", "--all-targets", "--", "-D", "warnings"]).output().unwrap();
if clippy.status.success() {
println!("OK - O Clippy não encontrou problemas");
} else {
println!("ERRO - O Clippy encontrou problemas");
}
// Executa os testes do projeto
let test = Command::new("cargo").arg("test").output().unwrap();
if test.status.success() {
println!("OK - Todos os testes passaram");
} else {
println!("ERRO - Alguns testes falharam");
}
println!("\nFim da validação!");
}
E verifique o Cargo.toml
:
[package]
name = "cargo-checklist"
version = "0.1.0"
edition = "2021"
description = "Uma ferramenta para executar validações: fmt, clippy e tests"
repository = "https://github.com/yourusername/cargo-checklist"
[dependencies]
Atenção: O nome do projeto (package name) DEVE ser
cargo-checklist
! Se utilizar outropackage name
então terá que renomear o executável criando uma seção do tipo array[[bin]]
com os atributosname
epath
.
Seções em forma de array: No TOML,
[[bin]]
(com colchetes duplos) denota um array de tabelas em vez de uma única tabela. Isso permite declarar múltiplos binários no mesmoCargo.toml
, cada um como um elemento separado do arraybin
. Se você usasse[bin]
(com colchetes simples), obteria apenas uma única tabela chamadabin
, e as declarações subsequentes sobrescreveriam a anterior. Portanto, sempre que quiser listar mais de um executável (ou mesmo apenas um, seguindo o padrão do Cargo), você deve usar[[bin]]
. O Cargo iterará sobre cada item desse array e gerará um binário para cada um.
cargo install --path .
Este comando compila e instala o executável cargo-checklist
na pasta ~/.cargo/bin
.
cargo checklist
Exemplo de saída:
cargo checklist
Executando a validação do projeto...
OK - Código formatado corretamente
OK - O Clippy não encontrou problemas
OK - Todos os testes passaram
Fim da validação!
Em certos casos, talvez você não queira instalar os comandos de forma global. Neste caso, você pode criar scripts locais utilizando uma pasta scripts/
e um código wrapper.
Exemplo: scripts/cargo-dev-server
#!/bin/bash
echo "Starting dev server..."
cargo watch -x "run"
Torne-o executável:
./scripts/cargo-dev-server
Dica: Use
just
(https://github.com/casey/just) oucargo-make
para executar tarefas de forma mais robusta.
Alguns comandos Cargo populares foram implementados assim:
cargo fmt
→ rustfmt
(via cargo-fmt
)cargo clippy
→ clippy
(via cargo-clippy
)cargo watch
→ external binarycargo sqlx
→ cargo-sqlx
Você pode até listar os comandos Cargo disponíveis:
cargo --list
Output:
Available commands:
build Compile a local package and all of its dependencies
check Check a local package and all of its dependencies for errors
clean Remove artifacts that cargo has generated in the past
...
greet (from ~/.cargo/bin/cargo-greet)
checklist (from ~/.cargo/bin/cargo-checklist)
cargo-<command>
.~/.cargo/bin
.cargo install --path .
para comandos.