psoc 0.1.1

Rust drivers and hardware abstraction layer for Infineon PSOC microcontrollers
#!/usr/bin/env cargo +nightly -Zscript
---
[package]
edition = "2024"

[dependencies]
psoc-devices = { path = "../devices" }

clap = { version = "4.6.1", features = ["derive"] }
indicatif = "0.18.4"
toml = "1.1.2"
---
// Copyright (c) 2026, Infineon Technologies AG or an affiliate of Infineon Technologies AG.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::OsStr;
use std::path::PathBuf;

use clap::Parser;

use psoc_devices as devices;

#[derive(clap::Parser)]
struct Args {
    /// Check all devices. By default, only checks one device for each die.
    #[clap(short, long)]
    all: bool,
}

fn main() {
    let args = Args::parse();
    let path = PathBuf::from(std::env::args().next().unwrap())
        .parent()
        .unwrap()
        .join("..");

    println!("Checking devices...");

    let to_check = devices::DICE
        .iter()
        .flat_map(|die| {
            die.parts()
                .iter()
                .take(if args.all { usize::MAX } else { 1 })
                .flat_map(|(part, _)| die.cores().iter().map(move |core| (part, core)))
        })
        .collect::<Vec<_>>();

    let progress_style = indicatif::ProgressStyle::with_template("[{wide_bar}] {pos}/{len}")
        .unwrap()
        .progress_chars("=> ");
    let progress =
        indicatif::ProgressBar::new(to_check.len() as u64).with_style(progress_style.clone());

    for (part, core) in to_check {
        let features = format!(
            "device-{},core-{},embassy-time,defmt",
            part.to_lowercase(),
            core.to_lowercase()
        );

        progress.suspend(|| println!("  Checking {}", features));
        let output = std::process::Command::new("cargo")
            .args([
                "check",
                "--color=always",
                "--quiet",
                "--manifest-path",
                path.join("Cargo.toml").to_str().unwrap(),
                "--features",
                &features,
                "--target",
                devices::target_for_core(core),
            ])
            .output()
            .expect("Failed to execute cargo check");
        if !output.stderr.is_empty() {
            progress.suspend(|| eprint!("{}", String::from_utf8_lossy(&output.stderr)));
        }
        if !output.status.success() {
            std::process::exit(1);
        }
        progress.inc(1);
    }
    progress.finish_and_clear();

    println!("Checking examples...");
    let examples = std::fs::read_dir(path.join("examples"))
        .unwrap()
        .filter_map(|entry| {
            let entry = entry.unwrap();
            if entry.file_type().unwrap().is_dir()
                && !entry.path().starts_with(".")
                && entry.path().file_name() != Some(OsStr::new("bsps"))
            {
                Some(entry.path())
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    let bsps = std::fs::read_dir(path.join("examples/bsps"))
        .unwrap()
        .filter_map(|entry| {
            let entry = entry.unwrap();
            if entry.file_type().unwrap().is_dir() && !entry.path().starts_with(".") {
                Some(entry.path())
            } else {
                None
            }
        })
        .map(|path| {
            // Read the BSP's Cargo.toml file to determine the core it targets
            let cargo_toml = std::fs::read_to_string(path.join("Cargo.toml")).unwrap();
            let cargo_toml: toml::Table = toml::from_str(&cargo_toml).unwrap();
            let core = cargo_toml
                .get("dependencies")
                .and_then(|f| f.as_table())
                .unwrap()
                .get("psoc")
                .and_then(|f| f.as_table())
                .unwrap()
                .get("features")
                .and_then(|f| f.as_array())
                .unwrap()
                .into_iter()
                .filter_map(|f| f.as_str())
                .filter_map(|f| f.strip_prefix("core-"))
                .next()
                .unwrap()
                .to_owned();

            (path, core)
        })
        .collect::<Vec<_>>();

    let progress = indicatif::ProgressBar::new((examples.len() * bsps.len()) as u64)
        .with_style(progress_style);

    for example in examples {
        for (bsp, core) in &bsps {
            progress.suspend(|| {
                println!(
                    "  Checking {} on {}",
                    example.file_name().unwrap().to_str().unwrap(),
                    bsp.file_name().unwrap().to_str().unwrap()
                )
            });
            let output = std::process::Command::new("cargo")
                .args([
                    "build",
                    "--color=always",
                    "--quiet",
                    "--manifest-path",
                    example.join("Cargo.toml").to_str().unwrap(),
                    "--features",
                    &format!("{}", bsp.file_name().unwrap().to_str().unwrap()),
                    "--target",
                    devices::target_for_core(core),
                ])
                .output()
                .expect("Failed to execute cargo build");
            if !output.stderr.is_empty() {
                progress.suspend(|| eprint!("{}", String::from_utf8_lossy(&output.stderr)));
            }
            if !output.status.success() {
                std::process::exit(1);
            }
            progress.inc(1);
        }
    }
}