Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "2"
members = ["crates/*"]

[workspace.package]
version = "0.1.25"
version = "0.1.26"
repository = "https://github.com/human-solutions/builder"
license = "MIT"
edition = "2024"
Expand Down
30 changes: 23 additions & 7 deletions crates/assemble/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,61 @@ use asset_ext::AssetExt;
use builder_command::AssembleCmd;
use camino_fs::*;
use common::site_fs::parse_site;
use common::{Timer, log_command, log_operation, log_trace};
use generator::generate_code;
use std::process::Command;
use tempfile::NamedTempFile;

pub fn run(cmd: &AssembleCmd) {
log::info!("Running builder-assemble");
let _timer = Timer::new("ASSEMBLE processing");
log_command!("ASSEMBLE", "Processing site root: {}", cmd.site_root);

let assets = parse_site(&cmd.site_root).unwrap();
log_operation!("ASSEMBLE", "Found {} assets", assets.len());

let out = generate_code(&assets);
log_operation!("ASSEMBLE", "Generated {} bytes of code", out.len());

let tmp_file = NamedTempFile::new().unwrap();
let tmp_path = Utf8PathBuf::from_path(tmp_file.path()).unwrap();
tmp_path.write(out).unwrap();

log::debug!("Formatting {tmp_path}");
log_operation!("ASSEMBLE", "Formatting generated code with rustfmt");
let status = Command::new("rustfmt").arg(&tmp_path).status().unwrap();
if !status.success() {
log::warn!("Failed to format {tmp_path}");
common::warn_cargo!("ASSEMBLE: rustfmt failed, using unformatted code");
} else {
log_operation!("ASSEMBLE", "Code formatting successful");
}

let formatted = tmp_path.read_bytes().unwrap();
if let Some(code_file) = &cmd.code_file {
if code_file.exists() {
let current = code_file.read_bytes().unwrap();
if current == formatted {
log::info!("No change detected, skipping {}", code_file);
log_command!("ASSEMBLE", "No changes detected, skipping code file write");
return;
}
} else if let Some(parent) = code_file.parent() {
parent.mkdirs().unwrap();
log_operation!("ASSEMBLE", "Code file changed, updating: {}", code_file);
} else {
if let Some(parent) = code_file.parent() {
log_trace!("ASSEMBLE", "Creating parent directory: {}", parent);
parent.mkdirs().unwrap();
}
log_operation!("ASSEMBLE", "Creating new code file: {}", code_file);
}
code_file.write(formatted).unwrap();
}
if let Some(url_env_file) = &cmd.url_env_file {
let mut envs = assets.iter().map(|a| a.url_const()).collect::<Vec<_>>();
envs.sort();

log::info!("Writing URL envs to {url_env_file}");
log_operation!(
"ASSEMBLE",
"Writing {} URL constants to: {}",
envs.len(),
url_env_file
);
url_env_file.write(envs.join("\n")).unwrap();
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/builder/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::env;

use builder_command::{BuilderCmd, Cmd};
use camino_fs::*;
use common::{RELEASE, VERBOSE, setup_logging};
use common::{LOG_LEVEL, RELEASE, setup_logging};

fn main() {
let args = std::env::args().collect::<Vec<_>>();
Expand All @@ -22,8 +22,8 @@ fn main() {

RELEASE.set(builder.release).unwrap();

setup_logging(builder.verbose);
VERBOSE.set(builder.verbose).unwrap();
setup_logging(builder.log_level, builder.log_destination.clone());
LOG_LEVEL.set(builder.log_level).unwrap();

let bin_version = env!("CARGO_PKG_VERSION");
let metadata = cargo_metadata::MetadataCommand::new().exec().unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/command/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ version.workspace = true

[dependencies]
camino-fs.workspace = true
log.workspace = true
124 changes: 113 additions & 11 deletions crates/command/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,43 @@ use camino_fs::Utf8PathBuf;
pub use copy::CopyCmd;
pub use fontforge::FontForgeCmd;
pub use localized::LocalizedCmd;
use log::LevelFilter;
pub use out::{Encoding, Output};
pub use sass::SassCmd;
use std::fs;
pub use swift_package::SwiftPackageCmd;
pub use uniffi::UniffiCmd;
pub use wasm::{DebugSymbolsMode, Profile, WasmProcessingCmd};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Normal, // Info level + enhanced summaries
Verbose, // Debug + detailed operations
Trace, // Everything including file-level operations
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LogDestination {
Cargo, // via cargo::warning
File(Utf8PathBuf), // given a path
Terminal, // standard output
TerminalPlain, // standard output, designed for when run in a Command that adds it's own prefixes to the logs
}

impl LogLevel {
pub fn to_level_filter(self) -> LevelFilter {
match self {
LogLevel::Normal => LevelFilter::Info,
LogLevel::Verbose => LevelFilter::Debug,
LogLevel::Trace => LevelFilter::Trace,
}
}
}

#[derive(Debug, PartialEq)]
pub struct BuilderCmd {
pub verbose: bool,
pub log_level: LogLevel,
pub log_destination: LogDestination,
pub release: bool,
/// The directory where the builder.toml file is located
/// Defaults to env OUT_DIR
Expand All @@ -41,9 +68,16 @@ impl Default for BuilderCmd {

impl BuilderCmd {
pub fn new() -> Self {
let default_log_destination = if env::var("CI").is_ok() {
LogDestination::Cargo
} else {
LogDestination::Terminal
};

Self {
cmds: Vec::new(),
verbose: false,
log_level: LogLevel::Normal,
log_destination: default_log_destination,
release: env::var("PROFILE").unwrap_or_default() == "release",
in_cargo: env::var("CARGO").is_ok(),
builder_toml: Utf8PathBuf::from(
Expand Down Expand Up @@ -101,8 +135,13 @@ impl BuilderCmd {
self
}

pub fn verbose(mut self, val: bool) -> Self {
self.verbose = val;
pub fn log_level(mut self, level: LogLevel) -> Self {
self.log_level = level;
self
}

pub fn log_destination(mut self, destination: LogDestination) -> Self {
self.log_destination = destination;
self
}

Expand Down Expand Up @@ -142,17 +181,30 @@ impl BuilderCmd {
}

fn log(&self, msg: &str) {
if self.verbose && self.in_cargo {
println!("cargo::warning={msg}");
} else if self.verbose {
let is_verbose = matches!(self.log_level, LogLevel::Verbose | LogLevel::Trace);
if is_verbose {
println!("{msg}");
}
}
}

impl Display for BuilderCmd {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "verbose={}", self.verbose)?;
let log_level_str = match self.log_level {
LogLevel::Normal => "normal",
LogLevel::Verbose => "verbose",
LogLevel::Trace => "trace",
};
writeln!(f, "log_level={}", log_level_str)?;

let log_destination_str = match &self.log_destination {
LogDestination::Cargo => "cargo".to_string(),
LogDestination::File(path) => format!("file:{}", path),
LogDestination::Terminal => "terminal".to_string(),
LogDestination::TerminalPlain => "terminal_plain".to_string(),
};
writeln!(f, "log_destination={}", log_destination_str)?;

writeln!(f, "release={}", self.release)?;
writeln!(f, "builder_toml={}", self.builder_toml)?;
for cmd in &self.cmds {
Expand All @@ -167,10 +219,38 @@ impl FromStr for BuilderCmd {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lines = s.lines();
let mut builder = BuilderCmd::new();
for line in lines.by_ref().take(3) {
for line in lines.by_ref().take(4) {
let (key, value) = line.split_once('=').unwrap();
match key {
"verbose" => builder.verbose = value.parse().unwrap(),
"log_level" => {
builder.log_level = match value {
"normal" => LogLevel::Normal,
"verbose" => LogLevel::Verbose,
"trace" => LogLevel::Trace,
_ => LogLevel::Normal,
};
}
"log_destination" => {
builder.log_destination = if let Some(path) = value.strip_prefix("file:") {
LogDestination::File(Utf8PathBuf::from(path))
} else {
match value {
"cargo" => LogDestination::Cargo,
"terminal" => LogDestination::Terminal,
"terminal_plain" => LogDestination::TerminalPlain,
_ => LogDestination::Terminal,
}
};
}
"verbose" => {
// Keep backward compatibility
let verbose: bool = value.parse().unwrap();
builder.log_level = if verbose {
LogLevel::Verbose
} else {
LogLevel::Normal
};
}
"release" => builder.release = value.parse().unwrap(),
"builder_toml" => builder.builder_toml = value.parse().unwrap(),
_ => panic!("Unknown key: {}", key),
Expand Down Expand Up @@ -247,11 +327,33 @@ fn roundtrip() {
.add_wasm(WasmProcessingCmd::default().debug_symbols(DebugSymbolsMode::Keep))
.add_copy(CopyCmd::default())
.add_swift_package(SwiftPackageCmd::default())
.verbose(true)
.log_level(LogLevel::Verbose)
.log_destination(LogDestination::File(camino_fs::Utf8PathBuf::from("/tmp/builder.log")))
.release(true)
.builder_toml("builder.toml");

let s = cmd.to_string();
let cmd2 = s.parse::<BuilderCmd>().unwrap();
assert_eq!(cmd, cmd2);
}

#[test]
fn roundtrip_log_destinations() {
// Test all log destination variants
let destinations = [
LogDestination::Cargo,
LogDestination::File(camino_fs::Utf8PathBuf::from("/path/to/log.txt")),
LogDestination::Terminal,
LogDestination::TerminalPlain,
];

for destination in destinations {
let cmd = BuilderCmd::new()
.log_destination(destination)
.log_level(LogLevel::Normal);

let s = cmd.to_string();
let cmd2 = s.parse::<BuilderCmd>().unwrap();
assert_eq!(cmd, cmd2);
}
}
Loading
Loading