Made it so the CmdRenderer writes directly to the child's stdin (#544)
This commit is contained in:
parent
be949ceae8
commit
9ab54412ea
|
@ -32,7 +32,6 @@ open = "1.1"
|
||||||
regex = "0.2.1"
|
regex = "0.2.1"
|
||||||
tempdir = "0.3.4"
|
tempdir = "0.3.4"
|
||||||
itertools = "0.7.4"
|
itertools = "0.7.4"
|
||||||
tempfile = "2.2.0"
|
|
||||||
shlex = "0.1.1"
|
shlex = "0.1.1"
|
||||||
toml-query = "0.6"
|
toml-query = "0.6"
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,6 @@ extern crate serde_derive;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate shlex;
|
extern crate shlex;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
extern crate tempfile;
|
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
extern crate toml_query;
|
extern crate toml_query;
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,8 @@ mod html_handlebars;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::{Command, Stdio};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use tempfile;
|
|
||||||
use shlex::Shlex;
|
use shlex::Shlex;
|
||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
@ -153,21 +152,30 @@ impl Renderer for CmdRenderer {
|
||||||
fn render(&self, ctx: &RenderContext) -> Result<()> {
|
fn render(&self, ctx: &RenderContext) -> Result<()> {
|
||||||
info!("Invoking the \"{}\" renderer", self.cmd);
|
info!("Invoking the \"{}\" renderer", self.cmd);
|
||||||
|
|
||||||
// We need to write the RenderContext to a temporary file here instead
|
let mut child = self.compose_command()?
|
||||||
// of passing it in via a pipe. This prevents a race condition where
|
.stdin(Stdio::piped())
|
||||||
// some quickly executing command (e.g. `/bin/true`) may exit before we
|
.stdout(Stdio::inherit())
|
||||||
// finish writing the render context (closing the stdin pipe and
|
.stderr(Stdio::inherit())
|
||||||
// throwing a write error).
|
|
||||||
let mut temp = tempfile::tempfile().chain_err(|| "Unable to create a temporary file")?;
|
|
||||||
serde_json::to_writer(&mut temp, &ctx)
|
|
||||||
.chain_err(|| "Unable to serialize the RenderContext")?;
|
|
||||||
|
|
||||||
let status = self.compose_command()?
|
|
||||||
.stdin(temp)
|
|
||||||
.current_dir(&ctx.destination)
|
.current_dir(&ctx.destination)
|
||||||
.status()
|
.spawn()
|
||||||
.chain_err(|| "Unable to start the renderer")?;
|
.chain_err(|| "Unable to start the renderer")?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut stdin = child.stdin.take().expect("Child has stdin");
|
||||||
|
if let Err(e) = serde_json::to_writer(&mut stdin, &ctx) {
|
||||||
|
// Looks like the backend hung up before we could finish
|
||||||
|
// sending it the render context. Log the error and keep going
|
||||||
|
warn!("Error writing the RenderContext to the backend, {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitly close the `stdin` file handle
|
||||||
|
drop(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = child
|
||||||
|
.wait()
|
||||||
|
.chain_err(|| "Error waiting for the backend to complete")?;
|
||||||
|
|
||||||
trace!("{} exited with output: {:?}", self.cmd, status);
|
trace!("{} exited with output: {:?}", self.cmd, status);
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
extern crate mdbook;
|
extern crate mdbook;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
use mdbook::config::Config;
|
use mdbook::config::Config;
|
||||||
use mdbook::MDBook;
|
use mdbook::MDBook;
|
||||||
|
use mdbook::renderer::RenderContext;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn passing_alternate_backend() {
|
fn passing_alternate_backend() {
|
||||||
|
@ -28,6 +30,22 @@ fn alternate_backend_with_arguments() {
|
||||||
md.build().unwrap();
|
md.build().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn backends_receive_render_context_via_stdin() {
|
||||||
|
let temp = TempDir::new("output").unwrap();
|
||||||
|
let out_file = temp.path().join("out.txt");
|
||||||
|
let cmd = format!("tee {}", out_file.display());
|
||||||
|
|
||||||
|
let (md, _temp) = dummy_book_with_backend("cat-to-file", &cmd);
|
||||||
|
|
||||||
|
assert!(!out_file.exists());
|
||||||
|
md.build().unwrap();
|
||||||
|
assert!(out_file.exists());
|
||||||
|
|
||||||
|
let got = RenderContext::from_json(File::open(&out_file).unwrap());
|
||||||
|
assert!(got.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
fn dummy_book_with_backend(name: &str, command: &str) -> (MDBook, TempDir) {
|
fn dummy_book_with_backend(name: &str, command: &str) -> (MDBook, TempDir) {
|
||||||
let temp = TempDir::new("mdbook").unwrap();
|
let temp = TempDir::new("mdbook").unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue