summaryrefslogtreecommitdiffstats
path: root/src/bindgen/cargo/cargo_expand.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/cargo/cargo_expand.rs')
-rw-r--r--src/bindgen/cargo/cargo_expand.rs145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/bindgen/cargo/cargo_expand.rs b/src/bindgen/cargo/cargo_expand.rs
new file mode 100644
index 0000000..565a0d1
--- /dev/null
+++ b/src/bindgen/cargo/cargo_expand.rs
@@ -0,0 +1,145 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::bindgen::config::Profile;
+use std::env;
+use std::error;
+use std::fmt;
+use std::io;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::str::{from_utf8, Utf8Error};
+
+extern crate tempfile;
+use self::tempfile::Builder;
+
+#[derive(Debug)]
+/// Possible errors that can occur during `rustc -Zunpretty=expanded`.
+pub enum Error {
+ /// Error during creation of temporary directory
+ Io(io::Error),
+ /// Output of `cargo metadata` was not valid utf8
+ Utf8(Utf8Error),
+ /// Error during execution of `cargo rustc -Zunpretty=expanded`
+ Compile(String),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::Io(err)
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(err: Utf8Error) -> Self {
+ Error::Utf8(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::Io(ref err) => err.fmt(f),
+ Error::Utf8(ref err) => err.fmt(f),
+ Error::Compile(ref err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Error::Io(ref err) => Some(err),
+ Error::Utf8(ref err) => Some(err),
+ Error::Compile(..) => None,
+ }
+ }
+}
+
+/// Use rustc to expand and pretty print the crate into a single file,
+/// removing any macros in the process.
+#[allow(clippy::too_many_arguments)]
+pub fn expand(
+ manifest_path: &Path,
+ crate_name: &str,
+ version: Option<&str>,
+ use_tempdir: bool,
+ expand_all_features: bool,
+ expand_default_features: bool,
+ expand_features: &Option<Vec<String>>,
+ profile: Profile,
+) -> Result<String, Error> {
+ let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
+ let mut cmd = Command::new(cargo);
+
+ let mut _temp_dir = None; // drop guard
+ if use_tempdir {
+ _temp_dir = Some(Builder::new().prefix("cbindgen-expand").tempdir()?);
+ cmd.env("CARGO_TARGET_DIR", _temp_dir.unwrap().path());
+ } else if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") {
+ cmd.env("CARGO_TARGET_DIR", path);
+ } else if let Ok(ref path) = env::var("OUT_DIR") {
+ // When cbindgen was started programatically from a build.rs file, Cargo is running and
+ // locking the default target directory. In this case we need to use another directory,
+ // else we would end up in a deadlock. If Cargo is running `OUT_DIR` will be set, so we
+ // can use a directory relative to that.
+ cmd.env("CARGO_TARGET_DIR", PathBuf::from(path).join("expanded"));
+ }
+
+ // Set this variable so that we don't call it recursively if we expand a crate that is using
+ // cbindgen
+ cmd.env("_CBINDGEN_IS_RUNNING", "1");
+
+ cmd.arg("rustc");
+ cmd.arg("--lib");
+ // When build with the release profile we can't choose the `check` profile.
+ if profile != Profile::Release {
+ cmd.arg("--profile=check");
+ }
+ cmd.arg("--manifest-path");
+ cmd.arg(manifest_path);
+ if let Some(features) = expand_features {
+ cmd.arg("--features");
+ let mut features_str = String::new();
+ for (index, feature) in features.iter().enumerate() {
+ if index != 0 {
+ features_str.push(' ');
+ }
+ features_str.push_str(feature);
+ }
+ cmd.arg(features_str);
+ }
+ if expand_all_features {
+ cmd.arg("--all-features");
+ }
+ if !expand_default_features {
+ cmd.arg("--no-default-features");
+ }
+ match profile {
+ Profile::Debug => {}
+ Profile::Release => {
+ cmd.arg("--release");
+ }
+ }
+ cmd.arg("-p");
+ let mut package = crate_name.to_owned();
+ if let Some(version) = version {
+ package.push(':');
+ package.push_str(version);
+ }
+ cmd.arg(&package);
+ cmd.arg("--verbose");
+ cmd.arg("--");
+ cmd.arg("-Zunpretty=expanded");
+ info!("Command: {:?}", cmd);
+ let output = cmd.output()?;
+
+ let src = from_utf8(&output.stdout)?.to_owned();
+ let error = from_utf8(&output.stderr)?.to_owned();
+
+ if src.is_empty() {
+ Err(Error::Compile(error))
+ } else {
+ Ok(src)
+ }
+}