mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 21:00:59 +00:00
Semver-Check (#3439)
* Semver-Check * Clippy * Improve * Fix * Adjust log level * Add initial API baseline * CI * Fix * Docs * Debug CI * Honor CARGO_TARGET_DIR * Compressed API baseline * Renaming * Refactor * Move code to commands mod * Prepare for 1.85 * Address review comments * Simplify
This commit is contained in:
parent
00e1cf7d7d
commit
7b7844a855
@ -1,5 +1,6 @@
|
|||||||
[alias]
|
[alias]
|
||||||
xtask = "run --package xtask --"
|
xtask = "run --package xtask --"
|
||||||
xdoc = "run --package xtask --features=deploy-docs,preview-docs --"
|
xdoc = "run --package xtask --features=deploy-docs,preview-docs --"
|
||||||
xfmt = "xtask fmt-packages"
|
xfmt = "xtask fmt-packages"
|
||||||
qa = "xtask run example qa-test"
|
qa = "xtask run example qa-test"
|
||||||
|
xcheck = "run --package xtask --features=semver-checks --"
|
||||||
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -96,6 +96,10 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: cargo +${{ matrix.device.toolchain }} xtask ci ${{ matrix.device.soc }}
|
run: cargo +${{ matrix.device.toolchain }} xtask ci ${{ matrix.device.soc }}
|
||||||
|
|
||||||
|
- name: Semver-Check
|
||||||
|
shell: bash
|
||||||
|
run: cargo +${{ matrix.device.toolchain }} xcheck semver-check --chips ${{ matrix.device.soc }} check
|
||||||
|
|
||||||
extras:
|
extras:
|
||||||
runs-on: macos-m1-self-hosted
|
runs-on: macos-m1-self-hosted
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ repository = "https://github.com/esp-rs/esp-hal"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
links = "esp-hal"
|
links = "esp-hal"
|
||||||
|
|
||||||
|
exclude = [ "api-baseline", "MIGRATING-*", "CHANGELOG.md" ]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
default-target = "riscv32imac-unknown-none-elf"
|
default-target = "riscv32imac-unknown-none-elf"
|
||||||
features = ["esp32c6"]
|
features = ["esp32c6"]
|
||||||
|
BIN
esp-hal/api-baseline/esp32.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32.json.gz
Normal file
Binary file not shown.
BIN
esp-hal/api-baseline/esp32c2.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32c2.json.gz
Normal file
Binary file not shown.
BIN
esp-hal/api-baseline/esp32c3.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32c3.json.gz
Normal file
Binary file not shown.
BIN
esp-hal/api-baseline/esp32c6.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32c6.json.gz
Normal file
Binary file not shown.
BIN
esp-hal/api-baseline/esp32h2.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32h2.json.gz
Normal file
Binary file not shown.
BIN
esp-hal/api-baseline/esp32s2.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32s2.json.gz
Normal file
Binary file not shown.
BIN
esp-hal/api-baseline/esp32s3.json.gz
Normal file
BIN
esp-hal/api-baseline/esp32s3.json.gz
Normal file
Binary file not shown.
@ -728,7 +728,7 @@ macro_rules! io_type {
|
|||||||
(Analog, $gpionum:literal) => {
|
(Analog, $gpionum:literal) => {
|
||||||
// FIXME: the implementation shouldn't be in the GPIO module
|
// FIXME: the implementation shouldn't be in the GPIO module
|
||||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
|
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||||
impl $crate::gpio::AnalogPin for paste::paste!( [<GPIO $gpionum>]<'_> ) {
|
impl $crate::gpio::AnalogPin for paste::paste!( [<GPIO $gpionum>]<'_> ) {
|
||||||
/// Configures the pin for analog mode.
|
/// Configures the pin for analog mode.
|
||||||
|
@ -576,14 +576,16 @@ pub struct Config {
|
|||||||
cpu_clock: CpuClock,
|
cpu_clock: CpuClock,
|
||||||
|
|
||||||
/// Enable watchdog timer(s).
|
/// Enable watchdog timer(s).
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||||
|
#[builder_lite(unstable)]
|
||||||
watchdog: WatchdogConfig,
|
watchdog: WatchdogConfig,
|
||||||
|
|
||||||
/// PSRAM configuration.
|
/// PSRAM configuration.
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||||
#[cfg(feature = "psram")]
|
#[cfg(feature = "psram")]
|
||||||
|
#[builder_lite(unstable)]
|
||||||
psram: psram::PsramConfig,
|
psram: psram::PsramConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ macro_rules! peripherals {
|
|||||||
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
|
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
|
||||||
crate feature is enabled. This comes with no stability guarantees, and could be changed
|
crate feature is enabled. This comes with no stability guarantees, and could be changed
|
||||||
or removed at any time."]
|
or removed at any time."]
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||||
pub $unstable_name: $unstable_name<'static>,
|
pub $unstable_name: $unstable_name<'static>,
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ macro_rules! peripherals {
|
|||||||
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
|
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
|
||||||
crate feature is enabled. This comes with no stability guarantees, and could be changed
|
crate feature is enabled. This comes with no stability guarantees, and could be changed
|
||||||
or removed at any time."]
|
or removed at any time."]
|
||||||
#[cfg(not(any(doc, feature = "unstable")))]
|
#[cfg(not(feature = "unstable"))]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) $unstable_name: $unstable_name<'static>,
|
pub(crate) $unstable_name: $unstable_name<'static>,
|
||||||
)*
|
)*
|
||||||
|
@ -2469,10 +2469,10 @@ mod dma {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod ehal1 {
|
mod ehal1 {
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
use embedded_hal::spi::{ErrorType, SpiBus};
|
use embedded_hal::spi::{ErrorType, SpiBus};
|
||||||
|
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[instability::unstable]
|
#[instability::unstable]
|
||||||
|
@ -23,7 +23,7 @@ crate::unstable_module! {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Error occurred due to a DMA-related issue.
|
/// Error occurred due to a DMA-related issue.
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||||
#[allow(clippy::enum_variant_names, reason = "DMA is unstable")]
|
#[allow(clippy::enum_variant_names, reason = "DMA is unstable")]
|
||||||
DmaError(DmaError),
|
DmaError(DmaError),
|
||||||
@ -48,7 +48,7 @@ impl From<DmaError> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(not(any(doc, feature = "unstable")))]
|
#[cfg(not(feature = "unstable"))]
|
||||||
impl From<DmaError> for Error {
|
impl From<DmaError> for Error {
|
||||||
fn from(_value: DmaError) -> Self {
|
fn from(_value: DmaError) -> Self {
|
||||||
Error::Unknown
|
Error::Unknown
|
||||||
|
@ -450,7 +450,7 @@ pub struct UartRx<'d, Dm: DriverMode> {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
/// The requested baud rate is not achievable.
|
/// The requested baud rate is not achievable.
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
#[cfg(feature = "unstable")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||||
UnachievableBaudrate,
|
UnachievableBaudrate,
|
||||||
|
|
||||||
|
@ -30,6 +30,16 @@ reqwest = { version = "0.12.12", features = [
|
|||||||
"native-tls-vendored",
|
"native-tls-vendored",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
|
|
||||||
|
# This pulls a gazillion crates - don't include it by default
|
||||||
|
cargo-semver-checks = { version = "0.41.0", optional = true }
|
||||||
|
|
||||||
|
# THIS NEEDS TO MATCH THE rustfmt-json FORMAT - see https://github.com/rust-lang/rustdoc-types/blob/trunk/CHANGELOG.md
|
||||||
|
rustdoc-types = { version = "0.35.0", optional = true }
|
||||||
|
|
||||||
|
flate2 = { version = "1.1.1", optional = true }
|
||||||
|
temp-file = { version = "0.1.9", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
deploy-docs = ["dep:reqwest"]
|
deploy-docs = ["dep:reqwest"]
|
||||||
preview-docs = ["dep:opener", "dep:rocket"]
|
preview-docs = ["dep:opener", "dep:rocket"]
|
||||||
|
semver-checks = [ "dep:cargo-semver-checks", "dep:rustdoc-types", "dep:flate2", "dep:temp-file" ]
|
||||||
|
@ -4,12 +4,13 @@ use anyhow::{Result, bail};
|
|||||||
use clap::Args;
|
use clap::Args;
|
||||||
use esp_metadata::Chip;
|
use esp_metadata::Chip;
|
||||||
|
|
||||||
pub use self::{build::*, bump_version::*, run::*};
|
pub use self::{build::*, bump_version::*, run::*, semver_check::*};
|
||||||
use crate::{Package, cargo::CargoAction};
|
use crate::{Package, cargo::CargoAction};
|
||||||
|
|
||||||
mod build;
|
mod build;
|
||||||
mod bump_version;
|
mod bump_version;
|
||||||
mod run;
|
mod run;
|
||||||
|
mod semver_check;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Subcommand Arguments
|
// Subcommand Arguments
|
||||||
|
145
xtask/src/commands/semver_check.rs
Normal file
145
xtask/src/commands/semver_check.rs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use clap::{Args, Subcommand};
|
||||||
|
use esp_metadata::Chip;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::Package;
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum SemverCheckCmd {
|
||||||
|
GenerateBaseline,
|
||||||
|
Check,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
pub struct SemverCheckArgs {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: SemverCheckCmd,
|
||||||
|
|
||||||
|
/// Package(s) to target.
|
||||||
|
#[arg(long, value_enum, value_delimiter = ',', default_values_t = vec![Package::EspHal])]
|
||||||
|
pub packages: Vec<Package>,
|
||||||
|
|
||||||
|
/// Chip(s) to target.
|
||||||
|
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Chip::iter())]
|
||||||
|
pub chips: Vec<Chip>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn semver_checks(workspace: &Path, args: SemverCheckArgs) -> anyhow::Result<()> {
|
||||||
|
#[cfg(not(feature = "semver-checks"))]
|
||||||
|
{
|
||||||
|
let _ = workspace;
|
||||||
|
let _ = args;
|
||||||
|
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Feature `semver-checks` is not enabled. Use the `xcheck` alias",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "semver-checks")]
|
||||||
|
match args.command {
|
||||||
|
SemverCheckCmd::GenerateBaseline => {
|
||||||
|
checker::generate_baseline(&workspace, args.packages, args.chips)
|
||||||
|
}
|
||||||
|
SemverCheckCmd::Check => {
|
||||||
|
checker::check_for_breaking_changes(&workspace, args.packages, args.chips)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "semver-checks")]
|
||||||
|
pub mod checker {
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Write,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use cargo_semver_checks::ReleaseType;
|
||||||
|
use esp_metadata::Chip;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Package,
|
||||||
|
semver_check::{build_doc_json, minimum_update, remove_unstable_items},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate_baseline(
|
||||||
|
workspace: &Path,
|
||||||
|
packages: Vec<Package>,
|
||||||
|
chips: Vec<Chip>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
for package in packages {
|
||||||
|
log::info!("Generating API baseline for {package}");
|
||||||
|
|
||||||
|
for chip in &chips {
|
||||||
|
log::info!("Chip = {}", chip.to_string());
|
||||||
|
let package_name = package.to_string();
|
||||||
|
let package_path = crate::windows_safe_path(&workspace.join(&package_name));
|
||||||
|
|
||||||
|
let current_path = build_doc_json(package, chip, &package_path)?;
|
||||||
|
|
||||||
|
remove_unstable_items(¤t_path)?;
|
||||||
|
|
||||||
|
let file_name = if package.chip_features_matter() {
|
||||||
|
chip.to_string()
|
||||||
|
} else {
|
||||||
|
"api".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_path = PathBuf::from(&package_path)
|
||||||
|
.join(format!("api-baseline/{}.json.gz", file_name));
|
||||||
|
fs::create_dir_all(to_path.parent().unwrap())?;
|
||||||
|
|
||||||
|
log::debug!("Compress into {current_path:?}");
|
||||||
|
let mut encoder = flate2::write::GzEncoder::new(
|
||||||
|
fs::File::create(to_path)?,
|
||||||
|
flate2::Compression::default(),
|
||||||
|
);
|
||||||
|
encoder.write_all(&std::fs::read(current_path)?)?;
|
||||||
|
|
||||||
|
if !package.chip_features_matter() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_for_breaking_changes(
|
||||||
|
workspace: &Path,
|
||||||
|
packages: Vec<Package>,
|
||||||
|
chips: Vec<Chip>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let mut semver_incompatible_packages = Vec::new();
|
||||||
|
|
||||||
|
for package in packages {
|
||||||
|
log::info!("Semver-check API for {package}");
|
||||||
|
|
||||||
|
for chip in &chips {
|
||||||
|
let result = minimum_update(workspace, package, *chip)?;
|
||||||
|
log::info!("Required bump = {:?}", result);
|
||||||
|
|
||||||
|
if result == ReleaseType::Major {
|
||||||
|
semver_incompatible_packages.push(package.to_string());
|
||||||
|
// no need to check other chips for this package
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !package.chip_features_matter() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !semver_incompatible_packages.is_empty() {
|
||||||
|
Err(anyhow::anyhow!(
|
||||||
|
"Semver check failed - needs a major bump: {}",
|
||||||
|
semver_incompatible_packages.join(", ")
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,9 @@ pub mod commands;
|
|||||||
pub mod documentation;
|
pub mod documentation;
|
||||||
pub mod firmware;
|
pub mod firmware;
|
||||||
|
|
||||||
|
#[cfg(feature = "semver-checks")]
|
||||||
|
pub mod semver_check;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
|
@ -15,6 +15,8 @@ use xtask::{
|
|||||||
commands::*,
|
commands::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Command-line Interface
|
// Command-line Interface
|
||||||
|
|
||||||
@ -40,6 +42,8 @@ enum Cli {
|
|||||||
Publish(PublishArgs),
|
Publish(PublishArgs),
|
||||||
/// Generate git tags for all new package releases.
|
/// Generate git tags for all new package releases.
|
||||||
TagReleases(TagReleasesArgs),
|
TagReleases(TagReleasesArgs),
|
||||||
|
/// Semver Checks
|
||||||
|
SemverCheck(SemverCheckArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
@ -137,6 +141,7 @@ fn main() -> Result<()> {
|
|||||||
Cli::LintPackages(args) => lint_packages(&workspace, args),
|
Cli::LintPackages(args) => lint_packages(&workspace, args),
|
||||||
Cli::Publish(args) => publish(&workspace, args),
|
Cli::Publish(args) => publish(&workspace, args),
|
||||||
Cli::TagReleases(args) => tag_releases(&workspace, args),
|
Cli::TagReleases(args) => tag_releases(&workspace, args),
|
||||||
|
Cli::SemverCheck(args) => semver_checks(&workspace, args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
220
xtask/src/semver_check.rs
Normal file
220
xtask/src/semver_check.rs
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Write,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use cargo_semver_checks::{Check, GlobalConfig, ReleaseType, Rustdoc};
|
||||||
|
use esp_metadata::Chip;
|
||||||
|
use rustdoc_types::ItemEnum;
|
||||||
|
|
||||||
|
use crate::{Package, cargo::CargoArgsBuilder};
|
||||||
|
|
||||||
|
/// Return the minimum required bump for the next release.
|
||||||
|
/// Even if nothing changed this will be [ReleaseType::Patch]
|
||||||
|
pub fn minimum_update(
|
||||||
|
workspace: &Path,
|
||||||
|
package: Package,
|
||||||
|
chip: Chip,
|
||||||
|
) -> Result<ReleaseType, anyhow::Error> {
|
||||||
|
log::info!("Chip = {}", chip.to_string());
|
||||||
|
|
||||||
|
let package_name = package.to_string();
|
||||||
|
let package_path = crate::windows_safe_path(&workspace.join(&package_name));
|
||||||
|
let current_path = build_doc_json(package, &chip, &package_path)?;
|
||||||
|
remove_unstable_items(¤t_path)?;
|
||||||
|
|
||||||
|
let file_name = if package.chip_features_matter() {
|
||||||
|
chip.to_string()
|
||||||
|
} else {
|
||||||
|
"api".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let baseline_path_gz =
|
||||||
|
PathBuf::from(&package_path).join(format!("api-baseline/{}.json.gz", file_name));
|
||||||
|
let baseline_path = temp_file::TempFile::new()?;
|
||||||
|
let buffer = Vec::new();
|
||||||
|
let mut decoder = flate2::write::GzDecoder::new(buffer);
|
||||||
|
decoder.write_all(&(fs::read(&baseline_path_gz)?))?;
|
||||||
|
fs::write(baseline_path.path(), decoder.finish()?)?;
|
||||||
|
|
||||||
|
let mut semver_check = Check::new(Rustdoc::from_path(current_path));
|
||||||
|
semver_check.set_baseline(Rustdoc::from_path(baseline_path.path()));
|
||||||
|
let mut cfg = GlobalConfig::new();
|
||||||
|
cfg.set_log_level(Some(log::Level::Info));
|
||||||
|
let result = semver_check.check_release(&mut cfg)?;
|
||||||
|
log::info!("Result {:?}", result);
|
||||||
|
|
||||||
|
let mut min_required_update = ReleaseType::Patch;
|
||||||
|
for (_, report) in result.crate_reports() {
|
||||||
|
if let Some(required_bump) = report.required_bump() {
|
||||||
|
let required_is_stricter = (min_required_update == ReleaseType::Patch)
|
||||||
|
|| (required_bump == ReleaseType::Major);
|
||||||
|
if required_is_stricter {
|
||||||
|
min_required_update = required_bump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(min_required_update)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_doc_json(
|
||||||
|
package: Package,
|
||||||
|
chip: &Chip,
|
||||||
|
package_path: &PathBuf,
|
||||||
|
) -> Result<PathBuf, anyhow::Error> {
|
||||||
|
let target_dir = std::env::var("CARGO_TARGET_DIR");
|
||||||
|
|
||||||
|
let target_path = if let Ok(target) = target_dir {
|
||||||
|
PathBuf::from(target)
|
||||||
|
} else {
|
||||||
|
PathBuf::from(package_path).join("target")
|
||||||
|
};
|
||||||
|
let current_path = target_path
|
||||||
|
.join(chip.target())
|
||||||
|
.join("doc")
|
||||||
|
.join(format!("{}.json", package.to_string().replace("-", "_")));
|
||||||
|
|
||||||
|
std::fs::remove_file(¤t_path).ok();
|
||||||
|
let features = if package.chip_features_matter() {
|
||||||
|
vec![chip.to_string(), "unstable".to_string()]
|
||||||
|
} else {
|
||||||
|
vec!["unstable".to_string()]
|
||||||
|
};
|
||||||
|
|
||||||
|
// always use `esp` toolchain so we don't have to deal with potentially
|
||||||
|
// different versions of the doc-json
|
||||||
|
let mut cargo_builder = CargoArgsBuilder::default()
|
||||||
|
.toolchain("esp")
|
||||||
|
.subcommand("rustdoc")
|
||||||
|
.features(&features)
|
||||||
|
.target(chip.target())
|
||||||
|
.arg("-Zunstable-options")
|
||||||
|
.arg("--lib")
|
||||||
|
.arg("--output-format=json");
|
||||||
|
cargo_builder = cargo_builder.arg("-Zbuild-std=alloc,core");
|
||||||
|
let cargo_args = cargo_builder.build();
|
||||||
|
log::debug!("{cargo_args:#?}");
|
||||||
|
crate::cargo::run(&cargo_args, package_path)?;
|
||||||
|
Ok(current_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove_unstable_items(path: &Path) -> Result<(), anyhow::Error> {
|
||||||
|
// this leaves orphaned items! cargo-semver-checks seems to be fine with that
|
||||||
|
// however the json fmt is unstable - we might fail when using the "wrong"
|
||||||
|
// version of `rustdoc_types` here
|
||||||
|
//
|
||||||
|
// Hopefully this whole pre-processing is just a stop-gap solution until it's
|
||||||
|
// possible to generate docs for the stable-API only.
|
||||||
|
|
||||||
|
log::info!("{:?}", path);
|
||||||
|
let json_string = std::fs::read_to_string(path)?;
|
||||||
|
let mut krate: rustdoc_types::Crate = serde_json::from_str(&json_string)?;
|
||||||
|
|
||||||
|
let mut to_remove = vec![];
|
||||||
|
|
||||||
|
// first pass - just look for cfg-gated items
|
||||||
|
//
|
||||||
|
// the string to match depends on the rustfmt-json version!
|
||||||
|
// later version emit `#[<cfg>(...` instead
|
||||||
|
let cfg_gates = vec![
|
||||||
|
"#[cfg(any(doc, feature = \"unstable\"))]",
|
||||||
|
"#[cfg(feature = \"unstable\")]",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (id, item) in &mut krate.index {
|
||||||
|
if item
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.any(|attr| cfg_gates.contains(&attr.as_str()))
|
||||||
|
{
|
||||||
|
to_remove.push(id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("Items to remove {:?}", to_remove);
|
||||||
|
|
||||||
|
for id in &to_remove {
|
||||||
|
krate.index.remove(&id);
|
||||||
|
krate.paths.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_id, item) in &mut krate.index {
|
||||||
|
match &mut item.inner {
|
||||||
|
ItemEnum::Module(module) => {
|
||||||
|
module.items.retain(|id| !to_remove.contains(id));
|
||||||
|
}
|
||||||
|
ItemEnum::Struct(struct_) => {
|
||||||
|
struct_.impls.retain(|id| !to_remove.contains(id));
|
||||||
|
|
||||||
|
match &mut struct_.kind {
|
||||||
|
rustdoc_types::StructKind::Unit => (),
|
||||||
|
rustdoc_types::StructKind::Tuple(_ids) => (),
|
||||||
|
rustdoc_types::StructKind::Plain {
|
||||||
|
fields,
|
||||||
|
has_stripped_fields: _,
|
||||||
|
} => {
|
||||||
|
for id in &to_remove {
|
||||||
|
if let Some(found) = fields.iter().enumerate().find(|i| i.1 == id) {
|
||||||
|
fields.remove(found.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if struct_.impls.is_empty() {
|
||||||
|
to_remove.push(_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemEnum::Enum(enum_) => {
|
||||||
|
enum_.impls.retain(|id| !to_remove.contains(id));
|
||||||
|
|
||||||
|
enum_.variants.retain(|id| !to_remove.contains(id));
|
||||||
|
|
||||||
|
if enum_.impls.is_empty() {
|
||||||
|
to_remove.push(_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemEnum::Impl(impl_) => {
|
||||||
|
impl_.items.retain(|id| !to_remove.contains(id));
|
||||||
|
|
||||||
|
if impl_.items.is_empty() {
|
||||||
|
to_remove.push(_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't honor (because they either don't contain sub-items (= already handled in the
|
||||||
|
// first pass) or we currently don't use them)
|
||||||
|
//
|
||||||
|
// ItemEnum::Use(_)
|
||||||
|
// ItemEnum::Union(union)
|
||||||
|
// ItemEnum::StructField(_)
|
||||||
|
// ItemEnum::Variant(variant)
|
||||||
|
// ItemEnum::Function(function)
|
||||||
|
// ItemEnum::Trait(_)
|
||||||
|
// ItemEnum::TraitAlias(trait_alias)
|
||||||
|
// ItemEnum::TypeAlias(type_alias)
|
||||||
|
// ItemEnum::Constant {
|
||||||
|
// ItemEnum::Static(_)
|
||||||
|
// ItemEnum::ExternType =>
|
||||||
|
// ItemEnum::Macro(_)
|
||||||
|
// ItemEnum::ProcMacro(proc_macro)
|
||||||
|
// ItemEnum::Primitive(primitive)
|
||||||
|
// ItemEnum::AssocConst {
|
||||||
|
// ItemEnum::AssocType {
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we added something more to remove (because the items are "empty" now -
|
||||||
|
// remove them, too)
|
||||||
|
for id in &to_remove {
|
||||||
|
krate.index.remove(&id);
|
||||||
|
krate.paths.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::write(path, serde_json::to_string(&krate)?)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user