tidy: Add a check to ensure Cargo.toml is in sync

This verifies that the crates listed in the `[dependencies]` section of
`Cargo.toml` are a subset of the crates listed in `lib.rs` for our in-tree
crates. This should help ensure that when we refactor crates over time we keep
these dependency lists in sync.
This commit is contained in:
Alex Crichton 2016-04-05 11:18:24 -07:00
parent 9dd3c54a2c
commit 7bfaeaaf9c
7 changed files with 115 additions and 8 deletions

View File

@ -242,13 +242,13 @@ cleantestlibs:
.PHONY: tidy
tidy: $(HBIN0_H_$(CFG_BUILD))/tidy$(X_$(CFG_BUILD))
$< $(S)src
$(TARGET_RPATH_VAR0_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $< $(S)src
$(HBIN0_H_$(CFG_BUILD))/tidy$(X_$(CFG_BUILD)): \
$(TSREQ0_T_$(CFG_BUILD)_H_$(CFG_BUILD)) \
$(TLIB0_T_$(CFG_BUILD)_H_$(CFG_BUILD))/stamp.std \
$(call rwildcard,$(S)src/tools/tidy/src,*.rs)
$(STAGE0_T_$(CFG_BUILD)_H_$(CFG_BUILD)) src/tools/tidy/src/main.rs \
$(STAGE0_T_$(CFG_BUILD)_H_$(CFG_BUILD)) $(S)src/tools/tidy/src/main.rs \
--out-dir $(@D) --crate-name tidy
######################################################################

View File

@ -323,8 +323,7 @@ impl<'a> Step<'a> {
}
Source::ToolLinkchecker { stage } |
Source::ToolTidy { stage } |
Source::ToolCargoTest { stage } => {
Source::ToolTidy { stage } => {
vec![self.libstd(self.compiler(stage))]
}
Source::ToolErrorIndex { stage } |

View File

@ -9,3 +9,7 @@
// except according to those terms.
// See comments in Cargo.toml for why this exists
#![feature(test)]
extern crate test;

View File

@ -0,0 +1,95 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Tidy check to ensure that `[dependencies]` and `extern crate` are in sync.
//!
//! This tidy check ensures that all crates listed in the `[dependencies]`
//! section of a `Cargo.toml` are present in the corresponding `lib.rs` as
//! `extern crate` declarations. This should help us keep the DAG correctly
//! structured through various refactorings to prune out unnecessary edges.
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
pub fn check(path: &Path, bad: &mut bool) {
for entry in t!(path.read_dir()).map(|e| t!(e)) {
// Look for `Cargo.toml` with a sibling `src/lib.rs` or `lib.rs`
if entry.file_name().to_str() == Some("Cargo.toml") {
if path.join("src/lib.rs").is_file() {
verify(&entry.path(), &path.join("src/lib.rs"), bad)
}
if path.join("lib.rs").is_file() {
verify(&entry.path(), &path.join("lib.rs"), bad)
}
} else if t!(entry.file_type()).is_dir() {
check(&entry.path(), bad);
}
}
}
// Verify that the dependencies in Cargo.toml at `tomlfile` are sync'd with the
// `extern crate` annotations in the lib.rs at `libfile`.
fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) {
let mut toml = String::new();
let mut librs = String::new();
t!(t!(File::open(tomlfile)).read_to_string(&mut toml));
t!(t!(File::open(libfile)).read_to_string(&mut librs));
if toml.contains("name = \"bootstrap\"") {
return
}
// "Poor man's TOML parser", just assume we use one syntax for now
//
// We just look for:
//
// [dependencies]
// name = ...
// name2 = ...
// name3 = ...
//
// If we encounter a line starting with `[` then we assume it's the end of
// the dependency section and bail out.
let deps = match toml.find("[dependencies]") {
Some(i) => &toml[i+1..],
None => return,
};
let mut lines = deps.lines().peekable();
while let Some(line) = lines.next() {
if line.starts_with("[") {
break
}
let mut parts = line.splitn(2, '=');
let krate = parts.next().unwrap().trim();
if parts.next().is_none() {
continue
}
// Don't worry about depending on core/std but not saying `extern crate
// core/std`, that's intentional.
if krate == "core" || krate == "std" {
continue
}
// This is intentional, this dependency just makes the crate available
// for others later on.
if krate == "alloc_jemalloc" && toml.contains("name = \"std\"") {
continue
}
if !librs.contains(&format!("extern crate {}", krate)) {
println!("{} doesn't have `extern crate {}`, but Cargo.toml \
depends on it", libfile.display(), krate);
*bad = true;
}
}
}

View File

@ -71,8 +71,7 @@ pub fn check(path: &Path, bad: &mut bool) {
});
let mut max = 0;
println!("* {} error codes", map.len());
for (code, entries) in map {
for (&code, entries) in map.iter() {
if code > max {
max = code;
}
@ -81,10 +80,14 @@ pub fn check(path: &Path, bad: &mut bool) {
}
println!("duplicate error code: {}", code);
for (file, line_num, line) in entries {
for &(ref file, line_num, ref line) in entries.iter() {
println!("{}:{}: {}", file.display(), line_num, line);
}
*bad = true;
}
println!("* highest error code: E{:04}", max);
if !*bad {
println!("* {} error codes", map.len());
println!("* highest error code: E{:04}", max);
}
}

View File

@ -100,6 +100,10 @@ pub fn check(path: &Path, bad: &mut bool) {
}
});
if *bad {
return
}
let mut lines = Vec::new();
for feature in features {
lines.push(format!("{:<32} {:<8} {:<12} {:<8}",

View File

@ -29,6 +29,7 @@ mod bins;
mod style;
mod errors;
mod features;
mod cargo;
fn main() {
let path = env::args_os().skip(1).next().expect("need an argument");
@ -38,6 +39,7 @@ fn main() {
bins::check(&path, &mut bad);
style::check(&path, &mut bad);
errors::check(&path, &mut bad);
cargo::check(&path, &mut bad);
features::check(&path, &mut bad);
if bad {