--make-deps fixes (#5873)

This commit is contained in:
Matthew Toohey 2025-02-18 13:04:40 -05:00 committed by GitHub
parent 74e4f78687
commit 3de3813ca0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -6,8 +6,9 @@ use std::path::{Path, PathBuf};
use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono::{DateTime, Datelike, Timelike, Utc};
use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::term; use codespan_reporting::term;
use ecow::{eco_format, EcoString}; use ecow::eco_format;
use parking_lot::RwLock; use parking_lot::RwLock;
use pathdiff::diff_paths;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use typst::diag::{ use typst::diag::{
bail, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned, bail, At, Severity, SourceDiagnostic, SourceResult, StrResult, Warned,
@ -188,7 +189,7 @@ pub fn compile_once(
match output { match output {
// Export the PDF / PNG. // Export the PDF / PNG.
Ok(()) => { Ok(outputs) => {
let duration = start.elapsed(); let duration = start.elapsed();
if config.watching { if config.watching {
@ -202,7 +203,7 @@ pub fn compile_once(
print_diagnostics(world, &[], &warnings, config.diagnostic_format) print_diagnostics(world, &[], &warnings, config.diagnostic_format)
.map_err(|err| eco_format!("failed to print diagnostics ({err})"))?; .map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
write_make_deps(world, config)?; write_make_deps(world, config, outputs)?;
open_output(config)?; open_output(config)?;
} }
@ -226,12 +227,15 @@ pub fn compile_once(
fn compile_and_export( fn compile_and_export(
world: &mut SystemWorld, world: &mut SystemWorld,
config: &mut CompileConfig, config: &mut CompileConfig,
) -> Warned<SourceResult<()>> { ) -> Warned<SourceResult<Vec<Output>>> {
match config.output_format { match config.output_format {
OutputFormat::Html => { OutputFormat::Html => {
let Warned { output, warnings } = typst::compile::<HtmlDocument>(world); let Warned { output, warnings } = typst::compile::<HtmlDocument>(world);
let result = output.and_then(|document| export_html(&document, config)); let result = output.and_then(|document| export_html(&document, config));
Warned { output: result, warnings } Warned {
output: result.map(|()| vec![config.output.clone()]),
warnings,
}
} }
_ => { _ => {
let Warned { output, warnings } = typst::compile::<PagedDocument>(world); let Warned { output, warnings } = typst::compile::<PagedDocument>(world);
@ -257,9 +261,14 @@ fn export_html(document: &HtmlDocument, config: &CompileConfig) -> SourceResult<
} }
/// Export to a paged target format. /// Export to a paged target format.
fn export_paged(document: &PagedDocument, config: &CompileConfig) -> SourceResult<()> { fn export_paged(
document: &PagedDocument,
config: &CompileConfig,
) -> SourceResult<Vec<Output>> {
match config.output_format { match config.output_format {
OutputFormat::Pdf => export_pdf(document, config), OutputFormat::Pdf => {
export_pdf(document, config).map(|()| vec![config.output.clone()])
}
OutputFormat::Png => { OutputFormat::Png => {
export_image(document, config, ImageExportFormat::Png).at(Span::detached()) export_image(document, config, ImageExportFormat::Png).at(Span::detached())
} }
@ -327,7 +336,7 @@ fn export_image(
document: &PagedDocument, document: &PagedDocument,
config: &CompileConfig, config: &CompileConfig,
fmt: ImageExportFormat, fmt: ImageExportFormat,
) -> StrResult<()> { ) -> StrResult<Vec<Output>> {
// Determine whether we have indexable templates in output // Determine whether we have indexable templates in output
let can_handle_multiple = match config.output { let can_handle_multiple = match config.output {
Output::Stdout => false, Output::Stdout => false,
@ -383,7 +392,7 @@ fn export_image(
&& config.export_cache.is_cached(*i, &page.frame) && config.export_cache.is_cached(*i, &page.frame)
&& path.exists() && path.exists()
{ {
return Ok(()); return Ok(Output::Path(path.to_path_buf()));
} }
Output::Path(path.to_owned()) Output::Path(path.to_owned())
@ -392,11 +401,9 @@ fn export_image(
}; };
export_image_page(config, page, &output, fmt)?; export_image_page(config, page, &output, fmt)?;
Ok(()) Ok(output)
}) })
.collect::<Result<Vec<()>, EcoString>>()?; .collect::<StrResult<Vec<Output>>>()
Ok(())
} }
mod output_template { mod output_template {
@ -501,14 +508,25 @@ impl ExportCache {
/// Writes a Makefile rule describing the relationship between the output and /// Writes a Makefile rule describing the relationship between the output and
/// its dependencies to the path specified by the --make-deps argument, if it /// its dependencies to the path specified by the --make-deps argument, if it
/// was provided. /// was provided.
fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult<()> { fn write_make_deps(
world: &mut SystemWorld,
config: &CompileConfig,
outputs: Vec<Output>,
) -> StrResult<()> {
let Some(ref make_deps_path) = config.make_deps else { return Ok(()) }; let Some(ref make_deps_path) = config.make_deps else { return Ok(()) };
let Output::Path(output_path) = &config.output else { let Ok(output_paths) = outputs
bail!("failed to create make dependencies file because output was stdout") .into_iter()
}; .filter_map(|o| match o {
let Some(output_path) = output_path.as_os_str().to_str() else { Output::Path(path) => Some(path.into_os_string().into_string()),
Output::Stdout => None,
})
.collect::<Result<Vec<_>, _>>()
else {
bail!("failed to create make dependencies file because output path was not valid unicode") bail!("failed to create make dependencies file because output path was not valid unicode")
}; };
if output_paths.is_empty() {
bail!("failed to create make dependencies file because output was stdout")
}
// Based on `munge` in libcpp/mkdeps.cc from the GCC source code. This isn't // Based on `munge` in libcpp/mkdeps.cc from the GCC source code. This isn't
// perfect as some special characters can't be escaped. // perfect as some special characters can't be escaped.
@ -522,6 +540,10 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
res.push('$'); res.push('$');
slashes = 0; slashes = 0;
} }
':' => {
res.push('\\');
slashes = 0;
}
' ' | '\t' => { ' ' | '\t' => {
// `munge`'s source contains a comment here that says: "A // `munge`'s source contains a comment here that says: "A
// space or tab preceded by 2N+1 backslashes represents N // space or tab preceded by 2N+1 backslashes represents N
@ -544,18 +566,29 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
fn write( fn write(
make_deps_path: &Path, make_deps_path: &Path,
output_path: &str, output_paths: Vec<String>,
root: PathBuf, root: PathBuf,
dependencies: impl Iterator<Item = PathBuf>, dependencies: impl Iterator<Item = PathBuf>,
) -> io::Result<()> { ) -> io::Result<()> {
let mut file = File::create(make_deps_path)?; let mut file = File::create(make_deps_path)?;
let current_dir = std::env::current_dir()?;
let relative_root = diff_paths(&root, &current_dir).unwrap_or(root.clone());
file.write_all(munge(output_path).as_bytes())?; for (i, output_path) in output_paths.into_iter().enumerate() {
if i != 0 {
file.write_all(b" ")?;
}
file.write_all(munge(&output_path).as_bytes())?;
}
file.write_all(b":")?; file.write_all(b":")?;
for dependency in dependencies { for dependency in dependencies {
let Some(dependency) = let relative_dependency = match dependency.strip_prefix(&root) {
dependency.strip_prefix(&root).unwrap_or(&dependency).to_str() Ok(root_relative_dependency) => {
else { relative_root.join(root_relative_dependency)
}
Err(_) => dependency,
};
let Some(relative_dependency) = relative_dependency.to_str() else {
// Silently skip paths that aren't valid unicode so we still // Silently skip paths that aren't valid unicode so we still
// produce a rule that will work for the other paths that can be // produce a rule that will work for the other paths that can be
// processed. // processed.
@ -563,14 +596,14 @@ fn write_make_deps(world: &mut SystemWorld, config: &CompileConfig) -> StrResult
}; };
file.write_all(b" ")?; file.write_all(b" ")?;
file.write_all(munge(dependency).as_bytes())?; file.write_all(munge(relative_dependency).as_bytes())?;
} }
file.write_all(b"\n")?; file.write_all(b"\n")?;
Ok(()) Ok(())
} }
write(make_deps_path, output_path, world.root().to_owned(), world.dependencies()) write(make_deps_path, output_paths, world.root().to_owned(), world.dependencies())
.map_err(|err| { .map_err(|err| {
eco_format!("failed to create make dependencies file due to IO error ({err})") eco_format!("failed to create make dependencies file due to IO error ({err})")
}) })