Build examples in debug mode (#2078)

* Build examples in debug mode

* Allow building psram examples in debug mode in CI

* Don't rebuild tests, try to avoid rebuilding dependencies
This commit is contained in:
Dániel Buga 2024-09-05 12:04:07 +02:00 committed by GitHub
parent 42a0417fba
commit 5370afb1eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 120 additions and 75 deletions

View File

@ -57,5 +57,7 @@ runs:
--target=${{ inputs.target }} \
esp-hal
- name: Build (examples)
env:
CI: 1
shell: bash
run: cargo +${{ inputs.toolchain }} xtask build-examples esp-hal ${{ inputs.device }}
run: cargo +${{ inputs.toolchain }} xtask build-examples esp-hal ${{ inputs.device }} --debug

View File

@ -6,14 +6,14 @@ fn main() -> Result<(), String> {
if cfg!(feature = "esp32") {
match std::env::var("OPT_LEVEL") {
Ok(level) => {
Ok(level) if std::env::var("CI").is_err() => {
if level != "2" && level != "3" {
Err(format!("Building esp-storage for ESP32 needs optimization level 2 or 3 - yours is {}. See https://github.com/esp-rs/esp-storage", level))
} else {
Ok(())
}
}
Err(_err) => Ok(()),
_ => Ok(()),
}
} else {
Ok(())

View File

@ -8,4 +8,12 @@ fn main() {
if cfg!(feature = "esp-wifi") {
println!("cargo::rustc-link-arg=-Trom_functions.x");
}
// Allow building examples in CI in debug mode
println!("cargo:rustc-check-cfg=cfg(is_not_release)");
println!("cargo:rerun-if-env-changed=CI");
#[cfg(debug_assertions)]
if std::env::var("CI").is_err() {
println!("cargo::rustc-cfg=is_not_release");
}
}

View File

@ -22,7 +22,7 @@ use esp_println::{print, println};
const WIDTH: usize = 80;
#[cfg(debug_assertions)]
#[cfg(is_not_release)]
compile_error!("Run this example in release mode");
#[embassy_executor::task]

View File

@ -25,11 +25,11 @@ fn init_psram_heap() {
}
}
#[cfg(is_not_release)]
compile_error!("PSRAM example must be built in release mode!");
#[entry]
fn main() -> ! {
#[cfg(debug_assertions)]
compile_error!("This example MUST be built in release mode!");
let peripherals = esp_hal::init(esp_hal::Config::default());
psram::init_psram(peripherals.PSRAM);

View File

@ -1,6 +1,6 @@
//! This shows how to use PSRAM as heap-memory via esp-alloc
//!
//! You need an ESP32, ESP32-S2, or ESP32-S3 with at least 2 MB of PSRAM memory.
//! You need an ESP32, ESP32-S2 or ESP32-S3 with at least 2 MB of PSRAM memory.
//% CHIPS: esp32 esp32s2 esp32s3
//% FEATURES: psram-2m
@ -25,11 +25,11 @@ fn init_psram_heap() {
}
}
#[cfg(is_not_release)]
compile_error!("PSRAM example must be built in release mode!");
#[entry]
fn main() -> ! {
#[cfg(debug_assertions)]
compile_error!("PSRAM example must be built in release mode!");
let peripherals = esp_hal::init(esp_hal::Config::default());
psram::init_psram(peripherals.PSRAM);

View File

@ -113,6 +113,14 @@ impl CargoArgsBuilder {
self
}
pub fn add_arg<S>(&mut self, arg: S) -> &mut Self
where
S: Into<String>,
{
self.args.push(arg.into());
self
}
#[must_use]
pub fn build(self) -> Vec<String> {
let mut args = vec![];

View File

@ -57,11 +57,11 @@ pub enum Package {
pub struct Metadata {
example_path: PathBuf,
chips: Vec<Chip>,
feature_sets: Vec<Vec<String>>,
feature_set: Vec<String>,
}
impl Metadata {
pub fn new(example_path: &Path, chips: Vec<Chip>, feature_sets: Vec<Vec<String>>) -> Self {
pub fn new(example_path: &Path, chips: Vec<Chip>, feature_set: Vec<String>) -> Self {
let chips = if chips.is_empty() {
Chip::iter().collect()
} else {
@ -71,7 +71,7 @@ impl Metadata {
Self {
example_path: example_path.to_path_buf(),
chips,
feature_sets,
feature_set,
}
}
@ -89,9 +89,9 @@ impl Metadata {
.replace(".rs", "")
}
/// A list of all features required for building a given examples.
pub fn feature_sets(&self) -> &[Vec<String>] {
&self.feature_sets
/// A list of all features required for building a given example.
pub fn feature_set(&self) -> &[String] {
&self.feature_set
}
/// If the specified chip is in the list of chips, then it is supported.
@ -154,7 +154,7 @@ pub fn build_documentation(
}
/// Load all examples at the given path, and parse their metadata.
pub fn load_examples(path: &Path) -> Result<Vec<Metadata>> {
pub fn load_examples(path: &Path, action: CargoAction) -> Result<Vec<Metadata>> {
let mut examples = Vec::new();
for entry in fs::read_dir(path)? {
@ -172,7 +172,7 @@ pub fn load_examples(path: &Path) -> Result<Vec<Metadata>> {
.trim()
.split_ascii_whitespace()
.map(|s| s.to_string())
.collect::<VecDeque<_>>();
.collect::<Vec<_>>();
if split.len() < 2 {
bail!(
@ -182,7 +182,7 @@ pub fn load_examples(path: &Path) -> Result<Vec<Metadata>> {
}
// The trailing ':' on metadata keys is optional :)
let key = split.pop_front().unwrap();
let key = split.swap_remove(0);
let key = key.trim_end_matches(':');
if key == "CHIPS" {
@ -191,15 +191,31 @@ pub fn load_examples(path: &Path) -> Result<Vec<Metadata>> {
.map(|s| Chip::from_str(s, false).unwrap())
.collect::<Vec<_>>();
} else if key == "FEATURES" {
feature_sets.push(split.into());
// Sort the features so they are in a deterministic order:
split.sort();
feature_sets.push(split);
} else {
log::warn!("Unrecognized metadata key '{key}', ignoring");
}
}
examples.push(Metadata::new(&path, chips, feature_sets));
if feature_sets.is_empty() {
feature_sets.push(Vec::new());
}
if action == CargoAction::Build {
// Only build the first feature set for each example.
// Rebuilding with a different feature set just wastes time because the latter
// one will overwrite the former one(s).
feature_sets.truncate(1);
}
for feature_set in feature_sets {
examples.push(Metadata::new(&path, chips.clone(), feature_set));
}
}
// Sort by feature set, to prevent rebuilding packages if not necessary.
examples.sort_by_key(|e| e.feature_set().join(","));
Ok(examples)
}
@ -210,7 +226,8 @@ pub fn execute_app(
target: &str,
app: &Metadata,
action: CargoAction,
repeat: usize,
mut repeat: usize,
debug: bool,
) -> Result<()> {
log::info!(
"Building example '{}' for '{}'",
@ -218,30 +235,7 @@ pub fn execute_app(
chip
);
let feature_sets = if app.feature_sets().is_empty() {
vec![vec![]]
} else {
app.feature_sets().to_vec()
};
for features in feature_sets {
execute_app_with_features(package_path, chip, target, app, action, repeat, features)?;
}
Ok(())
}
/// Run or build the specified test or example for the specified chip, with the
/// specified features enabled.
pub fn execute_app_with_features(
package_path: &Path,
chip: Chip,
target: &str,
app: &Metadata,
action: CargoAction,
mut repeat: usize,
mut features: Vec<String>,
) -> Result<()> {
let mut features = app.feature_set().to_vec();
if !features.is_empty() {
log::info!("Features: {}", features.join(","));
}
@ -269,19 +263,22 @@ pub fn execute_app_with_features(
let mut builder = CargoArgsBuilder::default()
.subcommand(subcommand)
.arg("--release")
.target(target)
.features(&features)
.arg(bin);
if !debug {
builder.add_arg("--release");
}
if subcommand == "test" && chip == Chip::Esp32c2 {
builder = builder.arg("--").arg("--speed").arg("15000");
builder.add_arg("--").add_arg("--speed").add_arg("15000");
}
// If targeting an Xtensa device, we must use the '+esp' toolchain modifier:
if target.starts_with("xtensa") {
builder = builder.toolchain("esp");
builder = builder.arg("-Zbuild-std=core,alloc")
builder.add_arg("-Zbuild-std=core,alloc");
}
let args = builder.build();

View File

@ -58,6 +58,9 @@ struct ExampleArgs {
chip: Chip,
/// Optional example to act on (all examples used if omitted)
example: Option<String>,
/// Build examples in debug mode only
#[arg(long)]
debug: bool,
}
#[derive(Debug, Args)]
@ -202,7 +205,7 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res
};
// Load all examples which support the specified chip and parse their metadata:
let mut examples = xtask::load_examples(&example_path)?
let mut examples = xtask::load_examples(&example_path, action)?
.iter()
.filter_map(|example| {
if example.supports_chip(args.chip) {
@ -227,16 +230,24 @@ fn build_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Pat
// Determine the appropriate build target for the given package and chip:
let target = target_triple(args.package, &args.chip)?;
if let Some(example) = examples.iter().find(|ex| Some(ex.name()) == args.example) {
if examples
.iter()
.find(|ex| Some(ex.name()) == args.example)
.is_some()
{
// Attempt to build only the specified example:
xtask::execute_app(
package_path,
args.chip,
target,
example,
CargoAction::Build,
1,
)
for example in examples.iter().filter(|ex| Some(ex.name()) == args.example) {
xtask::execute_app(
package_path,
args.chip,
target,
example,
CargoAction::Build,
1,
args.debug,
)?;
}
Ok(())
} else if args.example.is_some() {
// An invalid argument was provided:
bail!("Example not found or unsupported for the given chip")
@ -250,6 +261,7 @@ fn build_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Pat
example,
CargoAction::Build,
1,
args.debug,
)
})
}
@ -261,7 +273,9 @@ fn run_example(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Path)
// Filter the examples down to only the binary we're interested in, assuming it
// actually supports the specified chip:
if let Some(example) = examples.iter().find(|ex| Some(ex.name()) == args.example) {
let mut found_one = false;
for example in examples.iter().filter(|ex| Some(ex.name()) == args.example) {
found_one = true;
xtask::execute_app(
package_path,
args.chip,
@ -269,10 +283,17 @@ fn run_example(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Path)
example,
CargoAction::Run,
1,
)
} else {
bail!("Example not found or unsupported for the given chip")
args.debug,
)?;
}
ensure!(
found_one,
"Example not found or unsupported for {}",
args.chip
);
Ok(())
}
fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
@ -283,7 +304,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
let target = target_triple(Package::HilTest, &args.chip)?;
// Load all tests which support the specified chip and parse their metadata:
let mut tests = xtask::load_examples(&package_path.join("tests"))?
let mut tests = xtask::load_examples(&package_path.join("tests"), action)?
.into_iter()
.filter(|example| example.supports_chip(args.chip))
.collect::<Vec<_>>();
@ -292,15 +313,23 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
tests.sort_by_key(|a| a.name());
// Execute the specified action:
if let Some(test) = tests.iter().find(|test| Some(test.name()) == args.test) {
xtask::execute_app(
&package_path,
args.chip,
target,
test,
action,
args.repeat.unwrap_or(1),
)
if tests
.iter()
.find(|test| Some(test.name()) == args.test)
.is_some()
{
for test in tests.iter().filter(|test| Some(test.name()) == args.test) {
xtask::execute_app(
&package_path,
args.chip,
target,
test,
action,
args.repeat.unwrap_or(1),
false,
)?;
}
Ok(())
} else if args.test.is_some() {
bail!("Test not found or unsupported for the given chip")
} else {
@ -313,6 +342,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
&test,
action,
args.repeat.unwrap_or(1),
false,
)
.is_err()
{