@@ -89,15 +89,102 @@ pub fn fetch_grammars() -> Result<()> {
89 let mut grammars = get_grammar_configs()?;
89 let mut grammars = get_grammar_configs()?;
90 grammars.retain(|grammar| !matches!(grammar.source, GrammarSource::Local { .. }));
90 grammars.retain(|grammar| !matches!(grammar.source, GrammarSource::Local { .. }));
92+ println!("Fetching {} grammars", grammars.len());
92− run_parallel(grammars, fetch_grammar, "fetch")
93+ let results = run_parallel(grammars, fetch_grammar);
95+ let mut errors = Vec::new();
96+ let mut git_updated = Vec::new();
97+ let mut git_up_to_date = 0;
98+ let mut non_git = Vec::new();
100+ for res in results {
102+ Ok(FetchStatus::GitUpToDate) => git_up_to_date += 1,
103+ Ok(FetchStatus::GitUpdated {
106+ }) => git_updated.push((grammar_id, revision)),
107+ Ok(FetchStatus::NonGit { grammar_id }) => non_git.push(grammar_id),
108+ Err(e) => errors.push(e),
112+ non_git.sort_unstable();
113+ git_updated.sort_unstable_by(|a, b| a.0.cmp(&b.0));
115+ if git_up_to_date != 0 {
116+ println!("{} up to date git grammars", git_up_to_date);
119+ if !non_git.is_empty() {
120+ println!("{} non git grammars", non_git.len());
121+ println!("\t{:?}", non_git);
124+ if !git_updated.is_empty() {
125+ println!("{} updated grammars", git_updated.len());
126+ // We checked the vec is not empty, unwrapping will not panic
127+ let longest_id = git_updated.iter().map(|x| x.0.len()).max().unwrap();
128+ for (id, rev) in git_updated {
130+ "\t{id:width$} now on {rev}",
138+ if !errors.is_empty() {
139+ let len = errors.len();
140+ println!("{} grammars failed to fetch", len);
141+ for (i, error) in errors.into_iter().enumerate() {
142+ println!("\tFailure {}/{}: {}", i, len, error);
95pub fn build_grammars(target: Option<String>) -> Result<()> {
149pub fn build_grammars(target: Option<String>) -> Result<()> {
97− get_grammar_configs()?,
150+ let grammars = get_grammar_configs()?;
151+ println!("Building {} grammars", grammars.len());
152+ let results = run_parallel(grammars, move |grammar| {
98− move |grammar| build_grammar(grammar, target.as_deref()),
153+ build_grammar(grammar, target.as_deref())
156+ let mut errors = Vec::new();
157+ let mut already_built = 0;
158+ let mut built = Vec::new();
160+ for res in results {
162+ Ok(BuildStatus::AlreadyBuilt) => already_built += 1,
163+ Ok(BuildStatus::Built { grammar_id }) => built.push(grammar_id),
164+ Err(e) => errors.push(e),
168+ built.sort_unstable();
170+ if already_built != 0 {
171+ println!("{} grammars already built", already_built);
174+ if !built.is_empty() {
175+ println!("{} grammars built now", built.len());
176+ println!("\t{:?}", built);
179+ if !errors.is_empty() {
180+ let len = errors.len();
181+ println!("{} grammars failed to build", len);
182+ for (i, error) in errors.into_iter().enumerate() {
183+ println!("\tFailure {}/{}: {}", i, len, error);
103// Returns the set of grammar configurations the user requests.
190// Returns the set of grammar configurations the user requests.
@@ -126,9 +213,10 @@ fn get_grammar_configs() -> Result<Vec<GrammarConfiguration>> {
126 Ok(grammars)
213 Ok(grammars)
129−fn run_parallel<F>(grammars: Vec<GrammarConfiguration>, job: F, action: &'static str) -> Result<()>
216+fn run_parallel<F, Res>(grammars: Vec<GrammarConfiguration>, job: F) -> Vec<Result<Res>>
131− F: Fn(GrammarConfiguration) -> Result<()> + std::marker::Send + 'static + Clone,
218+ F: Fn(GrammarConfiguration) -> Result<Res> + Send + 'static + Clone,
219+ Res: Send + 'static,
133 let pool = threadpool::Builder::new().build();
221 let pool = threadpool::Builder::new().build();
134 let (tx, rx) = channel();
222 let (tx, rx) = channel();
@@ -146,14 +234,21 @@ where
147 drop(tx);
235 drop(tx);
149− // TODO: print all failures instead of the first one found.
150− rx.iter()
237+ rx.iter().collect()
151− .find(|result| result.is_err())
238+}
152− .map(|err| err.with_context(|| format!("Failed to {} some grammar(s)", action)))
239+
153− .unwrap_or(Ok(()))
240+enum FetchStatus {
156−fn fetch_grammar(grammar: GrammarConfiguration) -> Result<()> {
251+fn fetch_grammar(grammar: GrammarConfiguration) -> Result<FetchStatus> {
157 if let GrammarSource::Git {
252 if let GrammarSource::Git {
158 remote, revision, ..
253 remote, revision, ..
159 } = grammar.source
254 } = grammar.source
@@ -189,16 +284,18 @@ fn fetch_grammar(grammar: GrammarConfiguration) -> Result<()> {
190 git(&grammar_dir, ["checkout", &revision])?;
285 git(&grammar_dir, ["checkout", &revision])?;
192− println!(
287+ Ok(FetchStatus::GitUpdated {
193− "Grammar '{}' checked out at '{}'.",
194− grammar.grammar_id, revision
288+ grammar_id: grammar.grammar_id,
197− println!("Grammar '{}' is already up to date.", grammar.grammar_id);
292+ Ok(FetchStatus::GitUpToDate)
295+ Ok(FetchStatus::NonGit {
296+ grammar_id: grammar.grammar_id,
204// Sets the remote for a repository to the given URL, creating the remote if
301// Sets the remote for a repository to the given URL, creating the remote if
@@ -245,7 +342,12 @@ where
347+ Built { grammar_id: String },
248−fn build_grammar(grammar: GrammarConfiguration, target: Option<&str>) -> Result<()> {
350+fn build_grammar(grammar: GrammarConfiguration, target: Option<&str>) -> Result<BuildStatus> {
249 let grammar_dir = if let GrammarSource::Local { path } = &grammar.source {
351 let grammar_dir = if let GrammarSource::Local { path } = &grammar.source {
250 PathBuf::from(&path)
352 PathBuf::from(&path)
@@ -285,7 +387,7 @@ fn build_tree_sitter_library(
285 src_path: &Path,
387 src_path: &Path,
286 grammar: GrammarConfiguration,
388 grammar: GrammarConfiguration,
287 target: Option<&str>,
389 target: Option<&str>,
288−) -> Result<()> {
390+) -> Result<BuildStatus> {
289 let header_path = src_path;
391 let header_path = src_path;
290 let parser_path = src_path.join("parser.c");
392 let parser_path = src_path.join("parser.c");
291 let mut scanner_path = src_path.join("scanner.c");
393 let mut scanner_path = src_path.join("scanner.c");
@@ -308,12 +410,9 @@ fn build_tree_sitter_library(
308 .context("Failed to compare source and binary timestamps")?;
410 .context("Failed to compare source and binary timestamps")?;
310 if !recompile {
412 if !recompile {
311− println!("Grammar '{}' is already built.", grammar.grammar_id);
312− return Ok(());
413+ return Ok(BuildStatus::AlreadyBuilt);
315− println!("Building grammar '{}'", grammar.grammar_id);
317 let mut config = cc::Build::new();
416 let mut config = cc::Build::new();
319 .cpp(true)
418 .cpp(true)
@@ -381,7 +480,9 @@ fn build_tree_sitter_library(
384− Ok(())
483+ Ok(BuildStatus::Built {
484+ grammar_id: grammar.grammar_id,
387fn needs_recompile(
488fn needs_recompile(