From 170f4f57ccd893becbefad947e7862d0917c83c4 Mon Sep 17 00:00:00 2001 From: Bastian Köcher Date: Fri, 11 Aug 2017 17:59:32 +0200 Subject: Adds support for running rustfmt on generated bindings This patch enables bindgen to run rustfmt on generated bindings. Rustfmt is used from the global PATH. Two new command-line arguments are added: 1. --format-bindings: Enables running rustfmt 2. --format-configuration-file: The configuration file for rustfmt (not required). --- Cargo.lock | 10 ++++++++ Cargo.toml | 1 + src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ src/options.rs | 37 ++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f0f65df..8639b56e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,7 @@ dependencies = [ "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -339,6 +340,14 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "which" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -393,5 +402,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d238435618c0f298d2d75596c2d4fa7d4ea469c0c1c3ff824737ed50ad5ab61c" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 30c319f7..69ed15f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ syntex_syntax = "0.58" regex = "0.2" # This kinda sucks: https://github.com/rust-lang/cargo/issues/1982 clap = "2" +which = "1.0.2" [dependencies.aster] features = ["with-syntex"] diff --git a/src/lib.rs b/src/lib.rs index e72800c0..431fea0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ extern crate peeking_take_while; extern crate regex; #[macro_use] extern crate lazy_static; +extern crate which; #[cfg(feature = "logging")] #[macro_use] @@ -455,6 +456,16 @@ impl Builder { ); } + if !self.options.format_bindings { + output_vector.push("--format-bindings".into()); + } + + if let Some(path) = self.options.format_configuration_file.as_ref().and_then( + |f| f.to_str()) { + output_vector.push("--format-configuration-file".into()); + output_vector.push(path.into()); + } + output_vector } @@ -840,6 +851,19 @@ impl Builder { self } + /// Set whether rustfmt should format the generated bindings. + pub fn format_bindings(mut self, doit: bool) -> Self { + self.options.format_bindings = doit; + self + } + + /// Set the absolute path to the rustfmt configuration file, if None, the standard rustfmt + /// options are used. + pub fn format_configuration_file(mut self, path: Option) -> Self { + self.options.format_configuration_file = path; + self + } + /// Generate the Rust bindings using the options built up thus far. pub fn generate<'ctx>(mut self) -> Result, ()> { self.options.input_header = self.input_headers.pop(); @@ -1099,6 +1123,13 @@ pub struct BindgenOptions { /// Features to enable, derived from `rust_target` rust_features: RustFeatures, + + /// Whether rustfmt should format the generated bindings. + pub format_bindings: bool, + + /// The absolute path to the rustfmt configuration file, if None, the standard rustfmt + /// options are used. + pub format_configuration_file: Option, } /// TODO(emilio): This is sort of a lie (see the error message that results from @@ -1183,6 +1214,8 @@ impl Default for BindgenOptions { objc_extern_crate: false, enable_mangling: true, prepend_enum_name: true, + format_bindings: true, + format_configuration_file: None, } } } @@ -1334,14 +1367,19 @@ impl<'ctx> Bindings<'ctx> { /// Write these bindings as source text to a file. pub fn write_to_file>(&self, path: P) -> io::Result<()> { - let file = try!( - OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(path) - ); - self.write(Box::new(file)) + { + let file = try!( + OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path.as_ref()) + ); + self.write(Box::new(file))?; + } + + self.format_generated_file(path.as_ref()); + Ok(()) } /// Write these bindings as source text to the given `Write`able. @@ -1364,6 +1402,31 @@ impl<'ctx> Bindings<'ctx> { try!(eof(&mut ps.s)); ps.s.out.flush() } + + /// Checks if format_bindings is set and runs rustfmt on the file + fn format_generated_file(&self, file: &Path) { + if !self.context.options().format_bindings { + return; + } + + let rustfmt = if let Ok(rustfmt) = which::which("rustfmt") { + rustfmt + } else { + error!("Could not find rustfmt in the global path."); + return; + }; + + let mut cmd = Command::new(rustfmt); + + if let Some(path) = self.context.options().format_configuration_file.as_ref().and_then( + |f| f.to_str()) { + cmd.args(&["--config-path", path]); + } + + if let Err(e) = cmd.arg(file).status() { + error!("Error executing rustfmt (exit code: {:?}).", e); + } + } } /// Determines whether the given cursor is in any of the files matched by the diff --git a/src/options.rs b/src/options.rs index 28faea99..074807c8 100644 --- a/src/options.rs +++ b/src/options.rs @@ -4,6 +4,7 @@ use clap::{App, Arg}; use std::fs::File; use std::io::{self, Error, ErrorKind, Write, stderr}; use std::str::FromStr; +use std::path::PathBuf; /// Construct a new [`Builder`](./struct.Builder.html) from command line flags. pub fn builder_from_flags( @@ -231,7 +232,20 @@ where .help("Preprocess and dump the input header files to disk. \ Useful when debugging bindgen, using C-Reduce, or when \ filing issues. The resulting file will be named \ - something like `__bindgen.i` or `__bindgen.ii`.") + something like `__bindgen.i` or `__bindgen.ii`."), + Arg::with_name("format-bindings") + .long("format-bindings") + .help("Format the generated bindings with rustfmt. \ + Rustfmt needs to be in the global PATH."), + Arg::with_name("format-configuration-file") + .long("format-configuration-file") + .help("The absolute path to the rustfmt configuration file. \ + The configuration file will be used for formatting the bindings \ + (when enabled by --format-bindings).") + .value_name("path") + .takes_value(true) + .multiple(false) + .number_of_values(1), ]) // .args() .get_matches_from(args); @@ -458,6 +472,27 @@ where builder.dump_preprocessed_input()?; } + if matches.is_present("format-bindings") { + builder = builder.format_bindings(true); + } + + if let Some(path_str) = matches.value_of("format-configuration-file") { + let path = PathBuf::from(path_str); + + if !path.is_absolute() { + return Err(Error::new(ErrorKind::Other, + "--format-configuration--file needs to be an absolute path!")); + } + + if path.to_str().is_none() { + return Err( + Error::new(ErrorKind::Other, + "--format-configuration-file contains non-valid UTF8 characters.")); + } + + builder = builder.format_configuration_file(Some(path)); + } + let verbose = matches.is_present("verbose"); Ok((builder, output, verbose)) -- cgit v1.2.3