use std::{env, path::Path};
use esp_build::assert_unique_used_features;
fn main() {
assert_unique_used_features!(
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3"
);
assert_unique_used_features!("jtag-serial", "uart", "auto");
if cfg!(feature = "jtag-serial")
&& !(cfg!(feature = "esp32c3")
|| cfg!(feature = "esp32c6")
|| cfg!(feature = "esp32h2")
|| cfg!(feature = "esp32p4")
|| cfg!(feature = "esp32s3"))
{
panic!(
"The `jtag-serial` feature is only supported by the ESP32-C3, ESP32-C6, ESP32-H2, ESP32-P4, and ESP32-S3"
);
}
if cfg!(feature = "colors") && !cfg!(feature = "log") {
println!(
"cargo:warning=The `colors` feature is only effective when using the `log` feature"
);
}
if std::env::var("ESP_LOGLEVEL").is_ok() || std::env::var("ESP_LOGFILTER").is_ok() {
panic!("`ESP_LOGLEVEL` and `ESP_LOGFILTER` is not supported anymore. Please use `ESP_LOG` instead.");
}
generate_filter_snippet();
#[cfg(target_os = "windows")]
println!("cargo:rustc-cfg=host_is_windows");
println!("cargo:rerun-if-env-changed=ESP_LOG");
println!("cargo:rustc-check-cfg=cfg(host_is_windows)");
}
fn generate_filter_snippet() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("log_filter.rs");
let filter = env::var("ESP_LOG");
let snippet = if let Ok(filter) = filter {
let res = parse_spec(&filter);
if !res.errors.is_empty() {
panic!("Error parsing `ESP_LOG`: {:?}", res.errors);
} else {
let max = res
.directives
.iter()
.map(|v| v.level)
.max()
.unwrap_or(log::LevelFilter::Off);
let max = match max {
log::LevelFilter::Off => "Off",
log::LevelFilter::Error => "Error",
log::LevelFilter::Warn => "Warn",
log::LevelFilter::Info => "Info",
log::LevelFilter::Debug => "Debug",
log::LevelFilter::Trace => "Trace",
};
let mut snippet = String::new();
snippet.push_str(&format!(
"pub(crate) const FILTER_MAX: log::LevelFilter = log::LevelFilter::{};",
max
));
snippet
.push_str("pub(crate) fn is_enabled(level: log::Level, _target: &str) -> bool {");
for directive in res.directives {
let level = match directive.level {
log::LevelFilter::Off => "Off",
log::LevelFilter::Error => "Error",
log::LevelFilter::Warn => "Warn",
log::LevelFilter::Info => "Info",
log::LevelFilter::Debug => "Debug",
log::LevelFilter::Trace => "Trace",
};
if let Some(name) = directive.name {
snippet.push_str(&format!(
"if _target.starts_with(\"{}\") && level <= log::LevelFilter::{} {{ return true; }}",
&name, level
));
} else {
snippet.push_str(&format!(
"if level <= log::LevelFilter::{} {{ return true; }}",
level
));
}
}
snippet.push_str(" false");
snippet.push('}');
snippet
}
} else {
"pub(crate) const FILTER_MAX: log::LevelFilter = log::LevelFilter::Off; pub(crate) fn is_enabled(_level: log::Level, _target: &str) -> bool { true }".to_string()
};
std::fs::write(&dest_path, &snippet).unwrap();
}
#[derive(Default, Debug)]
struct ParseResult {
pub(crate) directives: Vec<Directive>,
pub(crate) errors: Vec<String>,
}
impl ParseResult {
fn add_directive(&mut self, directive: Directive) {
self.directives.push(directive);
}
fn add_error(&mut self, message: String) {
self.errors.push(message);
}
}
#[derive(Debug)]
struct Directive {
pub(crate) name: Option<String>,
pub(crate) level: log::LevelFilter,
}
fn parse_spec(spec: &str) -> ParseResult {
let mut result = ParseResult::default();
let mut parts = spec.split('/');
let mods = parts.next();
if let Some(m) = mods {
for s in m.split(',').map(|ss| ss.trim()) {
if s.is_empty() {
continue;
}
let mut parts = s.split('=');
let (log_level, name) =
match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
(Some(part0), None, None) => {
match part0.parse() {
Ok(num) => (num, None),
Err(_) => (log::LevelFilter::max(), Some(part0)),
}
}
(Some(part0), Some(""), None) => (log::LevelFilter::max(), Some(part0)),
(Some(part0), Some(part1), None) => {
if let Ok(num) = part1.parse() {
(num, Some(part0))
} else {
result.add_error(format!("invalid logging spec '{part1}'"));
continue;
}
}
_ => {
result.add_error(format!("invalid logging spec '{s}'"));
continue;
}
};
result.add_directive(Directive {
name: name.map(|s| s.to_owned()),
level: log_level,
});
}
}
result
}