mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
parent
847b710480
commit
72dd792106
@ -72,6 +72,10 @@ pub struct CompileCommand {
|
|||||||
#[clap(required_if_eq("input", "-"), value_parser = ValueParser::new(output_value_parser))]
|
#[clap(required_if_eq("input", "-"), value_parser = ValueParser::new(output_value_parser))]
|
||||||
pub output: Option<Output>,
|
pub output: Option<Output>,
|
||||||
|
|
||||||
|
/// Output a Makefile rule describing the current compilation
|
||||||
|
#[clap(long = "make-deps", value_name = "PATH")]
|
||||||
|
pub make_deps: Option<PathBuf>,
|
||||||
|
|
||||||
/// The format of the output file, inferred from the extension by default
|
/// The format of the output file, inferred from the extension by default
|
||||||
#[arg(long = "format", short = 'f')]
|
#[arg(long = "format", short = 'f')]
|
||||||
pub format: Option<OutputFormat>,
|
pub format: Option<OutputFormat>,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fs;
|
use std::fs::{self, File};
|
||||||
use std::io::Write;
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||||
@ -118,6 +118,8 @@ pub fn compile_once(
|
|||||||
print_diagnostics(world, &[], &warnings, command.common.diagnostic_format)
|
print_diagnostics(world, &[], &warnings, command.common.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, command)?;
|
||||||
|
|
||||||
if let Some(open) = command.open.take() {
|
if let Some(open) = command.open.take() {
|
||||||
if let Output::Path(file) = command.output() {
|
if let Output::Path(file) = command.output() {
|
||||||
open_file(open.as_deref(), &file)?;
|
open_file(open.as_deref(), &file)?;
|
||||||
@ -335,6 +337,84 @@ impl ExportCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes a Makefile rule describing the relationship between the output and
|
||||||
|
/// its dependencies to the path specified by the --make-deps argument, if it
|
||||||
|
/// was provided.
|
||||||
|
fn write_make_deps(world: &mut SystemWorld, command: &CompileCommand) -> StrResult<()> {
|
||||||
|
let Some(ref make_deps_path) = command.make_deps else { return Ok(()) };
|
||||||
|
let Output::Path(output_path) = command.output() else {
|
||||||
|
bail!("failed to create make dependencies file because output was stdout")
|
||||||
|
};
|
||||||
|
let Ok(output_path) = output_path.into_os_string().into_string() else {
|
||||||
|
bail!("failed to create make dependencies file because output path was not valid unicode")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Based on `munge` in libcpp/mkdeps.cc from the GCC source code. This isn't
|
||||||
|
// perfect as some special characters can't be escaped.
|
||||||
|
fn munge(s: &str) -> String {
|
||||||
|
let mut res = String::with_capacity(s.len());
|
||||||
|
let mut slashes = 0;
|
||||||
|
for c in s.chars() {
|
||||||
|
match c {
|
||||||
|
'\\' => slashes += 1,
|
||||||
|
'$' => {
|
||||||
|
res.push('$');
|
||||||
|
slashes = 0;
|
||||||
|
}
|
||||||
|
' ' | '\t' => {
|
||||||
|
// `munge`'s source contains a comment here that says: "A
|
||||||
|
// space or tab preceded by 2N+1 backslashes represents N
|
||||||
|
// backslashes followed by space..."
|
||||||
|
for _ in 0..slashes + 1 {
|
||||||
|
res.push('\\');
|
||||||
|
}
|
||||||
|
slashes = 0;
|
||||||
|
}
|
||||||
|
'#' => {
|
||||||
|
res.push('\\');
|
||||||
|
slashes = 0;
|
||||||
|
}
|
||||||
|
_ => slashes = 0,
|
||||||
|
};
|
||||||
|
res.push(c);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(
|
||||||
|
make_deps_path: &Path,
|
||||||
|
output_path: String,
|
||||||
|
root: PathBuf,
|
||||||
|
dependencies: impl Iterator<Item = PathBuf>,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut file = File::create(make_deps_path)?;
|
||||||
|
|
||||||
|
file.write_all(munge(&output_path).as_bytes())?;
|
||||||
|
file.write_all(b":")?;
|
||||||
|
for dependency in dependencies {
|
||||||
|
let Some(dependency) =
|
||||||
|
dependency.strip_prefix(&root).unwrap_or(&dependency).to_str()
|
||||||
|
else {
|
||||||
|
// Silently skip paths that aren't valid unicode so we still
|
||||||
|
// produce a rule that will work for the other paths that can be
|
||||||
|
// processed.
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
file.write_all(b" ")?;
|
||||||
|
file.write_all(munge(dependency).as_bytes())?;
|
||||||
|
}
|
||||||
|
file.write_all(b"\n")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
write(make_deps_path, output_path, world.root().to_owned(), world.dependencies())
|
||||||
|
.map_err(|err| {
|
||||||
|
eco_format!("failed to create make dependencies file due to IO error ({err})")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Opens the given file using:
|
/// Opens the given file using:
|
||||||
/// - The default file viewer if `open` is `None`.
|
/// - The default file viewer if `open` is `None`.
|
||||||
/// - The given viewer provided by `open` if it is `Some`.
|
/// - The given viewer provided by `open` if it is `Some`.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user