From 94ad4e1d38d4f3ac0f3e532b5cd07f0e15025c87 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 24 Jan 2018 00:38:41 +0100 Subject: [PATCH 1/9] Add theme tests --- src/librustdoc/lib.rs | 27 +++++ src/librustdoc/theme.rs | 258 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 src/librustdoc/theme.rs diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e39fe20310c..efdfcafb40a 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -91,6 +91,7 @@ pub mod plugins; pub mod visit_ast; pub mod visit_lib; pub mod test; +pub mod theme; use clean::AttributesExt; @@ -267,6 +268,11 @@ pub fn opts() -> Vec { "additional themes which will be added to the generated docs", "FILES") }), + unstable("theme-checker", |o| { + o.optmulti("", "theme-checker", + "check if given theme is valid", + "FILES") + }), ] } @@ -316,6 +322,27 @@ pub fn main_args(args: &[String]) -> isize { return 0; } + let to_check = matches.opt_strs("theme-checker"); + if !to_check.is_empty() { + let pathes = theme::load_css_pathes(include_bytes!("html/static/themes/main.css")); + let mut errors = 0; + + println!("rustdoc: [theme-checker] Starting tests!"); + for theme_file in to_check.iter() { + print!(" - Checking \"{}\"...", theme_file); + if !theme::test_theme_against(theme_file, &pathes) { + eprintln!(" FAILED"); + errors += 1; + } else { + println!(" OK"); + } + } + if errors != 0 { + return 1; + } + return 0; + } + if matches.free.is_empty() { print_error("missing file operand"); return 1; diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs new file mode 100644 index 00000000000..ff1adc3e4c4 --- /dev/null +++ b/src/librustdoc/theme.rs @@ -0,0 +1,258 @@ +// Copyright 2012-2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashSet; +use std::fs::File; +use std::hash::{Hash, Hasher}; +use std::io::Read; +use std::path::Path; + +macro_rules! try_false { + ($e:expr) => ({ + match $e { + Ok(c) => c, + Err(e) => { + eprintln!("rustdoc: got an error: {}", e); + return false; + } + } + }) +} + +#[derive(Debug, Clone, Eq)] +pub struct CssPath { + pub name: String, + pub children: HashSet, +} + +// This PartialEq implementation IS NOT COMMUTATIVE!!! +// +// The order is very important: the second object must have all first's rules. +// However, the first doesn't require to have all second's rules. +impl PartialEq for CssPath { + fn eq(&self, other: &CssPath) -> bool { + if self.name != other.name { + false + } else { + for child in &self.children { + if !other.children.iter().any(|c| child == c) { + return false; + } + } + true + } + } +} + +impl Hash for CssPath { + fn hash(&self, state: &mut H) { + self.name.hash(state); + for x in &self.children { + x.hash(state); + } + } +} + +impl CssPath { + fn new(name: String) -> CssPath { + CssPath { + name, + children: HashSet::new(), + } + } +} + +/// All variants contain the position they occur. +#[derive(Debug, Clone, Copy)] +enum Events { + StartLineComment(usize), + StartComment(usize), + EndComment(usize), + InBlock(usize), + OutBlock(usize), +} + +impl Events { + fn get_pos(&self) -> usize { + match *self { + Events::StartLineComment(p) | + Events::StartComment(p) | + Events::EndComment(p) | + Events::InBlock(p) | + Events::OutBlock(p) => p, + } + } + + fn is_comment(&self) -> bool { + match *self { + Events::StartLineComment(_) | + Events::StartComment(_) | + Events::EndComment(_) => true, + _ => false, + } + } +} + +fn previous_is_line_comment(events: &[Events]) -> bool { + if let Some(&Events::StartLineComment(_)) = events.last() { + true + } else { + false + } +} + +fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool { + if let Some(&Events::StartComment(_)) = events.last() { + return false; + } + pos + 1 < v.len() && v[pos + 1] == b'/' +} + +fn load_css_events(v: &[u8]) -> Vec { + let mut pos = 0; + let mut events = Vec::with_capacity(100); + + while pos < v.len() - 1 { + match v[pos] { + b'/' if pos + 1 < v.len() && v[pos + 1] == b'*' => { + events.push(Events::StartComment(pos)); + pos += 1; + } + b'/' if is_line_comment(pos, v, &events) => { + events.push(Events::StartLineComment(pos)); + pos += 1; + } + b'\n' if previous_is_line_comment(&events) => { + events.push(Events::EndComment(pos)); + } + b'*' if pos + 1 < v.len() && v[pos + 1] == b'/' => { + events.push(Events::EndComment(pos + 2)); + pos += 1; + } + b'{' if !previous_is_line_comment(&events) => { + if let Some(&Events::StartComment(_)) = events.last() { + pos += 1; + continue + } + events.push(Events::InBlock(pos + 1)); + } + b'}' if !previous_is_line_comment(&events) => { + if let Some(&Events::StartComment(_)) = events.last() { + pos += 1; + continue + } + events.push(Events::OutBlock(pos + 1)); + } + _ => {} + } + pos += 1; + } + events +} + +fn get_useful_next(events: &[Events], pos: &mut usize) -> Option { + while *pos < events.len() { + if !events[*pos].is_comment() { + return Some(events[*pos]); + } + *pos += 1; + } + None +} + +fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet { + let mut pathes = Vec::with_capacity(50); + + while *pos < events.len() { + if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) { + println!("00 => {:?}", events[*pos]); + *pos += 1; + break + } + println!("a => {:?}", events[*pos]); + if let Some(Events::InBlock(start_pos)) = get_useful_next(events, pos) { + println!("aa => {:?}", events[*pos]); + pathes.push(CssPath::new(::std::str::from_utf8(if *pos > 0 { + &v[events[*pos - 1].get_pos()..start_pos - 1] + } else { + &v[..start_pos] + }).unwrap_or("").trim().to_owned())); + *pos += 1; + } + println!("b => {:?}", events[*pos]); + while let Some(Events::InBlock(_)) = get_useful_next(events, pos) { + println!("bb => {:?}", events[*pos]); + if let Some(ref mut path) = pathes.last_mut() { + for entry in inner(v, events, pos).iter() { + path.children.insert(entry.clone()); + } + } + } + if *pos < events.len() { + println!("c => {:?}", events[*pos]); + } + if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) { + *pos += 1; + } + } + pathes.iter().cloned().collect() +} + +pub fn load_css_pathes(v: &[u8]) -> CssPath { + let events = load_css_events(v); + let mut pos = 0; + + println!("\n======> {:?}", events); + let mut parent = CssPath::new("parent".to_owned()); + parent.children = inner(v, &events, &mut pos); + parent +} + +pub fn test_theme_against>(f: &P, against: &CssPath) -> bool { + let mut file = try_false!(File::open(f)); + let mut data = Vec::with_capacity(1000); + + try_false!(file.read_to_end(&mut data)); + let pathes = load_css_pathes(&data); + println!("========= {:?}", pathes); + println!("========= {:?}", against); + pathes == *against +} + +#[test] +fn test_comments_in_rules() { + let text = r#" +rule a {} + +rule b, c +// a line comment +{} + +rule d +// another line comment +e {} + +rule f/* a multine + +comment*/{} + +rule g/* another multine + +comment*/h + +i {} + +rule j/*commeeeeent + +you like things like "{}" in there? :) +*/ +end {} +"#; +} \ No newline at end of file From b44b033bf1a8f74f190854048616ca90db21319e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Jan 2018 21:58:10 +0100 Subject: [PATCH 2/9] get differences --- src/librustdoc/lib.rs | 4 +++- src/librustdoc/theme.rs | 49 +++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index efdfcafb40a..613ac67f03b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -330,9 +330,11 @@ pub fn main_args(args: &[String]) -> isize { println!("rustdoc: [theme-checker] Starting tests!"); for theme_file in to_check.iter() { print!(" - Checking \"{}\"...", theme_file); - if !theme::test_theme_against(theme_file, &pathes) { + let differences = theme::test_theme_against(theme_file, &pathes); + if !differences.is_empty() { eprintln!(" FAILED"); errors += 1; + eprintln!("{}", differences.join("\n")); } else { println!(" OK"); } diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index ff1adc3e4c4..933749cfcf9 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -14,13 +14,13 @@ use std::hash::{Hash, Hasher}; use std::io::Read; use std::path::Path; -macro_rules! try_false { - ($e:expr) => ({ +macro_rules! try_something { + ($e:expr, $out:expr) => ({ match $e { Ok(c) => c, Err(e) => { eprintln!("rustdoc: got an error: {}", e); - return false; + return $out; } } }) @@ -215,18 +215,49 @@ pub fn load_css_pathes(v: &[u8]) -> CssPath { parent } -pub fn test_theme_against>(f: &P, against: &CssPath) -> bool { - let mut file = try_false!(File::open(f)); +fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec) { + if against.name != other.name { + return + } else { + for child in &against.children { + let mut found = false; + let mut found_working = false; + let mut tmp = Vec::new(); + + for other_child in &other.children { + if child.name == other_child.name { + if child != other_child { + get_differences(child, other_child, &mut tmp); + } else { + found_working = true; + } + found = true; + break + } + } + if found == false { + v.push(format!(" Missing \"{}\" rule", child.name)); + } else if found_working == false { + v.extend(tmp.iter().cloned()); + } + } + } +} + +pub fn test_theme_against>(f: &P, against: &CssPath) -> Vec { + let mut file = try_something!(File::open(f), Vec::new()); let mut data = Vec::with_capacity(1000); - try_false!(file.read_to_end(&mut data)); + try_something!(file.read_to_end(&mut data), Vec::new()); let pathes = load_css_pathes(&data); println!("========= {:?}", pathes); println!("========= {:?}", against); - pathes == *against + let mut ret = Vec::new(); + get_differences(against, &pathes, &mut ret); + ret } -#[test] +/*#[test] fn test_comments_in_rules() { let text = r#" rule a {} @@ -255,4 +286,4 @@ you like things like "{}" in there? :) */ end {} "#; -} \ No newline at end of file +}*/ From 649715d09b29c76f71eb5d213d34b4108b8dcf65 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Jan 2018 21:59:54 +0100 Subject: [PATCH 3/9] Fix missing rules for dark.css --- src/librustdoc/html/static/themes/dark.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index 907a6e4fcb4..b45c3bf8e5f 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -112,10 +112,13 @@ pre { } .content .highlighted a, .content .highlighted span { color: #eee !important; } .content .highlighted.trait { background-color: #013191; } +.content .highlighted.mod, +.content .highlighted.externcrate { background-color: #afc6e4; } .content .highlighted.mod { background-color: #803a1b; } .content .highlighted.externcrate { background-color: #396bac; } .content .highlighted.enum { background-color: #5b4e68; } .content .highlighted.struct { background-color: #194e9f; } +.content .highlighted.union { background-color: #b7bd49; } .content .highlighted.fn, .content .highlighted.method, .content .highlighted.tymethod { background-color: #4950ed; } From 583b29f85c3f478e633eba38c59505e1a31694e6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Jan 2018 23:23:09 +0100 Subject: [PATCH 4/9] Handle comments in css selector and add tests --- src/librustdoc/theme.rs | 103 +++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 933749cfcf9..b64d61184e0 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -167,37 +167,53 @@ fn get_useful_next(events: &[Events], pos: &mut usize) -> Option { None } +fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec { + let mut ret = Vec::with_capacity(3); + + ret.push(events[pos].get_pos() - 1); + if pos > 0 { + pos -= 1; + } + loop { + ret.push(events[pos].get_pos()); + if pos < 1 || !events[pos].is_comment() { + break + } + pos -= 1; + } + if events[pos].is_comment() { + ret.push(0); + } + ret.iter().rev().cloned().collect() +} + +fn build_rule(v: &[u8], positions: &[usize]) -> String { + positions.chunks(2) + .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("").to_owned()) + .collect::() + .trim() + .replace("\n", " ") +} + fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet { let mut pathes = Vec::with_capacity(50); while *pos < events.len() { if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) { - println!("00 => {:?}", events[*pos]); *pos += 1; break } - println!("a => {:?}", events[*pos]); - if let Some(Events::InBlock(start_pos)) = get_useful_next(events, pos) { - println!("aa => {:?}", events[*pos]); - pathes.push(CssPath::new(::std::str::from_utf8(if *pos > 0 { - &v[events[*pos - 1].get_pos()..start_pos - 1] - } else { - &v[..start_pos] - }).unwrap_or("").trim().to_owned())); + if let Some(Events::InBlock(_)) = get_useful_next(events, pos) { + pathes.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos)))); *pos += 1; } - println!("b => {:?}", events[*pos]); while let Some(Events::InBlock(_)) = get_useful_next(events, pos) { - println!("bb => {:?}", events[*pos]); if let Some(ref mut path) = pathes.last_mut() { for entry in inner(v, events, pos).iter() { path.children.insert(entry.clone()); } } } - if *pos < events.len() { - println!("c => {:?}", events[*pos]); - } if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) { *pos += 1; } @@ -209,13 +225,12 @@ pub fn load_css_pathes(v: &[u8]) -> CssPath { let events = load_css_events(v); let mut pos = 0; - println!("\n======> {:?}", events); let mut parent = CssPath::new("parent".to_owned()); parent.children = inner(v, &events, &mut pos); parent } -fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec) { +pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec) { if against.name != other.name { return } else { @@ -250,16 +265,18 @@ pub fn test_theme_against>(f: &P, against: &CssPath) -> Vec Date: Thu, 25 Jan 2018 23:31:48 +0100 Subject: [PATCH 5/9] Add test when trying to add new theme --- src/librustdoc/lib.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 613ac67f03b..1bdcebcee98 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -398,12 +398,23 @@ pub fn main_args(args: &[String]) -> isize { } let mut themes = Vec::new(); - for theme in matches.opt_strs("themes").iter().map(|s| PathBuf::from(&s)) { - if !theme.is_file() { - eprintln!("rustdoc: option --themes arguments must all be files"); - return 1; + if matches.opt_present("themes") { + let pathes = theme::load_css_pathes(include_bytes!("html/static/themes/main.css")); + + for (theme_file, theme_s) in matches.opt_strs("themes") + .iter() + .map(|s| (PathBuf::from(&s), s.to_owned())) { + if !theme_file.is_file() { + eprintln!("rustdoc: option --themes arguments must all be files"); + return 1; + } + if !theme::test_theme_against(&theme_file, &pathes).is_empty() { + eprintln!("rustdoc: invalid theme: \"{}\"", theme_s); + eprintln!(" Check what's wrong with the \"theme-checker\" option"); + return 1; + } + themes.push(theme_file); } - themes.push(theme); } let external_html = match ExternalHtml::load( From 63ee1cd846b92eb3a124ec345d4889bdb5bca8e3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Jan 2018 00:43:57 +0100 Subject: [PATCH 6/9] Improve output a bit in case of error --- src/librustdoc/lib.rs | 11 +++++++---- src/librustdoc/theme.rs | 21 +++++++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 1bdcebcee98..c08cff98892 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -330,11 +330,13 @@ pub fn main_args(args: &[String]) -> isize { println!("rustdoc: [theme-checker] Starting tests!"); for theme_file in to_check.iter() { print!(" - Checking \"{}\"...", theme_file); - let differences = theme::test_theme_against(theme_file, &pathes); - if !differences.is_empty() { + let (success, differences) = theme::test_theme_against(theme_file, &pathes); + if !differences.is_empty() || !success { eprintln!(" FAILED"); errors += 1; - eprintln!("{}", differences.join("\n")); + if !differences.is_empty() { + eprintln!("{}", differences.join("\n")); + } } else { println!(" OK"); } @@ -408,7 +410,8 @@ pub fn main_args(args: &[String]) -> isize { eprintln!("rustdoc: option --themes arguments must all be files"); return 1; } - if !theme::test_theme_against(&theme_file, &pathes).is_empty() { + let (success, ret) = theme::test_theme_against(&theme_file, &pathes); + if !success || !ret.is_empty() { eprintln!("rustdoc: invalid theme: \"{}\"", theme_s); eprintln!(" Check what's wrong with the \"theme-checker\" option"); return 1; diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index b64d61184e0..fe753878041 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -259,15 +259,15 @@ pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec) } } -pub fn test_theme_against>(f: &P, against: &CssPath) -> Vec { - let mut file = try_something!(File::open(f), Vec::new()); +pub fn test_theme_against>(f: &P, against: &CssPath) -> (bool, Vec) { + let mut file = try_something!(File::open(f), (false, Vec::new())); let mut data = Vec::with_capacity(1000); - try_something!(file.read_to_end(&mut data), Vec::new()); + try_something!(file.read_to_end(&mut data), (false, Vec::new())); let pathes = load_css_pathes(&data); let mut ret = Vec::new(); get_differences(against, &pathes, &mut ret); - ret + (true, ret) } #[cfg(test)] @@ -321,6 +321,19 @@ rule j end {} &load_css_pathes(text.as_bytes())).is_empty()); } + #[test] + fn test_text() { + let text = r#" +a +/* sdfs +*/ b +c // sdf +d {} +"#; + let pathes = load_css_pathes(text.as_bytes()); + assert!(pathes.children.get("a b c d").is_some()); + } + #[test] fn test_comparison() { let x = r#" From 51580d46f919c1f97d82aeca1ea1086c545c7484 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 26 Jan 2018 00:44:52 +0100 Subject: [PATCH 7/9] Add tests for themes --- src/bootstrap/builder.rs | 2 +- src/bootstrap/check.rs | 1 - src/bootstrap/test.rs | 42 ++++++++++++++++++ src/librustdoc/lib.rs | 8 ++-- src/librustdoc/theme.rs | 58 +++++++++++++++++-------- src/tools/rustdoc-themes/test-themes.py | 52 ++++++++++++++++++++++ 6 files changed, 138 insertions(+), 25 deletions(-) create mode 100644 src/tools/rustdoc-themes/test-themes.py diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index bf7b1015a49..6c68ee18506 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -258,7 +258,7 @@ impl<'a> Builder<'a> { test::HostCompiletest, test::Crate, test::CrateLibrustc, test::Rustdoc, test::Linkcheck, test::Cargotest, test::Cargo, test::Rls, test::Docs, test::ErrorIndex, test::Distcheck, test::Rustfmt, test::Miri, test::Clippy, - test::RustdocJS), + test::RustdocJS, test::RustdocTheme), Kind::Bench => describe!(test::Crate, test::CrateLibrustc), Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook, doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon, diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index e6871764b2c..ede403491d7 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -160,4 +160,3 @@ pub fn libtest_stamp(build: &Build, compiler: Compiler, target: Interned pub fn librustc_stamp(build: &Build, compiler: Compiler, target: Interned) -> PathBuf { build.cargo_out(compiler, Mode::Librustc, target).join(".librustc-check.stamp") } - diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index e4c1cdb79fd..1c6cd066ad9 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -424,6 +424,48 @@ fn path_for_cargo(builder: &Builder, compiler: Compiler) -> OsString { env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("") } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct RustdocTheme { + pub compiler: Compiler, + pub host: Interned, +} + +impl Step for RustdocTheme { + type Output = (); + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun) -> ShouldRun { + run.path("src/tools/rustdoc-themes") + } + + fn make_run(run: RunConfig) { + let compiler = run.builder.compiler(run.builder.top_stage, run.host); + + run.builder.ensure(RustdocTheme { + compiler: compiler, + host: run.builder.build.build, + }); + } + + fn run(self, builder: &Builder) { + let rustdoc = builder.rustdoc(self.compiler.host); + let mut cmd = Command::new(builder.config.python.clone().expect("python not defined")); + cmd.args(&["src/tools/rustdoc-themes/test-themes.py", rustdoc.to_str().unwrap()]); + cmd.env("RUSTC_STAGE", self.compiler.stage.to_string()) + .env("RUSTC_SYSROOT", builder.sysroot(self.compiler)) + .env("RUSTDOC_LIBDIR", builder.sysroot_libdir(self.compiler, self.compiler.host)) + .env("CFG_RELEASE_CHANNEL", &builder.build.config.channel) + .env("RUSTDOC_REAL", builder.rustdoc(self.host)) + .env("RUSTDOC_CRATE_VERSION", builder.build.rust_version()) + .env("RUSTC_BOOTSTRAP", "1"); + if let Some(linker) = builder.build.linker(self.host) { + cmd.env("RUSTC_TARGET_LINKER", linker); + } + builder.run(&mut cmd); + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RustdocJS { pub host: Interned, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c08cff98892..17cf2b7349b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -324,13 +324,13 @@ pub fn main_args(args: &[String]) -> isize { let to_check = matches.opt_strs("theme-checker"); if !to_check.is_empty() { - let pathes = theme::load_css_pathes(include_bytes!("html/static/themes/main.css")); + let paths = theme::load_css_paths(include_bytes!("html/static/themes/main.css")); let mut errors = 0; println!("rustdoc: [theme-checker] Starting tests!"); for theme_file in to_check.iter() { print!(" - Checking \"{}\"...", theme_file); - let (success, differences) = theme::test_theme_against(theme_file, &pathes); + let (success, differences) = theme::test_theme_against(theme_file, &paths); if !differences.is_empty() || !success { eprintln!(" FAILED"); errors += 1; @@ -401,7 +401,7 @@ pub fn main_args(args: &[String]) -> isize { let mut themes = Vec::new(); if matches.opt_present("themes") { - let pathes = theme::load_css_pathes(include_bytes!("html/static/themes/main.css")); + let paths = theme::load_css_paths(include_bytes!("html/static/themes/main.css")); for (theme_file, theme_s) in matches.opt_strs("themes") .iter() @@ -410,7 +410,7 @@ pub fn main_args(args: &[String]) -> isize { eprintln!("rustdoc: option --themes arguments must all be files"); return 1; } - let (success, ret) = theme::test_theme_against(&theme_file, &pathes); + let (success, ret) = theme::test_theme_against(&theme_file, &paths); if !success || !ret.is_empty() { eprintln!("rustdoc: invalid theme: \"{}\"", theme_s); eprintln!(" Check what's wrong with the \"theme-checker\" option"); diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index fe753878041..39c9a6e2aa4 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -170,18 +170,24 @@ fn get_useful_next(events: &[Events], pos: &mut usize) -> Option { fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec { let mut ret = Vec::with_capacity(3); - ret.push(events[pos].get_pos() - 1); + ret.push(events[pos].get_pos()); if pos > 0 { pos -= 1; } loop { - ret.push(events[pos].get_pos()); if pos < 1 || !events[pos].is_comment() { + let x = events[pos].get_pos(); + if *ret.last().unwrap() != x { + ret.push(x); + } else { + ret.push(0); + } break } + ret.push(events[pos].get_pos()); pos -= 1; } - if events[pos].is_comment() { + if ret.len() & 1 != 0 && events[pos].is_comment() { ret.push(0); } ret.iter().rev().cloned().collect() @@ -189,14 +195,22 @@ fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec { fn build_rule(v: &[u8], positions: &[usize]) -> String { positions.chunks(2) - .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("").to_owned()) + .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("")) .collect::() .trim() .replace("\n", " ") + .replace("/", "") + .replace("\t", " ") + .replace("{", "") + .replace("}", "") + .split(" ") + .filter(|s| s.len() > 0) + .collect::>() + .join(" ") } fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet { - let mut pathes = Vec::with_capacity(50); + let mut paths = Vec::with_capacity(50); while *pos < events.len() { if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) { @@ -204,11 +218,11 @@ fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet { break } if let Some(Events::InBlock(_)) = get_useful_next(events, pos) { - pathes.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos)))); + paths.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos)))); *pos += 1; } while let Some(Events::InBlock(_)) = get_useful_next(events, pos) { - if let Some(ref mut path) = pathes.last_mut() { + if let Some(ref mut path) = paths.last_mut() { for entry in inner(v, events, pos).iter() { path.children.insert(entry.clone()); } @@ -218,10 +232,10 @@ fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> HashSet { *pos += 1; } } - pathes.iter().cloned().collect() + paths.iter().cloned().collect() } -pub fn load_css_pathes(v: &[u8]) -> CssPath { +pub fn load_css_paths(v: &[u8]) -> CssPath { let events = load_css_events(v); let mut pos = 0; @@ -264,9 +278,9 @@ pub fn test_theme_against>(f: &P, against: &CssPath) -> (bool, Ve let mut data = Vec::with_capacity(1000); try_something!(file.read_to_end(&mut data), (false, Vec::new())); - let pathes = load_css_pathes(&data); + let paths = load_css_paths(&data); let mut ret = Vec::new(); - get_differences(against, &pathes, &mut ret); + get_differences(against, &paths, &mut ret); (true, ret) } @@ -317,8 +331,11 @@ rule gh i {} rule j end {} "#; - assert!(get_differences(&load_css_pathes(against.as_bytes()), - &load_css_pathes(text.as_bytes())).is_empty()); + let mut ret = Vec::new(); + get_differences(&load_css_paths(against.as_bytes()), + &load_css_paths(text.as_bytes()), + &mut ret); + assert!(ret.is_empty()); } #[test] @@ -330,8 +347,8 @@ a c // sdf d {} "#; - let pathes = load_css_pathes(text.as_bytes()); - assert!(pathes.children.get("a b c d").is_some()); + let paths = load_css_paths(text.as_bytes()); + assert!(paths.children.get(&CssPath::new("a b c d".to_owned())).is_some()); } #[test] @@ -350,10 +367,13 @@ a { } "#; - let against = load_css_pathes(y.as_bytes()); - let other = load_css_pathes(x.as_bytes()); + let against = load_css_paths(y.as_bytes()); + let other = load_css_paths(x.as_bytes()); - assert!(get_differences(&against, &other).is_empty()); - assert_eq!(get_differences(&other, &against), vec![" Missing \"c\" rule".to_owned()]) + let mut ret = Vec::new(); + get_differences(&against, &other, &mut ret); + assert!(ret.is_empty()); + get_differences(&other, &against, &mut ret); + assert_eq!(ret, vec![" Missing \"c\" rule".to_owned()]); } } diff --git a/src/tools/rustdoc-themes/test-themes.py b/src/tools/rustdoc-themes/test-themes.py new file mode 100644 index 00000000000..27756e3bef6 --- /dev/null +++ b/src/tools/rustdoc-themes/test-themes.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2018 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 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +from os import listdir +from os.path import isfile, join +import subprocess +import sys + +FILES_TO_IGNORE = ['main.css'] +THEME_DIR_PATH = "src/librustdoc/html/static/themes" + + +def print_err(msg): + sys.stderr.write('{}\n'.format(msg)) + + +def exec_command(command): + child = subprocess.Popen(command) + stdout, stderr = child.communicate() + return child.returncode + + +def main(argv): + if len(argv) < 1: + print_err("Needs rustdoc binary path") + return 1 + rustdoc_bin = argv[0] + themes = [join(THEME_DIR_PATH, f) for f in listdir(THEME_DIR_PATH) + if isfile(join(THEME_DIR_PATH, f)) and f not in FILES_TO_IGNORE] + if len(themes) < 1: + print_err('No theme found in "{}"...'.format(THEME_DIR_PATH)) + return 1 + args = [rustdoc_bin, '-Z', 'unstable-options', '--theme-checker'] + args.extend(themes) + return exec_command(args) + + +if __name__ != '__main__': + print_err("Needs to be run as main") + sys.exit(1) +else: + sys.exit(main(sys.argv[1:])) From b1b11d4589e8f7486bfc181286b954c498bba4c9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 27 Jan 2018 22:12:28 +0100 Subject: [PATCH 8/9] Pass themes folder as parameter --- src/bootstrap/test.rs | 4 +++- src/librustdoc/lib.rs | 10 +++++----- src/librustdoc/theme.rs | 2 +- src/tools/rustdoc-themes/test-themes.py | 10 +++++----- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 1c6cd066ad9..351d10df28d 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -451,7 +451,9 @@ impl Step for RustdocTheme { fn run(self, builder: &Builder) { let rustdoc = builder.rustdoc(self.compiler.host); let mut cmd = Command::new(builder.config.python.clone().expect("python not defined")); - cmd.args(&["src/tools/rustdoc-themes/test-themes.py", rustdoc.to_str().unwrap()]); + cmd.args(&[builder.src.join("src/tools/rustdoc-themes/test-themes.py").to_str().unwrap(), + rustdoc.to_str().unwrap(), + builder.src.join("src/librustdoc/html/static/themes").to_str().unwrap()]); cmd.env("RUSTC_STAGE", self.compiler.stage.to_string()) .env("RUSTC_SYSROOT", builder.sysroot(self.compiler)) .env("RUSTDOC_LIBDIR", builder.sysroot_libdir(self.compiler, self.compiler.host)) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 17cf2b7349b..a72026c7d6b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -332,10 +332,10 @@ pub fn main_args(args: &[String]) -> isize { print!(" - Checking \"{}\"...", theme_file); let (success, differences) = theme::test_theme_against(theme_file, &paths); if !differences.is_empty() || !success { - eprintln!(" FAILED"); + println!(" FAILED"); errors += 1; if !differences.is_empty() { - eprintln!("{}", differences.join("\n")); + println!("{}", differences.join("\n")); } } else { println!(" OK"); @@ -407,13 +407,13 @@ pub fn main_args(args: &[String]) -> isize { .iter() .map(|s| (PathBuf::from(&s), s.to_owned())) { if !theme_file.is_file() { - eprintln!("rustdoc: option --themes arguments must all be files"); + println!("rustdoc: option --themes arguments must all be files"); return 1; } let (success, ret) = theme::test_theme_against(&theme_file, &paths); if !success || !ret.is_empty() { - eprintln!("rustdoc: invalid theme: \"{}\"", theme_s); - eprintln!(" Check what's wrong with the \"theme-checker\" option"); + println!("rustdoc: invalid theme: \"{}\"", theme_s); + println!(" Check what's wrong with the \"theme-checker\" option"); return 1; } themes.push(theme_file); diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 39c9a6e2aa4..1e4f64f5c52 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -348,7 +348,7 @@ c // sdf d {} "#; let paths = load_css_paths(text.as_bytes()); - assert!(paths.children.get(&CssPath::new("a b c d".to_owned())).is_some()); + assert!(paths.children.contains(&CssPath::new("a b c d".to_owned()))); } #[test] diff --git a/src/tools/rustdoc-themes/test-themes.py b/src/tools/rustdoc-themes/test-themes.py index 27756e3bef6..31591277ce3 100644 --- a/src/tools/rustdoc-themes/test-themes.py +++ b/src/tools/rustdoc-themes/test-themes.py @@ -17,7 +17,6 @@ import subprocess import sys FILES_TO_IGNORE = ['main.css'] -THEME_DIR_PATH = "src/librustdoc/html/static/themes" def print_err(msg): @@ -31,14 +30,15 @@ def exec_command(command): def main(argv): - if len(argv) < 1: + if len(argv) < 2: print_err("Needs rustdoc binary path") return 1 rustdoc_bin = argv[0] - themes = [join(THEME_DIR_PATH, f) for f in listdir(THEME_DIR_PATH) - if isfile(join(THEME_DIR_PATH, f)) and f not in FILES_TO_IGNORE] + themes_folder = argv[1] + themes = [join(themes_folder, f) for f in listdir(themes_folder) + if isfile(join(themes_folder, f)) and f not in FILES_TO_IGNORE] if len(themes) < 1: - print_err('No theme found in "{}"...'.format(THEME_DIR_PATH)) + print_err('No theme found in "{}"...'.format(themes_folder)) return 1 args = [rustdoc_bin, '-Z', 'unstable-options', '--theme-checker'] args.extend(themes) From dec9fab768e43a5c75456bb61c21701502db6de6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 5 Feb 2018 23:43:53 +0100 Subject: [PATCH 9/9] Convert python script to rust --- src/Cargo.lock | 4 ++ src/Cargo.toml | 1 + src/bootstrap/test.rs | 19 ++++---- src/bootstrap/tool.rs | 1 + src/tools/rustdoc-themes/Cargo.toml | 8 ++++ src/tools/rustdoc-themes/main.rs | 59 +++++++++++++++++++++++++ src/tools/rustdoc-themes/test-themes.py | 52 ---------------------- 7 files changed, 81 insertions(+), 63 deletions(-) create mode 100644 src/tools/rustdoc-themes/Cargo.toml create mode 100644 src/tools/rustdoc-themes/main.rs delete mode 100644 src/tools/rustdoc-themes/test-themes.py diff --git a/src/Cargo.lock b/src/Cargo.lock index 52ed134c01e..afe7f841f25 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -2217,6 +2217,10 @@ dependencies = [ "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustdoc-themes" +version = "0.1.0" + [[package]] name = "rustdoc-tool" version = "0.0.0" diff --git a/src/Cargo.toml b/src/Cargo.toml index c22ba7a37c8..c03301852cd 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -22,6 +22,7 @@ members = [ "tools/rls", "tools/rustfmt", "tools/miri", + "tools/rustdoc-themes", # FIXME(https://github.com/rust-lang/cargo/issues/4089): move these to exclude "tools/rls/test_data/bin_lib", "tools/rls/test_data/borrow_error", diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 351d10df28d..eae8ec1311d 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -113,7 +113,7 @@ impl Step for Linkcheck { let _time = util::timeit(); try_run(build, builder.tool_cmd(Tool::Linkchecker) - .arg(build.out.join(host).join("doc"))); + .arg(build.out.join(host).join("doc"))); } fn should_run(run: ShouldRun) -> ShouldRun { @@ -427,7 +427,6 @@ fn path_for_cargo(builder: &Builder, compiler: Compiler) -> OsString { #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RustdocTheme { pub compiler: Compiler, - pub host: Interned, } impl Step for RustdocTheme { @@ -444,27 +443,25 @@ impl Step for RustdocTheme { run.builder.ensure(RustdocTheme { compiler: compiler, - host: run.builder.build.build, }); } fn run(self, builder: &Builder) { let rustdoc = builder.rustdoc(self.compiler.host); - let mut cmd = Command::new(builder.config.python.clone().expect("python not defined")); - cmd.args(&[builder.src.join("src/tools/rustdoc-themes/test-themes.py").to_str().unwrap(), - rustdoc.to_str().unwrap(), - builder.src.join("src/librustdoc/html/static/themes").to_str().unwrap()]); - cmd.env("RUSTC_STAGE", self.compiler.stage.to_string()) + let mut cmd = builder.tool_cmd(Tool::RustdocTheme); + cmd.arg(rustdoc.to_str().unwrap()) + .arg(builder.src.join("src/librustdoc/html/static/themes").to_str().unwrap()) + .env("RUSTC_STAGE", self.compiler.stage.to_string()) .env("RUSTC_SYSROOT", builder.sysroot(self.compiler)) .env("RUSTDOC_LIBDIR", builder.sysroot_libdir(self.compiler, self.compiler.host)) .env("CFG_RELEASE_CHANNEL", &builder.build.config.channel) - .env("RUSTDOC_REAL", builder.rustdoc(self.host)) + .env("RUSTDOC_REAL", builder.rustdoc(self.compiler.host)) .env("RUSTDOC_CRATE_VERSION", builder.build.rust_version()) .env("RUSTC_BOOTSTRAP", "1"); - if let Some(linker) = builder.build.linker(self.host) { + if let Some(linker) = builder.build.linker(self.compiler.host) { cmd.env("RUSTC_TARGET_LINKER", linker); } - builder.run(&mut cmd); + try_run(builder.build, &mut cmd); } } diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index ea055cb5d1b..9036eb044b5 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -260,6 +260,7 @@ tool!( BuildManifest, "src/tools/build-manifest", "build-manifest", Mode::Libstd; RemoteTestClient, "src/tools/remote-test-client", "remote-test-client", Mode::Libstd; RustInstaller, "src/tools/rust-installer", "fabricate", Mode::Libstd; + RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes", Mode::Libstd; ); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] diff --git a/src/tools/rustdoc-themes/Cargo.toml b/src/tools/rustdoc-themes/Cargo.toml new file mode 100644 index 00000000000..c0e2f527301 --- /dev/null +++ b/src/tools/rustdoc-themes/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rustdoc-themes" +version = "0.1.0" +authors = ["Guillaume Gomez "] + +[[bin]] +name = "rustdoc-themes" +path = "main.rs" diff --git a/src/tools/rustdoc-themes/main.rs b/src/tools/rustdoc-themes/main.rs new file mode 100644 index 00000000000..4028cb4e8b6 --- /dev/null +++ b/src/tools/rustdoc-themes/main.rs @@ -0,0 +1,59 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::env::args; +use std::fs::read_dir; +use std::path::Path; +use std::process::{Command, exit}; + +const FILES_TO_IGNORE: &[&str] = &["main.css"]; + +fn get_folders>(folder_path: P) -> Vec { + let mut ret = Vec::with_capacity(10); + + for entry in read_dir(folder_path.as_ref()).expect("read_dir failed") { + let entry = entry.expect("Couldn't unwrap entry"); + let path = entry.path(); + + if !path.is_file() { + continue + } + let filename = path.file_name().expect("file_name failed"); + if FILES_TO_IGNORE.iter().any(|x| x == &filename) { + continue + } + ret.push(format!("{}", path.display())); + } + ret +} + +fn main() { + let argv: Vec = args().collect(); + + if argv.len() < 3 { + eprintln!("Needs rustdoc binary path"); + exit(1); + } + let rustdoc_bin = &argv[1]; + let themes_folder = &argv[2]; + let themes = get_folders(&themes_folder); + if themes.is_empty() { + eprintln!("No theme found in \"{}\"...", themes_folder); + exit(1); + } + let status = Command::new(rustdoc_bin) + .args(&["-Z", "unstable-options", "--theme-checker"]) + .args(&themes) + .status() + .expect("failed to execute child"); + if !status.success() { + exit(1); + } +} diff --git a/src/tools/rustdoc-themes/test-themes.py b/src/tools/rustdoc-themes/test-themes.py deleted file mode 100644 index 31591277ce3..00000000000 --- a/src/tools/rustdoc-themes/test-themes.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright 2018 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 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -from os import listdir -from os.path import isfile, join -import subprocess -import sys - -FILES_TO_IGNORE = ['main.css'] - - -def print_err(msg): - sys.stderr.write('{}\n'.format(msg)) - - -def exec_command(command): - child = subprocess.Popen(command) - stdout, stderr = child.communicate() - return child.returncode - - -def main(argv): - if len(argv) < 2: - print_err("Needs rustdoc binary path") - return 1 - rustdoc_bin = argv[0] - themes_folder = argv[1] - themes = [join(themes_folder, f) for f in listdir(themes_folder) - if isfile(join(themes_folder, f)) and f not in FILES_TO_IGNORE] - if len(themes) < 1: - print_err('No theme found in "{}"...'.format(themes_folder)) - return 1 - args = [rustdoc_bin, '-Z', 'unstable-options', '--theme-checker'] - args.extend(themes) - return exec_command(args) - - -if __name__ != '__main__': - print_err("Needs to be run as main") - sys.exit(1) -else: - sys.exit(main(sys.argv[1:]))