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
4 changes: 2 additions & 2 deletions crates/fbuild-build/src/compile_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ impl fbuild_packages::library::library_compiler::LibCompileBackend for EmbeddedL
&self,
compiler: &std::path::Path,
args: Vec<String>,
cwd: std::path::PathBuf,
cwd: fbuild_core::path::NormalizedPath,
env: Vec<(String, String)>,
) -> fbuild_core::Result<fbuild_packages::library::library_compiler::LibCompileOutcome> {
let global = get_global().ok_or_else(|| {
Expand All @@ -118,7 +118,7 @@ impl fbuild_packages::library::library_compiler::LibCompileBackend for EmbeddedL
)
})?;
let svc = global.service();
let compile_fut = svc.compile(compiler, args, cwd, env);
let compile_fut = svc.compile(compiler, args, cwd.into_path_buf(), env);
let outcome = tokio::time::timeout(std::time::Duration::from_secs(300), compile_fut)
.await
.map_err(|_| {
Expand Down
8 changes: 4 additions & 4 deletions crates/fbuild-build/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,14 +319,14 @@ fn object_hash_key(source: &Path, build_dir: &Path) -> String {
.unwrap_or(false)
{
if let Some(workspace) = ancestor.parent() {
if let Ok(rel) = source.strip_prefix(workspace) {
return rel.to_string_lossy().replace('\\', "/");
}
// The blessed compile-CWD relativization owns the
// `strip_prefix` + slash normalization (see fbuild-core).
return fbuild_core::path::path_arg_for_compile_cwd(source, workspace);
}
break;
}
}
source.to_string_lossy().replace('\\', "/")
NormalizedPath::from(source).display_slash()
}

/// Filter both `flags` and `extra_flags` through `unflags` using the shared
Expand Down
2 changes: 1 addition & 1 deletion crates/fbuild-build/src/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ fn test_needs_rebuild_missing_object() {
#[test]
fn test_object_path() {
let path = CompilerBase::object_path(Path::new("main.cpp"), Path::new("/build"));
assert!(path.starts_with("/build"));
assert_eq!(path.parent(), Some(Path::new("/build")));
assert!(path
.file_name()
.unwrap_or_default()
Expand Down
33 changes: 20 additions & 13 deletions crates/fbuild-build/src/esp32/orchestrator/boot_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::path::Path;
use std::time::Instant;

use fbuild_core::path::NormalizedPath;
use fbuild_core::Result;

use super::super::mcu_config::Esp32McuConfig;
Expand Down Expand Up @@ -132,7 +133,7 @@ pub(super) async fn prepare_boot_artifacts(
let partitions_name = board.partitions.as_deref().unwrap_or("default.csv");
let parts_csv = resolve_partitions_csv(
project_dir,
framework.get_partitions_csv(partitions_name),
framework.get_partitions_csv(partitions_name).into(),
board.partitions.as_deref(),
)?;
let gen_tool = framework.get_gen_esp32part();
Expand Down Expand Up @@ -200,13 +201,13 @@ pub(super) async fn prepare_boot_artifacts(
/// existence check keeps the historical warn-and-continue behavior.
fn resolve_partitions_csv(
project_dir: &Path,
framework_candidate: std::path::PathBuf,
framework_candidate: NormalizedPath,
configured: Option<&str>,
) -> Result<std::path::PathBuf> {
) -> Result<NormalizedPath> {
let Some(name) = configured else {
return Ok(framework_candidate);
};
let project_candidate = project_dir.join(name);
let project_candidate = NormalizedPath::new(project_dir.join(name));
if project_candidate.exists() {
return Ok(project_candidate);
}
Expand Down Expand Up @@ -234,11 +235,13 @@ mod tests {

let resolved = resolve_partitions_csv(
project,
project.join("framework/tools/partitions/config/custom.csv"),
project
.join("framework/tools/partitions/config/custom.csv")
.into(),
Some("config/custom.csv"),
)
.unwrap();
assert_eq!(resolved, project.join("config/custom.csv"));
assert_eq!(resolved.as_path(), project.join("config/custom.csv"));
}

#[test]
Expand All @@ -249,10 +252,13 @@ mod tests {
std::fs::create_dir_all(&fw_dir).unwrap();
std::fs::write(fw_dir.join("huge_app.csv"), "csv").unwrap();

let resolved =
resolve_partitions_csv(project, fw_dir.join("huge_app.csv"), Some("huge_app.csv"))
.unwrap();
assert_eq!(resolved, fw_dir.join("huge_app.csv"));
let resolved = resolve_partitions_csv(
project,
fw_dir.join("huge_app.csv").into(),
Some("huge_app.csv"),
)
.unwrap();
assert_eq!(resolved.as_path(), fw_dir.join("huge_app.csv"));
}

#[test]
Expand All @@ -262,7 +268,7 @@ mod tests {

let err = resolve_partitions_csv(
project,
project.join("framework/tools/partitions/nope.csv"),
project.join("framework/tools/partitions/nope.csv").into(),
Some("nope.csv"),
)
.unwrap_err();
Expand All @@ -275,7 +281,8 @@ mod tests {
fn unconfigured_default_keeps_warn_and_continue_semantics() {
let tmp = tempfile::TempDir::new().unwrap();
let missing_default = tmp.path().join("framework/tools/partitions/default.csv");
let resolved = resolve_partitions_csv(tmp.path(), missing_default.clone(), None).unwrap();
assert_eq!(resolved, missing_default);
let resolved =
resolve_partitions_csv(tmp.path(), missing_default.clone().into(), None).unwrap();
assert_eq!(resolved.as_path(), missing_default.as_path());
}
}
4 changes: 2 additions & 2 deletions crates/fbuild-build/src/esp32/orchestrator/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ impl BuildOrchestrator for Esp32Orchestrator {
&flash_freq,
ctx.board.max_flash,
ctx.board.max_ram,
esptool_bin.clone(),
esptool_bin.clone().map(|path| path.into_path_buf()),
params.verbose,
);

Expand Down Expand Up @@ -822,7 +822,7 @@ impl BuildOrchestrator for Esp32Orchestrator {
&ctx.board,
&mcu_config,
&flash_freq,
esptool_bin.as_deref(),
esptool_bin.as_ref().map(|path| path.as_path()),
&mut perf,
)
.await?;
Expand Down
7 changes: 4 additions & 3 deletions crates/fbuild-build/src/esp32/orchestrator/packages.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! Package resolution for pioarduino (platform.json, framework, toolchain).

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::path::Path;

use fbuild_core::path::NormalizedPath;
use fbuild_core::Result;

/// Resolve framework + toolchain for pioarduino mode (GCC 14 + ESP-IDF 5.x).
Expand All @@ -23,7 +24,7 @@ pub(super) async fn resolve_pioarduino_packages(
) -> Result<(
fbuild_packages::toolchain::Esp32Toolchain,
fbuild_packages::library::Esp32Framework,
Option<PathBuf>,
Option<NormalizedPath>,
)> {
// Ensure pioarduino platform (contains platform.json with metadata URLs).
// Honor `platform_packages = platform-espressif32@<URL>#<sha>` from the env
Expand Down Expand Up @@ -145,7 +146,7 @@ pub(super) async fn resolve_pioarduino_packages(
async fn resolve_esptool(
platform: &fbuild_packages::library::Esp32Platform,
project_dir: &Path,
) -> Option<PathBuf> {
) -> Option<NormalizedPath> {
let url = match platform.get_package_url("tool-esptoolpy") {
Ok(url) => url,
Err(e) => {
Expand Down
54 changes: 53 additions & 1 deletion crates/fbuild-cli/src/lib_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use std::path::{Path, PathBuf};

use fbuild_config::PlatformIOConfig;
use fbuild_core::path::normalize_for_key;
use fbuild_core::Platform;
use fbuild_library_select::{resolve, Selection};
use fbuild_packages::library::framework_library::discover_framework_libraries;
Expand Down Expand Up @@ -333,10 +334,24 @@ fn first_reached_under(included: &[PathBuf], lib: &FrameworkLibrary) -> Option<P
.collect();
included
.iter()
.find(|p| canon_dirs.iter().any(|d| p.starts_with(d)))
.find(|p| canon_dirs.iter().any(|d| normalized_path_is_under(p, d)))
.cloned()
}

fn normalized_path_is_under(path: &Path, dir: &Path) -> bool {
let path_key = normalize_for_key(path);
let dir_key = normalize_for_key(dir);
if path_key == dir_key {
return true;
}
let dir_prefix = if dir_key.ends_with('/') {
dir_key
} else {
format!("{dir_key}/")
};
path_key.starts_with(&dir_prefix)
}

fn emit_explain(
project_dir: &Path,
env_name: &str,
Expand Down Expand Up @@ -433,3 +448,40 @@ fn emit_json(
.expect("fbuild-cli: lib-select JSON payload is built from primitives, serialization is infallible")
);
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn normalized_path_is_under_matches_exact_path() {
assert!(normalized_path_is_under(
Path::new("/foo/bar"),
Path::new("/foo/bar")
));
}

#[test]
fn normalized_path_is_under_matches_nested_path() {
assert!(normalized_path_is_under(
Path::new("/foo/bar/baz.h"),
Path::new("/foo/bar")
));
}

#[test]
fn normalized_path_is_under_handles_dir_trailing_slash() {
assert!(normalized_path_is_under(
Path::new("/foo/bar/baz.h"),
Path::new("/foo/bar/")
));
}

#[test]
fn normalized_path_is_under_rejects_sibling_prefix() {
assert!(!normalized_path_is_under(
Path::new("/foo/barbaz/header.h"),
Path::new("/foo/bar")
));
}
}
28 changes: 14 additions & 14 deletions crates/fbuild-deploy/src/lpc_debugger_reflash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
//! subsequent `fbuild deploy` reaches ISP mode automatically via
//! `-control` alone — no more button dance.

use std::path::{Path, PathBuf};
use std::path::Path;

use fbuild_core::path::NormalizedPath;
use fbuild_core::{FbuildError, Result};

/// The vendored asset base URL — points at
Expand Down Expand Up @@ -79,7 +80,7 @@ pub const LPC_LINK2_FIRMWARE_ENV_VAR: &str = "FBUILD_LPC_LINK2_FIRMWARE";
/// The one canonical directory fbuild caches the LPC-Link2 debugger
/// tools under. Honors `FBUILD_DEV_MODE=1` for `~/.fbuild/dev/…`
/// isolation, same as `find_lpc21isp` and the rest of `fbuild-paths`.
pub fn managed_tools_dir() -> Option<PathBuf> {
pub fn managed_tools_dir() -> Option<NormalizedPath> {
let home = home_dir()?;
let mode = if std::env::var_os("FBUILD_DEV_MODE").is_some() {
"dev"
Expand Down Expand Up @@ -109,9 +110,9 @@ pub fn asset_url(name: &str) -> String {
/// Returns `None` if nothing is present; the caller emits the actionable
/// "run `fbuild deploy --upgrade-debugger` once to install the tools"
/// diagnostic.
pub fn find_dfu_util() -> Option<PathBuf> {
pub fn find_dfu_util() -> Option<NormalizedPath> {
if let Some(env_hit) = std::env::var_os(DFU_UTIL_PATH_ENV_VAR) {
let p = PathBuf::from(env_hit);
let p = NormalizedPath::new(Path::new(&env_hit));
if p.is_file() {
return Some(p);
}
Expand All @@ -134,9 +135,9 @@ pub fn find_dfu_util() -> Option<PathBuf> {
/// 1. `FBUILD_LPC_LINK2_FIRMWARE` env override.
/// 2. `<managed_tools_dir>/lpc-link2-cmsis-dap-v2.hex` (preferred).
/// 3. `<managed_tools_dir>/lpc-link2-cmsis-dap-v1.hex` (legacy fallback).
pub fn find_lpc_link2_firmware() -> Option<PathBuf> {
pub fn find_lpc_link2_firmware() -> Option<NormalizedPath> {
if let Some(env_hit) = std::env::var_os(LPC_LINK2_FIRMWARE_ENV_VAR) {
let p = PathBuf::from(env_hit);
let p = NormalizedPath::new(Path::new(&env_hit));
if p.is_file() {
return Some(p);
}
Expand Down Expand Up @@ -264,7 +265,7 @@ pub fn required_asset_names() -> [&'static str; 2] {

/// Errors specific to the reflash flow. Wrapped into `FbuildError` at
/// the deploy layer.
pub fn require_installed() -> Result<(PathBuf, PathBuf)> {
pub fn require_installed() -> Result<(NormalizedPath, NormalizedPath)> {
let dfu = find_dfu_util().ok_or_else(|| FbuildError::DeployFailed(install_hint()))?;
let fw = find_lpc_link2_firmware().ok_or_else(|| FbuildError::DeployFailed(install_hint()))?;
Ok((dfu, fw))
Expand All @@ -273,15 +274,14 @@ pub fn require_installed() -> Result<(PathBuf, PathBuf)> {
/// Resolve `$HOME` / `%USERPROFILE%`. Kept local so this module does
/// not gain a `dirs` dependency for one call site — mirrors the same
/// helper in `fbuild_deploy::lpc`.
fn home_dir() -> Option<PathBuf> {
fn home_dir() -> Option<NormalizedPath> {
#[cfg(target_os = "windows")]
{
std::env::var_os("USERPROFILE").map(PathBuf::from)
}
#[cfg(not(target_os = "windows"))]
{
std::env::var_os("HOME").map(PathBuf::from)
if let Some(value) = std::env::var_os("USERPROFILE") {
return Some(NormalizedPath::new(Path::new(&value)));
}
}
std::env::var_os("HOME").map(|value| NormalizedPath::new(Path::new(&value)))
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

#[cfg(test)]
Expand Down Expand Up @@ -355,7 +355,7 @@ mod tests {
Some(v) => std::env::set_var(DFU_UTIL_PATH_ENV_VAR, v),
None => std::env::remove_var(DFU_UTIL_PATH_ENV_VAR),
}
assert_eq!(got.as_deref(), Some(fake.as_path()));
assert_eq!(got.as_ref().map(|p| p.as_path()), Some(fake.as_path()));
}

#[test]
Expand Down
15 changes: 8 additions & 7 deletions crates/fbuild-deploy/src/probe_rs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
//! can dispatch to it in preference to the UART-ISP path (lpc21isp),
//! which requires a `SW3 + SW4` button press to enter ISP mode.

use std::path::{Path, PathBuf};
use std::path::Path;
use std::time::Duration;

use fbuild_core::path::NormalizedPath;
use fbuild_core::subprocess::run_command_blocking;
use fbuild_core::{FbuildError, Result};

Expand All @@ -55,7 +56,7 @@ const PROBE_RS_TIMEOUT: Duration = Duration::from_secs(120);
///
/// Honors `FBUILD_DEV_MODE=1` → `~/.fbuild/dev/tools/probe-rs/` to
/// match the isolation the rest of `fbuild-paths` applies.
pub fn managed_probe_rs_path() -> Option<PathBuf> {
pub fn managed_probe_rs_path() -> Option<NormalizedPath> {
let exe = if cfg!(windows) {
"probe-rs.exe"
} else {
Expand Down Expand Up @@ -92,9 +93,9 @@ pub fn managed_probe_rs_path() -> Option<PathBuf> {
/// distro package will lack the FastLED patches and will hang on the
/// LPC-Link2 v1.0.7 firmware, which is the exact failure mode this
/// module exists to avoid.
pub fn find_probe_rs() -> Option<PathBuf> {
pub fn find_probe_rs() -> Option<NormalizedPath> {
if let Some(env_hit) = std::env::var_os(PROBE_RS_PATH_ENV_VAR) {
let p = PathBuf::from(env_hit);
let p = NormalizedPath::new(Path::new(&env_hit));
if p.is_file() {
return Some(p);
}
Expand Down Expand Up @@ -275,14 +276,14 @@ impl ProbeRsRun {
}
}

fn home_dir_local() -> Option<PathBuf> {
fn home_dir_local() -> Option<NormalizedPath> {
#[cfg(windows)]
{
if let Some(v) = std::env::var_os("USERPROFILE") {
return Some(PathBuf::from(v));
return Some(NormalizedPath::new(Path::new(&v)));
}
}
std::env::var_os("HOME").map(PathBuf::from)
std::env::var_os("HOME").map(|value| NormalizedPath::new(Path::new(&value)))
}

#[cfg(test)]
Expand Down
Loading
Loading