Skip to content

Commit 9ab5441

Browse files
Made it so the CmdRenderer writes directly to the child's stdin (#544)
1 parent be949ce commit 9ab5441

File tree

4 files changed

+40
-16
lines changed

4 files changed

+40
-16
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ open = "1.1"
3232
regex = "0.2.1"
3333
tempdir = "0.3.4"
3434
itertools = "0.7.4"
35-
tempfile = "2.2.0"
3635
shlex = "0.1.1"
3736
toml-query = "0.6"
3837

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ extern crate serde_derive;
111111
extern crate serde_json;
112112
extern crate shlex;
113113
extern crate tempdir;
114-
extern crate tempfile;
115114
extern crate toml;
116115
extern crate toml_query;
117116

src/renderer/mod.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ mod html_handlebars;
1717

1818
use std::io::Read;
1919
use std::path::PathBuf;
20-
use std::process::Command;
20+
use std::process::{Command, Stdio};
2121
use serde_json;
22-
use tempfile;
2322
use shlex::Shlex;
2423

2524
use errors::*;
@@ -153,21 +152,30 @@ impl Renderer for CmdRenderer {
153152
fn render(&self, ctx: &RenderContext) -> Result<()> {
154153
info!("Invoking the \"{}\" renderer", self.cmd);
155154

156-
// We need to write the RenderContext to a temporary file here instead
157-
// of passing it in via a pipe. This prevents a race condition where
158-
// some quickly executing command (e.g. `/bin/true`) may exit before we
159-
// finish writing the render context (closing the stdin pipe and
160-
// throwing a write error).
161-
let mut temp = tempfile::tempfile().chain_err(|| "Unable to create a temporary file")?;
162-
serde_json::to_writer(&mut temp, &ctx)
163-
.chain_err(|| "Unable to serialize the RenderContext")?;
164-
165-
let status = self.compose_command()?
166-
.stdin(temp)
155+
let mut child = self.compose_command()?
156+
.stdin(Stdio::piped())
157+
.stdout(Stdio::inherit())
158+
.stderr(Stdio::inherit())
167159
.current_dir(&ctx.destination)
168-
.status()
160+
.spawn()
169161
.chain_err(|| "Unable to start the renderer")?;
170162

163+
{
164+
let mut stdin = child.stdin.take().expect("Child has stdin");
165+
if let Err(e) = serde_json::to_writer(&mut stdin, &ctx) {
166+
// Looks like the backend hung up before we could finish
167+
// sending it the render context. Log the error and keep going
168+
warn!("Error writing the RenderContext to the backend, {}", e);
169+
}
170+
171+
// explicitly close the `stdin` file handle
172+
drop(stdin);
173+
}
174+
175+
let status = child
176+
.wait()
177+
.chain_err(|| "Error waiting for the backend to complete")?;
178+
171179
trace!("{} exited with output: {:?}", self.cmd, status);
172180

173181
if !status.success() {

tests/alternate_backends.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
extern crate mdbook;
44
extern crate tempdir;
55

6+
use std::fs::File;
67
use tempdir::TempDir;
78
use mdbook::config::Config;
89
use mdbook::MDBook;
10+
use mdbook::renderer::RenderContext;
911

1012
#[test]
1113
fn passing_alternate_backend() {
@@ -28,6 +30,22 @@ fn alternate_backend_with_arguments() {
2830
md.build().unwrap();
2931
}
3032

33+
#[test]
34+
fn backends_receive_render_context_via_stdin() {
35+
let temp = TempDir::new("output").unwrap();
36+
let out_file = temp.path().join("out.txt");
37+
let cmd = format!("tee {}", out_file.display());
38+
39+
let (md, _temp) = dummy_book_with_backend("cat-to-file", &cmd);
40+
41+
assert!(!out_file.exists());
42+
md.build().unwrap();
43+
assert!(out_file.exists());
44+
45+
let got = RenderContext::from_json(File::open(&out_file).unwrap());
46+
assert!(got.is_ok());
47+
}
48+
3149
fn dummy_book_with_backend(name: &str, command: &str) -> (MDBook, TempDir) {
3250
let temp = TempDir::new("mdbook").unwrap();
3351

0 commit comments

Comments
 (0)