|
1 | 1 | //! Support for future-incompatible warning reporting.
|
2 | 2 |
|
3 |
| -use crate::core::{PackageId, Workspace}; |
| 3 | +use crate::core::compiler::BuildContext; |
| 4 | +use crate::core::{Dependency, PackageId, Workspace}; |
| 5 | +use crate::sources::SourceConfigMap; |
4 | 6 | use crate::util::{iter_join, CargoResult, Config};
|
5 | 7 | use anyhow::{bail, format_err, Context};
|
6 | 8 | use serde::{Deserialize, Serialize};
|
7 |
| -use std::collections::BTreeMap; |
| 9 | +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; |
| 10 | +use std::fmt::Write as _; |
8 | 11 | use std::io::{Read, Write};
|
9 | 12 |
|
10 | 13 | pub const REPORT_PREAMBLE: &str = "\
|
@@ -208,7 +211,7 @@ impl OnDiskReports {
|
208 | 211 | format_err!(
|
209 | 212 | "could not find package with ID `{}`\n
|
210 | 213 | Available packages are: {}\n
|
211 |
| - Omit the `--crate` flag to display a report for all crates", |
| 214 | + Omit the `--package` flag to display a report for all packages", |
212 | 215 | package,
|
213 | 216 | iter_join(report.per_package.keys(), ", ")
|
214 | 217 | )
|
@@ -260,3 +263,186 @@ fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> BTreeMa
|
260 | 263 | }
|
261 | 264 | report
|
262 | 265 | }
|
| 266 | + |
| 267 | +// Returns a pair (compatible_updates, incompatible_updates), |
| 268 | +// of semver-compatible and semver-incompatible update versions, |
| 269 | +// respectively. |
| 270 | +fn get_updates(ws: &Workspace<'_>, package_ids: &BTreeSet<PackageId>) -> Option<String> { |
| 271 | + // This in general ignores all errors since this is opportunistic. |
| 272 | + let _lock = ws.config().acquire_package_cache_lock().ok()?; |
| 273 | + // Create a set of updated registry sources. |
| 274 | + let map = SourceConfigMap::new(ws.config()).ok()?; |
| 275 | + let package_ids: BTreeSet<_> = package_ids |
| 276 | + .iter() |
| 277 | + .filter(|pkg_id| pkg_id.source_id().is_registry()) |
| 278 | + .collect(); |
| 279 | + let source_ids: HashSet<_> = package_ids |
| 280 | + .iter() |
| 281 | + .map(|pkg_id| pkg_id.source_id()) |
| 282 | + .collect(); |
| 283 | + let mut sources: HashMap<_, _> = source_ids |
| 284 | + .into_iter() |
| 285 | + .filter_map(|sid| { |
| 286 | + let source = map.load(sid, &HashSet::new()).ok()?; |
| 287 | + Some((sid, source)) |
| 288 | + }) |
| 289 | + .collect(); |
| 290 | + // Query the sources for new versions. |
| 291 | + let mut updates = String::new(); |
| 292 | + for pkg_id in package_ids { |
| 293 | + let source = match sources.get_mut(&pkg_id.source_id()) { |
| 294 | + Some(s) => s, |
| 295 | + None => continue, |
| 296 | + }; |
| 297 | + let dep = Dependency::parse(pkg_id.name(), None, pkg_id.source_id()).ok()?; |
| 298 | + let summaries = source.query_vec(&dep).ok()?; |
| 299 | + let mut updated_versions: Vec<_> = summaries |
| 300 | + .iter() |
| 301 | + .map(|summary| summary.version()) |
| 302 | + .filter(|version| *version > pkg_id.version()) |
| 303 | + .collect(); |
| 304 | + updated_versions.sort(); |
| 305 | + |
| 306 | + let updated_versions = iter_join( |
| 307 | + updated_versions |
| 308 | + .into_iter() |
| 309 | + .map(|version| version.to_string()), |
| 310 | + ", ", |
| 311 | + ); |
| 312 | + |
| 313 | + if !updated_versions.is_empty() { |
| 314 | + writeln!( |
| 315 | + updates, |
| 316 | + "{} has the following newer versions available: {}", |
| 317 | + pkg_id, updated_versions |
| 318 | + ) |
| 319 | + .unwrap(); |
| 320 | + } |
| 321 | + } |
| 322 | + Some(updates) |
| 323 | +} |
| 324 | + |
| 325 | +pub fn render_message( |
| 326 | + bcx: &BuildContext<'_, '_>, |
| 327 | + per_package_future_incompat_reports: &[FutureIncompatReportPackage], |
| 328 | +) { |
| 329 | + if !bcx.config.cli_unstable().future_incompat_report { |
| 330 | + return; |
| 331 | + } |
| 332 | + let should_display_message = match bcx.config.future_incompat_config() { |
| 333 | + Ok(config) => config.should_display_message(), |
| 334 | + Err(e) => { |
| 335 | + crate::display_warning_with_error( |
| 336 | + "failed to read future-incompat config from disk", |
| 337 | + &e, |
| 338 | + &mut bcx.config.shell(), |
| 339 | + ); |
| 340 | + true |
| 341 | + } |
| 342 | + }; |
| 343 | + |
| 344 | + if per_package_future_incompat_reports.is_empty() { |
| 345 | + // Explicitly passing a command-line flag overrides |
| 346 | + // `should_display_message` from the config file |
| 347 | + if bcx.build_config.future_incompat_report { |
| 348 | + drop( |
| 349 | + bcx.config |
| 350 | + .shell() |
| 351 | + .note("0 dependencies had future-incompatible warnings"), |
| 352 | + ); |
| 353 | + } |
| 354 | + return; |
| 355 | + } |
| 356 | + |
| 357 | + // Get a list of unique and sorted package name/versions. |
| 358 | + let package_ids: BTreeSet<_> = per_package_future_incompat_reports |
| 359 | + .iter() |
| 360 | + .map(|r| r.package_id) |
| 361 | + .collect(); |
| 362 | + let package_vers: Vec<_> = package_ids.iter().map(|pid| pid.to_string()).collect(); |
| 363 | + |
| 364 | + if should_display_message || bcx.build_config.future_incompat_report { |
| 365 | + drop(bcx.config.shell().warn(&format!( |
| 366 | + "the following packages contain code that will be rejected by a future \ |
| 367 | + version of Rust: {}", |
| 368 | + package_vers.join(", ") |
| 369 | + ))); |
| 370 | + } |
| 371 | + |
| 372 | + let updated_versions = get_updates(bcx.ws, &package_ids).unwrap_or(String::new()); |
| 373 | + |
| 374 | + let update_message = if !updated_versions.is_empty() { |
| 375 | + format!( |
| 376 | + " |
| 377 | +- Some affected dependencies have newer versions available. |
| 378 | +You may want to consider updating them to a newer version to see if the issue has been fixed. |
| 379 | +
|
| 380 | +{updated_versions}\n", |
| 381 | + updated_versions = updated_versions |
| 382 | + ) |
| 383 | + } else { |
| 384 | + String::new() |
| 385 | + }; |
| 386 | + |
| 387 | + let on_disk_reports = OnDiskReports::save_report( |
| 388 | + bcx.ws, |
| 389 | + update_message.clone(), |
| 390 | + per_package_future_incompat_reports, |
| 391 | + ); |
| 392 | + let report_id = on_disk_reports.last_id(); |
| 393 | + |
| 394 | + if bcx.build_config.future_incompat_report { |
| 395 | + let upstream_info = package_ids |
| 396 | + .iter() |
| 397 | + .map(|package_id| { |
| 398 | + let manifest = bcx.packages.get_one(*package_id).unwrap().manifest(); |
| 399 | + format!( |
| 400 | + " |
| 401 | + - {name} |
| 402 | + - Repository: {url} |
| 403 | + - Detailed warning command: `cargo report future-incompatibilities --id {id} --crate \"{name}\"", |
| 404 | + name = format!("{}:{}", package_id.name(), package_id.version()), |
| 405 | + url = manifest |
| 406 | + .metadata() |
| 407 | + .repository |
| 408 | + .as_deref() |
| 409 | + .unwrap_or("<not found>"), |
| 410 | + id = report_id, |
| 411 | + ) |
| 412 | + }) |
| 413 | + .collect::<Vec<_>>() |
| 414 | + .join("\n"); |
| 415 | + drop(bcx.config.shell().note(&format!( |
| 416 | + " |
| 417 | +To solve this problem, you can try the following approaches: |
| 418 | +
|
| 419 | +{update_message} |
| 420 | +- If the issue is not solved by updating the dependencies, a fix has to be |
| 421 | +implemented by those dependencies. You can help with that by notifying the |
| 422 | +maintainers of this problem (e.g. by creating a bug report) or by proposing a |
| 423 | +fix to the maintainers (e.g. by creating a pull request): |
| 424 | +{upstream_info} |
| 425 | +
|
| 426 | +- If waiting for an upstream fix is not an option, you can use the `[patch]` |
| 427 | +section in `Cargo.toml` to use your own version of the dependency. For more |
| 428 | +information, see: |
| 429 | +https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section |
| 430 | + ", |
| 431 | + upstream_info = upstream_info, |
| 432 | + update_message = update_message, |
| 433 | + ))); |
| 434 | + |
| 435 | + drop(bcx.config.shell().note(&format!( |
| 436 | + "this report can be shown with `cargo report \ |
| 437 | + future-incompatibilities -Z future-incompat-report --id {}`", |
| 438 | + report_id |
| 439 | + ))); |
| 440 | + } else if should_display_message { |
| 441 | + drop(bcx.config.shell().note(&format!( |
| 442 | + "to see what the problems were, use the option \ |
| 443 | + `--future-incompat-report`, or run `cargo report \ |
| 444 | + future-incompatibilities --id {}`", |
| 445 | + report_id |
| 446 | + ))); |
| 447 | + } |
| 448 | +} |
0 commit comments