|  | @@ -6,7 +6,7 @@ use std::{
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  use anyhow::{bail, Context as _, Result};
 | 
	
		
			
				|  |  | -use cargo_metadata::{Metadata, Package};
 | 
	
		
			
				|  |  | +use cargo_metadata::{Metadata, Package, Target};
 | 
	
		
			
				|  |  |  use clap::Parser;
 | 
	
		
			
				|  |  |  use dialoguer::{theme::ColorfulTheme, Confirm};
 | 
	
		
			
				|  |  |  use diff::{lines, Result as Diff};
 | 
	
	
		
			
				|  | @@ -17,11 +17,15 @@ pub struct Options {
 | 
	
		
			
				|  |  |      /// Bless new API changes.
 | 
	
		
			
				|  |  |      #[clap(long)]
 | 
	
		
			
				|  |  |      pub bless: bool,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /// Bless new API changes.
 | 
	
		
			
				|  |  | +    #[clap(long)]
 | 
	
		
			
				|  |  | +    pub target: Option<String>,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
 | 
	
		
			
				|  |  |      let toolchain = "nightly";
 | 
	
		
			
				|  |  | -    let Options { bless } = options;
 | 
	
		
			
				|  |  | +    let Options { bless, target } = options;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if !rustup_toolchain::is_installed(toolchain)? {
 | 
	
		
			
				|  |  |          if Confirm::with_theme(&ColorfulTheme::default())
 | 
	
	
		
			
				|  | @@ -42,21 +46,40 @@ pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      let errors: Vec<_> = packages
 | 
	
		
			
				|  |  |          .into_iter()
 | 
	
		
			
				|  |  | -        .map(|Package { name, publish, .. }| {
 | 
	
		
			
				|  |  | -            if matches!(publish, Some(publish) if publish.is_empty()) {
 | 
	
		
			
				|  |  | -                Ok(())
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | -                let diff = check_package_api(&name, toolchain, bless, workspace_root.as_std_path())
 | 
	
		
			
				|  |  | -                    .with_context(|| format!("{name} failed to check public API"))?;
 | 
	
		
			
				|  |  | -                if diff.is_empty() {
 | 
	
		
			
				|  |  | +        .map(
 | 
	
		
			
				|  |  | +            |Package {
 | 
	
		
			
				|  |  | +                 name,
 | 
	
		
			
				|  |  | +                 publish,
 | 
	
		
			
				|  |  | +                 targets,
 | 
	
		
			
				|  |  | +                 ..
 | 
	
		
			
				|  |  | +             }| {
 | 
	
		
			
				|  |  | +                if matches!(publish, Some(publish) if publish.is_empty()) {
 | 
	
		
			
				|  |  |                      Ok(())
 | 
	
		
			
				|  |  |                  } else {
 | 
	
		
			
				|  |  | -                    Err(anyhow::anyhow!(
 | 
	
		
			
				|  |  | -                        "{name} public API changed; re-run with --bless. diff:\n{diff}"
 | 
	
		
			
				|  |  | -                    ))
 | 
	
		
			
				|  |  | +                    let target = target.as_ref().and_then(|target| {
 | 
	
		
			
				|  |  | +                        let proc_macro = targets.iter().any(|Target { kind, .. }| {
 | 
	
		
			
				|  |  | +                            kind.iter().any(|kind| kind == "proc-macro")
 | 
	
		
			
				|  |  | +                        });
 | 
	
		
			
				|  |  | +                        (!proc_macro).then_some(target)
 | 
	
		
			
				|  |  | +                    });
 | 
	
		
			
				|  |  | +                    let diff = check_package_api(
 | 
	
		
			
				|  |  | +                        &name,
 | 
	
		
			
				|  |  | +                        toolchain,
 | 
	
		
			
				|  |  | +                        target.cloned(),
 | 
	
		
			
				|  |  | +                        bless,
 | 
	
		
			
				|  |  | +                        workspace_root.as_std_path(),
 | 
	
		
			
				|  |  | +                    )
 | 
	
		
			
				|  |  | +                    .with_context(|| format!("{name} failed to check public API"))?;
 | 
	
		
			
				|  |  | +                    if diff.is_empty() {
 | 
	
		
			
				|  |  | +                        Ok(())
 | 
	
		
			
				|  |  | +                    } else {
 | 
	
		
			
				|  |  | +                        Err(anyhow::anyhow!(
 | 
	
		
			
				|  |  | +                            "{name} public API changed; re-run with --bless. diff:\n{diff}"
 | 
	
		
			
				|  |  | +                        ))
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        })
 | 
	
		
			
				|  |  | +            },
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  |          .filter_map(|result| match result {
 | 
	
		
			
				|  |  |              Ok(()) => None,
 | 
	
		
			
				|  |  |              Err(err) => Some(err),
 | 
	
	
		
			
				|  | @@ -73,6 +96,7 @@ pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
 | 
	
		
			
				|  |  |  fn check_package_api(
 | 
	
		
			
				|  |  |      package: &str,
 | 
	
		
			
				|  |  |      toolchain: &str,
 | 
	
		
			
				|  |  | +    target: Option<String>,
 | 
	
		
			
				|  |  |      bless: bool,
 | 
	
		
			
				|  |  |      workspace_root: &Path,
 | 
	
		
			
				|  |  |  ) -> Result<String> {
 | 
	
	
		
			
				|  | @@ -82,16 +106,28 @@ fn check_package_api(
 | 
	
		
			
				|  |  |          .join(package)
 | 
	
		
			
				|  |  |          .with_extension("txt");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    let rustdoc_json = rustdoc_json::Builder::default()
 | 
	
		
			
				|  |  | +    let mut builder = rustdoc_json::Builder::default()
 | 
	
		
			
				|  |  |          .toolchain(toolchain)
 | 
	
		
			
				|  |  |          .package(package)
 | 
	
		
			
				|  |  | -        .all_features(true)
 | 
	
		
			
				|  |  | -        .build()
 | 
	
		
			
				|  |  | -        .context("rustdoc_json::Builder::build")?;
 | 
	
		
			
				|  |  | +        .all_features(true);
 | 
	
		
			
				|  |  | +    if let Some(target) = target {
 | 
	
		
			
				|  |  | +        builder = builder.target(target);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    let rustdoc_json = builder.build().with_context(|| {
 | 
	
		
			
				|  |  | +        format!(
 | 
	
		
			
				|  |  | +            "rustdoc_json::Builder::default().toolchain({}).package({}).build()",
 | 
	
		
			
				|  |  | +            toolchain, package
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  | +    })?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    let public_api = public_api::Builder::from_rustdoc_json(rustdoc_json)
 | 
	
		
			
				|  |  | +    let public_api = public_api::Builder::from_rustdoc_json(&rustdoc_json)
 | 
	
		
			
				|  |  |          .build()
 | 
	
		
			
				|  |  | -        .context("public_api::Builder::build")?;
 | 
	
		
			
				|  |  | +        .with_context(|| {
 | 
	
		
			
				|  |  | +            format!(
 | 
	
		
			
				|  |  | +                "public_api::Builder::from_rustdoc_json({})::build()",
 | 
	
		
			
				|  |  | +                rustdoc_json.display()
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +        })?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if bless {
 | 
	
		
			
				|  |  |          let mut output =
 |