diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7e7756e..19c1a7b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,10 +57,10 @@ jobs: fail-fast: false matrix: include: - - { vendor: Atmel } - - { vendor: Atmel, options: "-- --strict --atomics" } - - { vendor: Freescale } - - { vendor: Freescale, options: "-- --strict --atomics" } + - { vendor: atmel } + - { vendor: atmel, options: "-- --strict --atomics" } + - { vendor: freescale } + - { vendor: freescale, options: "-- --strict --atomics" } - { vendor: Fujitsu } - { vendor: Fujitsu, options: "-- --atomics" } - { vendor: Holtek } @@ -123,9 +123,18 @@ jobs: # stable. run: cargo +stable regress tests --toolchain 1.76.0 -m Nordic -- --strict --atomics - ci-clippy: + ci-docs-clippy: runs-on: ubuntu-latest needs: [check] + strategy: + fail-fast: false + matrix: + include: + - { chip: STM32F030 } + - { chip: STM32F410 } + - { chip: esp32c3 } + - { chip: MKW41Z4 } + - { chip: nrf51 } steps: - uses: actions/checkout@v4 @@ -140,12 +149,8 @@ jobs: run: | cargo install svd2rust --path . - - name: Run CI script - env: - VENDOR: RISC-V - OPTIONS: "" - COMMAND: clippy - run: bash ci/script.sh + - name: Check docs and clippy on generated PACs + run: cargo regress test -c ${{ matrix.chip }} --docs --clippy ${{ matrix.options }} ci-serde: runs-on: ubuntu-latest diff --git a/ci/svd2rust-regress/src/command.rs b/ci/svd2rust-regress/src/command.rs index 2c873bb8..2786166c 100644 --- a/ci/svd2rust-regress/src/command.rs +++ b/ci/svd2rust-regress/src/command.rs @@ -7,7 +7,8 @@ pub trait CommandExt { fn run(&mut self, hide: bool) -> Result<(), anyhow::Error>; #[track_caller] - fn get_output(&mut self, can_fail: bool) -> Result; + fn run_and_get_output(&mut self, can_fail: bool) + -> Result; #[track_caller] fn get_output_string(&mut self) -> Result; @@ -33,7 +34,10 @@ impl CommandExt for Command { } #[track_caller] - fn get_output(&mut self, can_fail: bool) -> Result { + fn run_and_get_output( + &mut self, + can_fail: bool, + ) -> Result { let output = self .output() .with_context(|| format!("command `{}` couldn't be run", self.display()))?; @@ -51,7 +55,7 @@ impl CommandExt for Command { #[track_caller] fn get_output_string(&mut self) -> Result { - String::from_utf8(self.get_output(true)?.stdout).map_err(Into::into) + String::from_utf8(self.run_and_get_output(true)?.stdout).map_err(Into::into) } fn display(&self) -> String { diff --git a/ci/svd2rust-regress/src/github.rs b/ci/svd2rust-regress/src/github.rs index 5dc68508..5a9bbf2b 100644 --- a/ci/svd2rust-regress/src/github.rs +++ b/ci/svd2rust-regress/src/github.rs @@ -148,7 +148,7 @@ pub fn get_release_binary_artifact( Command::new("gzip") .arg("-d") .arg(output_dir.join(artifact)) - .get_output(false)?; + .run_and_get_output(false)?; } } _ => { diff --git a/ci/svd2rust-regress/src/main.rs b/ci/svd2rust-regress/src/main.rs index 22b03985..938f8436 100644 --- a/ci/svd2rust-regress/src/main.rs +++ b/ci/svd2rust-regress/src/main.rs @@ -90,6 +90,14 @@ pub struct TestAll { /// Enable splitting `lib.rs` with `form` pub form_lib: bool, + /// Check generated crates with clippy. + #[clap(long)] + pub clippy: bool, + + /// Check documentation build. + #[clap(long)] + pub docs: bool, + /// Print all available test using the specified filters #[clap(long)] pub list: bool, @@ -143,6 +151,13 @@ pub struct Test { /// Chip to use, use `--url` or `--svd-file` for another way to specify svd pub chip: Option, + /// Check generated crate with clippy. + #[arg(long)] + pub clippy: bool, + /// Check documentation build. + #[arg(long)] + pub docs: bool, + /// Path to an `svd2rust` binary, relative or absolute. /// Defaults to `target/release/svd2rust[.exe]` of this repository /// (which must be already built) @@ -191,7 +206,13 @@ impl Test { .ok_or_else(|| anyhow::anyhow!("no test found for chip"))? .to_owned() }; - test.test(opts, &self.current_bin_path, &self.passthrough_opts)?; + test.test( + opts, + &self.current_bin_path, + self.clippy, + self.docs, + &self.passthrough_opts, + )?; Ok(()) } } @@ -247,7 +268,13 @@ impl TestAll { tests.par_iter().for_each(|t| { let start = Instant::now(); - match t.test(opt, &self.current_bin_path, &self.passthrough_opts) { + match t.test( + opt, + &self.current_bin_path, + self.clippy, + self.docs, + &self.passthrough_opts, + ) { Ok(s) => { if let Some(stderrs) = s { let mut buf = String::new(); diff --git a/ci/svd2rust-regress/src/svd_test.rs b/ci/svd2rust-regress/src/svd_test.rs index 4801bd3e..708c6c68 100644 --- a/ci/svd2rust-regress/src/svd_test.rs +++ b/ci/svd2rust-regress/src/svd_test.rs @@ -71,7 +71,7 @@ impl std::fmt::Debug for ProcessFailed { } trait CommandHelper { - fn capture_outputs( + fn run_and_capture_outputs( &mut self, cant_fail: bool, name: &str, @@ -79,11 +79,27 @@ trait CommandHelper { stderr: Option<&PathBuf>, previous_processes_stderr: &[PathBuf], ) -> Result<(), TestError>; + + fn run_and_capture_stderr( + &mut self, + cant_fail: bool, + name: &str, + stderr: &PathBuf, + previous_processes_stderr: &[PathBuf], + ) -> Result<(), TestError> { + self.run_and_capture_outputs( + cant_fail, + name, + None, + Some(stderr), + previous_processes_stderr, + ) + } } impl CommandHelper for Command { #[tracing::instrument(skip_all, fields(stdout = tracing::field::Empty, stderr = tracing::field::Empty))] - fn capture_outputs( + fn run_and_capture_outputs( &mut self, cant_fail: bool, name: &str, @@ -91,7 +107,7 @@ impl CommandHelper for Command { stderr: Option<&PathBuf>, previous_processes_stderr: &[PathBuf], ) -> Result<(), TestError> { - let output = self.get_output(true)?; + let output = self.run_and_get_output(true)?; let out_payload = String::from_utf8_lossy(&output.stdout); if let Some(out) = stdout { file_helper(&out_payload, out)?; @@ -142,10 +158,12 @@ impl TestCase { &self, opts: &Opts, bin_path: &Path, - cli_opts: &Option>, + run_clippy: bool, + run_docs: bool, + cli_passthrough_opts: &Option>, ) -> Result>, TestError> { let (chip_dir, mut process_stderr_paths) = self - .setup_case(&opts.output_dir, bin_path, cli_opts) + .setup_case(&opts.output_dir, bin_path, cli_passthrough_opts) .with_context(|| anyhow!("when setting up case for {}", self.name()))?; // Run `cargo check`, capturing stderr to a log file if !self.skip_check { @@ -153,16 +171,53 @@ impl TestCase { Command::new("cargo") .arg("check") .current_dir(&chip_dir) - .capture_outputs( + .run_and_capture_stderr( true, "cargo check", - None, - Some(&cargo_check_err_file), + &cargo_check_err_file, &process_stderr_paths, ) - .with_context(|| "failed to check")?; + .with_context(|| "failed to check with cargo check")?; process_stderr_paths.push(cargo_check_err_file); } + if run_docs { + tracing::info!("Checking docs build"); + let cargo_docs_err_file = path_helper_base(&chip_dir, &["cargo-docs.err.log"]); + // Docs are built like docs.rs would build them. Additionally, build with all features. + + // Set the RUSTDOCFLAGS environment variable + let rustdocflags = "--cfg docsrs --generate-link-to-definition -Z unstable-options"; + Command::new("cargo") + .arg("+nightly") + .arg("doc") + .arg("--all-features") + .env("RUSTDOCFLAGS", rustdocflags) // Set the environment variable + .current_dir(&chip_dir) + .run_and_capture_stderr( + true, + "cargo docs", + &cargo_docs_err_file, + &process_stderr_paths, + ) + .with_context(|| "failed to generate docs with cargo docs")?; + } + if run_clippy { + tracing::info!("Checking with clippy"); + let cargo_clippy_err_file = path_helper_base(&chip_dir, &["cargo-clippy.err.log"]); + Command::new("cargo") + .arg("clippy") + .arg("--") + .arg("-D") + .arg("warnings") + .current_dir(&chip_dir) + .run_and_capture_stderr( + true, + "cargo clippy", + &cargo_clippy_err_file, + &process_stderr_paths, + ) + .with_context(|| "failed to check with cargo clippy")?; + } Ok(if opts.verbose > 1 { Some(process_stderr_paths) } else { @@ -170,13 +225,13 @@ impl TestCase { }) } - #[tracing::instrument(skip(self, output_dir, command), fields(name = %self.name(), chip_dir = tracing::field::Empty))] + #[tracing::instrument(skip(self, output_dir, passthrough_opts), fields(name = %self.name(), chip_dir = tracing::field::Empty))] pub fn setup_case( &self, output_dir: &Path, svd2rust_bin_path: &Path, - command: &Option>, + passthrough_opts: &Option>, ) -> Result<(PathBuf, Vec), TestError> { let user = match std::env::var("USER") { Ok(val) => val, @@ -209,10 +264,10 @@ impl TestCase { .arg("none") .arg("--lib") .arg(&chip_dir) - .capture_outputs(true, "cargo init", None, None, &[]) + .run_and_capture_outputs(true, "cargo init", None, None, &[]) .with_context(|| "Failed to cargo init")?; - self.prepare_chip_test_toml(&chip_dir, command)?; + self.prepare_chip_test_toml(&chip_dir, passthrough_opts)?; let chip_svd = self.prepare_svd_file(&chip_dir)?; self.prepare_rust_toolchain_file(&chip_dir)?; @@ -225,7 +280,7 @@ impl TestCase { &chip_dir, &lib_rs_file, &svd2rust_err_file, - command, + passthrough_opts, )?; process_stderr_paths.push(svd2rust_err_file); match self.arch { @@ -261,7 +316,7 @@ impl TestCase { .arg(&new_lib_rs_file) .arg("--outdir") .arg(&src_dir) - .capture_outputs( + .run_and_capture_outputs( true, "form", None, @@ -290,7 +345,7 @@ impl TestCase { Command::new(rustfmt_bin_path) .arg(entry) .args(["--edition", "2021"]) - .capture_outputs( + .run_and_capture_outputs( false, "rustfmt", None, @@ -416,7 +471,7 @@ impl TestCase { if let Some(opts) = self.opts.as_ref() { base_cmd.args(opts); } - base_cmd.current_dir(chip_dir).capture_outputs( + base_cmd.current_dir(chip_dir).run_and_capture_outputs( true, "svd2rust", Some(lib_rs_file).filter(|_| {