lib: rustfmt output to stdout

Simplify the rustfmt and write mechanism.  Use rustfmt generated string to allow
writing to stdout or to rustfmt a file.
This commit is contained in:
Manas Karekar 2017-09-26 07:45:51 -04:00 committed by Nick Fitzgerald
parent a08d8fd65f
commit d5d962332b
3 changed files with 84 additions and 46 deletions

View File

@ -1652,18 +1652,13 @@ impl Bindings {
/// Write these bindings as source text to a file. /// Write these bindings as source text to a file.
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
{ let file = OpenOptions::new()
let file = try!( .write(true)
OpenOptions::new() .truncate(true)
.write(true) .create(true)
.truncate(true) .open(path.as_ref())?;
.create(true) self.write(Box::new(file))?;
.open(path.as_ref()) Ok(())
);
self.write(Box::new(file))?;
}
self.rustfmt_generated_file(path.as_ref())
} }
/// Write these bindings as source text to the given `Write`able. /// Write these bindings as source text to the given `Write`able.
@ -1676,31 +1671,52 @@ impl Bindings {
writer.write(line.as_bytes())?; writer.write(line.as_bytes())?;
writer.write("\n".as_bytes())?; writer.write("\n".as_bytes())?;
} }
if !self.options.raw_lines.is_empty() { if !self.options.raw_lines.is_empty() {
writer.write("\n".as_bytes())?; writer.write("\n".as_bytes())?;
} }
writer.write(self.module.as_str().as_bytes())?; let bindings = self.module.as_str().to_string();
match self.rustfmt_generated_string(bindings) {
Ok(rustfmt_bindings) => {
writer.write(rustfmt_bindings.as_str().as_bytes())?;
},
Err(err) => eprintln!("{:?}", err),
}
Ok(()) Ok(())
} }
/// Checks if rustfmt_bindings is set and runs rustfmt on the file /// Checks if rustfmt_bindings is set and runs rustfmt on the string
fn rustfmt_generated_file(&self, file: &Path) -> io::Result<()> { fn rustfmt_generated_string(&self, source: String) -> io::Result<String> {
let _t = time::Timer::new("rustfmt_generated_file") let _t = time::Timer::new("rustfmt_generated_string")
.with_output(self.options.time_phases); .with_output(self.options.time_phases);
if !self.options.rustfmt_bindings { if !self.options.rustfmt_bindings {
return Ok(()); return Ok(source);
} }
let rustfmt = if let Ok(rustfmt) = which::which("rustfmt") { let rustfmt = if let Ok(rustfmt) = which::which("rustfmt") {
rustfmt rustfmt
} else { } else {
warn!("Not running rustfmt because it does not exist in PATH"); eprintln!("warning: could not find usable rustfmt to pretty print bindings");
return Ok(()); return Ok(source);
}; };
let mut cmd = Command::new(rustfmt); // Prefer using the `rustfmt-nightly` version of `rustmft`, if
// possible. It requires being run via `rustup run nightly ...`.
let mut cmd = if let Ok(rustup) = which::which("rustup") {
let mut cmd = Command::new(rustup);
cmd.args(&["run", "nightly", "rustfmt", "--"]);
cmd
} else {
Command::new(rustfmt)
};
cmd
.args(&["--write-mode=display"])
.stdin(Stdio::piped())
.stdout(Stdio::piped());
if let Some(path) = self.options if let Some(path) = self.options
.rustfmt_configuration_file .rustfmt_configuration_file
@ -1710,34 +1726,52 @@ impl Bindings {
cmd.args(&["--config-path", path]); cmd.args(&["--config-path", path]);
} }
if let Ok(output) = cmd.arg(file).output() { match cmd.spawn() {
if !output.status.success() { Ok(mut child) => {
let stderr = String::from_utf8_lossy(&output.stderr); let mut child_stdin = child.stdin.take().unwrap();
match output.status.code() { let mut child_stdout = child.stdout.take().unwrap();
Some(2) => Err(io::Error::new(
io::ErrorKind::Other, // Write to stdin in a new thread, so that we can read from stdout on this
format!("Rustfmt parsing errors:\n{}", stderr), // thread. This keeps the child from blocking on writing to its stdout which
)), // might block us from writing to its stdin.
Some(3) => { let stdin_handle = ::std::thread::spawn(move || {
warn!( let _ = child_stdin.write_all(source.as_bytes());
"Rustfmt could not format some lines:\n{}", source
stderr });
);
Ok(()) let mut output = vec![];
} io::copy(&mut child_stdout, &mut output)?;
_ => Err(io::Error::new(
io::ErrorKind::Other, let status = child.wait()?;
format!("Internal rustfmt error:\n{}", stderr), let source = stdin_handle.join()
)), .expect("The thread writing to rustfmt's stdin doesn't do \
anything that could panic");
match String::from_utf8(output) {
Ok(bindings) => {
match status.code() {
Some(0) => Ok(bindings),
Some(2) => Err(io::Error::new(
io::ErrorKind::Other,
"Rustfmt parsing errors.".to_string(),
)),
Some(3) => {
warn!("Rustfmt could not format some lines.");
Ok(bindings)
}
_ => Err(io::Error::new(
io::ErrorKind::Other,
"Internal rustfmt error".to_string(),
)),
}
},
_ => Ok(source)
} }
} else {
Ok(())
} }
} else { Err(e) => {
Err(io::Error::new( eprintln!("Error spawning rustfmt: {}", e);
io::ErrorKind::Other, Ok(source)
"Error executing rustfmt!", }
))
} }
} }
} }

View File

@ -256,6 +256,9 @@ fn create_bindgen_builder(header: &PathBuf) -> Result<Option<Builder>, Error> {
let prepend = [ let prepend = [
"bindgen", "bindgen",
// We format in `compare_generated_header` ourselves to have a little
// more control.
"--no-rustfmt-bindings",
"--with-derive-default", "--with-derive-default",
header_str, header_str,
"--raw-line", "--raw-line",