Skip to content

Commit 5719870

Browse files
committed
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).
1 parent 8c71eed commit 5719870

File tree

4 files changed

+118
-9
lines changed

4 files changed

+118
-9
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ syntex_syntax = "0.58"
5151
regex = "0.2"
5252
# This kinda sucks: https://github.com/rust-lang/cargo/issues/1982
5353
clap = "2"
54+
which = "1.0.2"
5455

5556
[dependencies.aster]
5657
features = ["with-syntex"]

src/lib.rs

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern crate peeking_take_while;
2626
extern crate regex;
2727
#[macro_use]
2828
extern crate lazy_static;
29+
extern crate which;
2930

3031
#[cfg(feature = "logging")]
3132
#[macro_use]
@@ -455,6 +456,16 @@ impl Builder {
455456
);
456457
}
457458

459+
if !self.options.format_bindings {
460+
output_vector.push("--format-bindings".into());
461+
}
462+
463+
if let Some(path) = self.options.format_configuration_file.as_ref().and_then(
464+
|f| f.to_str()) {
465+
output_vector.push("--format-configuration-file".into());
466+
output_vector.push(path.into());
467+
}
468+
458469
output_vector
459470
}
460471

@@ -840,6 +851,19 @@ impl Builder {
840851
self
841852
}
842853

854+
/// Set whether rustfmt should format the generated bindings.
855+
pub fn format_bindings(mut self, doit: bool) -> Self {
856+
self.options.format_bindings = doit;
857+
self
858+
}
859+
860+
/// Set the absolute path to the rustfmt configuration file, if None, the standard rustfmt
861+
/// options are used.
862+
pub fn format_configuration_file(mut self, path: Option<PathBuf>) -> Self {
863+
self.options.format_configuration_file = path;
864+
self
865+
}
866+
843867
/// Generate the Rust bindings using the options built up thus far.
844868
pub fn generate<'ctx>(mut self) -> Result<Bindings<'ctx>, ()> {
845869
self.options.input_header = self.input_headers.pop();
@@ -1099,6 +1123,13 @@ pub struct BindgenOptions {
10991123

11001124
/// Features to enable, derived from `rust_target`
11011125
rust_features: RustFeatures,
1126+
1127+
/// Whether rustfmt should format the generated bindings.
1128+
pub format_bindings: bool,
1129+
1130+
/// The absolute path to the rustfmt configuration file, if None, the standard rustfmt
1131+
/// options are used.
1132+
pub format_configuration_file: Option<PathBuf>,
11021133
}
11031134

11041135
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1183,6 +1214,8 @@ impl Default for BindgenOptions {
11831214
objc_extern_crate: false,
11841215
enable_mangling: true,
11851216
prepend_enum_name: true,
1217+
format_bindings: true,
1218+
format_configuration_file: None,
11861219
}
11871220
}
11881221
}
@@ -1334,14 +1367,19 @@ impl<'ctx> Bindings<'ctx> {
13341367

13351368
/// Write these bindings as source text to a file.
13361369
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
1337-
let file = try!(
1338-
OpenOptions::new()
1339-
.write(true)
1340-
.truncate(true)
1341-
.create(true)
1342-
.open(path)
1343-
);
1344-
self.write(Box::new(file))
1370+
{
1371+
let file = try!(
1372+
OpenOptions::new()
1373+
.write(true)
1374+
.truncate(true)
1375+
.create(true)
1376+
.open(path.as_ref())
1377+
);
1378+
self.write(Box::new(file))?;
1379+
}
1380+
1381+
self.format_generated_file(path.as_ref());
1382+
Ok(())
13451383
}
13461384

13471385
/// Write these bindings as source text to the given `Write`able.
@@ -1364,6 +1402,31 @@ impl<'ctx> Bindings<'ctx> {
13641402
try!(eof(&mut ps.s));
13651403
ps.s.out.flush()
13661404
}
1405+
1406+
/// Checks if format_bindings is set and runs rustfmt on the file
1407+
fn format_generated_file(&self, file: &Path) {
1408+
if !self.context.options().format_bindings {
1409+
return;
1410+
}
1411+
1412+
let rustfmt = if let Ok(rustfmt) = which::which("rustfmt") {
1413+
rustfmt
1414+
} else {
1415+
error!("Could not find rustfmt in the global path.");
1416+
return;
1417+
};
1418+
1419+
let mut cmd = Command::new(rustfmt);
1420+
1421+
if let Some(path) = self.context.options().format_configuration_file.as_ref().and_then(
1422+
|f| f.to_str()) {
1423+
cmd.args(&["--config-path", path]);
1424+
}
1425+
1426+
if let Err(e) = cmd.arg(file).status() {
1427+
error!("Error executing rustfmt (exit code: {:?}).", e);
1428+
}
1429+
}
13671430
}
13681431

13691432
/// Determines whether the given cursor is in any of the files matched by the

src/options.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use clap::{App, Arg};
44
use std::fs::File;
55
use std::io::{self, Error, ErrorKind, Write, stderr};
66
use std::str::FromStr;
7+
use std::path::PathBuf;
78

89
/// Construct a new [`Builder`](./struct.Builder.html) from command line flags.
910
pub fn builder_from_flags<I>(
@@ -231,7 +232,20 @@ where
231232
.help("Preprocess and dump the input header files to disk. \
232233
Useful when debugging bindgen, using C-Reduce, or when \
233234
filing issues. The resulting file will be named \
234-
something like `__bindgen.i` or `__bindgen.ii`.")
235+
something like `__bindgen.i` or `__bindgen.ii`."),
236+
Arg::with_name("format-bindings")
237+
.long("format-bindings")
238+
.help("Format the generated bindings with rustfmt. \
239+
Rustfmt needs to be in the global PATH."),
240+
Arg::with_name("format-configuration-file")
241+
.long("format-configuration-file")
242+
.help("The absolute path to the rustfmt configuration file. \
243+
The configuration file will be used for formatting the bindings \
244+
(when enabled by --format-bindings).")
245+
.value_name("path")
246+
.takes_value(true)
247+
.multiple(false)
248+
.number_of_values(1),
235249
]) // .args()
236250
.get_matches_from(args);
237251

@@ -458,6 +472,27 @@ where
458472
builder.dump_preprocessed_input()?;
459473
}
460474

475+
if matches.is_present("format-bindings") {
476+
builder = builder.format_bindings(true);
477+
}
478+
479+
if let Some(path_str) = matches.value_of("format-configuration-file") {
480+
let path = PathBuf::from(path_str);
481+
482+
if !path.is_absolute() {
483+
return Err(Error::new(ErrorKind::Other,
484+
"--format-configuration--file needs to be an absolute path!"));
485+
}
486+
487+
if path.to_str().is_none() {
488+
return Err(
489+
Error::new(ErrorKind::Other,
490+
"--format-configuration-file contains non-valid UTF8 characters."));
491+
}
492+
493+
builder = builder.format_configuration_file(Some(path));
494+
}
495+
461496
let verbose = matches.is_present("verbose");
462497

463498
Ok((builder, output, verbose))

0 commit comments

Comments
 (0)