use std::iter::once;
use anyhow::*;
use common::*;
use embuild::bindgen::types::callbacks::{IntKind, ParseCallbacks};
use embuild::bindgen::BindgenExt;
use embuild::utils::OsStrExt;
use embuild::{bindgen as bindgen_utils, build, cargo, kconfig, path_buf};
mod common;
mod config;
#[cfg(any(feature = "native", not(feature = "pio")))]
mod native;
#[cfg(all(not(feature = "native"), feature = "pio"))]
mod pio;
#[cfg(any(feature = "native", not(feature = "pio")))]
use native as build_driver;
#[cfg(all(not(feature = "native"), feature = "pio"))]
use pio as build_driver;
#[derive(Debug)]
struct BindgenCallbacks;
impl ParseCallbacks for BindgenCallbacks {
fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
const PREFIX: &str = "ESP_";
const SUFFIX: &str = "ERR_";
const SUFFIX_SPECIAL: [&str; 2] = ["OK", "FAIL"];
let name = name.strip_prefix(PREFIX)?;
if name.starts_with(SUFFIX) || SUFFIX_SPECIAL.contains(&name) {
Some(IntKind::I32)
} else {
None
}
}
}
fn main() -> anyhow::Result<()> {
let build_output = build_driver::build()?;
let kconfig_str_allow = regex::Regex::new(r"IDF_TARGET")?;
let cfg_args = build::CfgArgs {
args: build_output
.kconfig_args
.filter(|(key, value)| {
matches!(value, kconfig::Value::Tristate(kconfig::Tristate::True))
|| kconfig_str_allow.is_match(key)
})
.filter_map(|(key, value)| value.to_rustc_cfg("esp_idf", key))
.collect(),
};
let mcu = cfg_args
.get("esp_idf_idf_target")
.ok_or_else(|| {
anyhow!(
"Failed to get IDF_TARGET from kconfig. cfgs:\n{:?}",
cfg_args.args
)
})?
.to_lowercase();
let manifest_dir = manifest_dir()?;
let header_file = path_buf![
&manifest_dir,
"src",
"include",
if mcu == "esp8266" {
"esp-8266-rtos-sdk"
} else {
"esp-idf"
},
"bindings.h"
];
cargo::track_file(&header_file);
let configure_bindgen = |bindgen: embuild::bindgen::types::Builder| {
Ok(bindgen
.parse_callbacks(Box::new(BindgenCallbacks))
.use_core()
.enable_function_attribute_detection()
.clang_arg("-DESP_PLATFORM")
.blocklist_function("strtold")
.blocklist_function("_strtold_r")
.blocklist_function("v.*printf")
.blocklist_function("v.*scanf")
.blocklist_function("_v.*printf_r")
.blocklist_function("_v.*scanf_r")
.blocklist_function("esp_log_writev")
.blocklist_type("pcnt_unit_t") .clang_args(build_output.components.clang_args())
.clang_args(vec![
"-target",
if mcu != "esp32" && mcu != "esp32s2" && mcu != "esp32s3" {
"riscv32"
} else {
"xtensa"
},
]))
};
let bindings_file = bindgen_utils::default_bindings_file()?;
let bindgen_err = || {
anyhow!(
"failed to generate bindings in file '{}'",
bindings_file.display()
)
};
#[allow(unused_mut)]
let mut headers = vec![header_file];
#[cfg(any(feature = "native", not(feature = "pio")))]
headers.extend(
build_output
.config
.native
.combined_bindings_headers()?
.into_iter()
.inspect(|h| cargo::track_file(h)),
);
configure_bindgen(build_output.bindgen.clone().builder()?)?
.path_headers(headers)?
.generate()
.with_context(bindgen_err)?
.write_to_file(&bindings_file)
.with_context(bindgen_err)?;
#[cfg(any(feature = "native", not(feature = "pio")))]
(|| {
use std::fs;
use std::io::{BufWriter, Write};
let mut output_file =
BufWriter::new(fs::File::options().append(true).open(&bindings_file)?);
for (module_name, headers) in build_output.config.native.module_bindings_headers()? {
let bindings = configure_bindgen(build_output.bindgen.clone().builder()?)?
.path_headers(headers.into_iter().inspect(|h| cargo::track_file(h)))?
.generate()?;
writeln!(
&mut output_file,
"pub mod {module_name} {{\
{bindings}\
}}"
)?;
}
Ok(())
})()
.with_context(bindgen_err)?;
bindgen_utils::cargo_fmt_file(&bindings_file);
let cfg_args = build::CfgArgs {
args: cfg_args
.args
.into_iter()
.chain(EspIdfVersion::parse(bindings_file)?.cfg_args())
.chain(build_output.components.cfg_args())
.chain(once(mcu))
.collect(),
};
cfg_args.propagate();
cfg_args.output();
build_output.cincl_args.propagate();
if let Some(env_path) = build_output.env_path {
cargo::set_metadata(embuild::build::ENV_PATH_VAR, env_path);
}
cargo::set_metadata(
embuild::build::ESP_IDF_PATH_VAR,
build_output.esp_idf.try_to_str()?,
);
if let Some(link_args) = build_output.link_args {
link_args.propagate();
link_args.output();
}
Ok(())
}