Allow controlling the Cargo profile used for macro expansion

If there's already a release build, it's better for cbindgen to reuse
the build artifacts from that to expand macros rather than starting
from scratch with a debug build. Controlled with

  --profile (debug|release)

as well as parse.expand.profile in cbindgen.toml, though hardcoding a
profile in a config file seems unlikely.
This commit is contained in:
Jordan Rose 2020-10-22 13:34:48 -07:00 committed by Emilio Cobos Álvarez
parent 398b28ca30
commit 0ba241498e
No known key found for this signature in database
GPG Key ID: E1152D0994E4BF8A
10 changed files with 264 additions and 3 deletions

83
Cargo.lock generated
View File

@ -44,6 +44,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
"serial_test",
"syn",
"tempfile",
"toml",
@ -70,6 +71,15 @@ dependencies = [
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "getrandom"
version = "0.1.15"
@ -121,12 +131,27 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.11"
@ -136,6 +161,30 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "parking_lot"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cloudabi",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.9"
@ -222,6 +271,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.116"
@ -253,6 +308,34 @@ dependencies = [
"serde",
]
[[package]]
name = "serial_test"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b15f74add9a9d4a3eb2bf739c9a427d266d3895b53d992c3a7c234fec2ff1f1"
dependencies = [
"lazy_static",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65f59259be9fc1bf677d06cc1456e97756004a1a5a577480f71430bd7c17ba33"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
[[package]]
name = "strsim"
version = "0.8.0"

View File

@ -31,6 +31,9 @@ version = "1.0.3"
default-features = false
features = ["clone-impls", "extra-traits", "full", "parsing", "printing"]
[dev-dependencies]
serial_test = "0.5.0"
[features]
default = ["clap"]

View File

@ -6,7 +6,7 @@ use std::path;
use crate::bindgen::bindings::Bindings;
use crate::bindgen::cargo::Cargo;
use crate::bindgen::config::{Braces, Config, Language, Style};
use crate::bindgen::config::{Braces, Config, Language, Profile, Style};
use crate::bindgen::error::Error;
use crate::bindgen::library::Library;
use crate::bindgen::parser::{self, Parse};
@ -225,6 +225,12 @@ impl Builder {
self
}
#[allow(unused)]
pub fn with_parse_expand_profile(mut self, profile: Profile) -> Builder {
self.config.parse.expand.profile = profile;
self
}
#[allow(unused)]
pub fn with_documentation(mut self, documentation: bool) -> Builder {
self.config.documentation = documentation;

View File

@ -11,6 +11,7 @@ use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
use crate::bindgen::cargo::cargo_toml;
use crate::bindgen::error::Error;
use crate::bindgen::ir::Cfg;
pub(crate) use cargo_expand::Profile;
/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
@ -233,6 +234,7 @@ impl Cargo {
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
profile: Profile,
) -> Result<String, cargo_expand::Error> {
cargo_expand::expand(
&self.manifest_path,
@ -242,6 +244,7 @@ impl Cargo {
expand_all_features,
expand_default_features,
expand_features,
profile,
)
}
}

View File

@ -24,6 +24,14 @@ pub enum Error {
Compile(String),
}
/// Which Cargo profile (group) to use when expanding macros.
pub enum Profile {
/// Do not pass `--release` when expanding macros
Debug,
/// Pass `--release` when expanding macros
Release,
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
@ -65,6 +73,7 @@ pub fn expand(
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);
@ -108,6 +117,12 @@ pub fn expand(
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 {

View File

@ -668,6 +668,27 @@ pub struct MacroExpansionConfig {
pub bitflags: bool,
}
/// Controls which Cargo profile is used for macro expansion.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Profile {
Debug,
Release,
}
impl FromStr for Profile {
type Err = String;
fn from_str(s: &str) -> Result<Profile, Self::Err> {
match s {
"debug" | "Debug" => Ok(Profile::Debug),
"release" | "Release" => Ok(Profile::Release),
_ => Err(format!("Unrecognized Profile: '{}'.", s)),
}
}
}
deserialize_enum_str!(Profile);
/// Settings to apply when running `rustc --pretty=expanded`
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
@ -683,6 +704,8 @@ pub struct ParseExpandConfig {
/// List of features to use when expanding. Combines with `default_features` like in
/// `Cargo.toml`.
pub features: Option<Vec<String>>,
/// Controls whether or not to pass `--release` when expanding.
pub profile: Profile,
}
impl Default for ParseExpandConfig {
@ -692,6 +715,7 @@ impl Default for ParseExpandConfig {
all_features: false,
default_features: true,
features: None,
profile: Profile::Debug,
}
}
}
@ -723,6 +747,7 @@ fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>(
all_features: true,
default_features: true,
features: None,
profile: Profile::Debug,
})
}

View File

@ -60,5 +60,6 @@ pub(crate) use self::cargo::*;
pub use self::bindings::Bindings;
pub use self::builder::Builder;
pub use self::config::Profile; // disambiguate with cargo::Profile
pub use self::config::*;
pub use self::error::Error;

View File

@ -9,8 +9,9 @@ use std::io::Read;
use std::path::{Path as FilePath, PathBuf as FilePathBuf};
use crate::bindgen::bitflags;
use crate::bindgen::cargo;
use crate::bindgen::cargo::{Cargo, PackageRef};
use crate::bindgen::config::{Config, ParseConfig};
use crate::bindgen::config::{Config, ParseConfig, Profile};
use crate::bindgen::error::Error;
use crate::bindgen::ir::{
AnnotationSet, Cfg, Constant, Documentation, Enum, Function, GenericParams, ItemMap,
@ -191,6 +192,10 @@ impl<'a> Parser<'a> {
self.config.parse.expand.all_features,
self.config.parse.expand.default_features,
&self.config.parse.expand.features,
match self.config.parse.expand.profile {
Profile::Debug => cargo::Profile::Debug,
Profile::Release => cargo::Profile::Release,
},
)
.map_err(|x| Error::CargoExpand(pkg.name.clone(), x))?;
let i = syn::parse_file(&s).map_err(|x| Error::ParseSyntaxError {

View File

@ -5,6 +5,7 @@
use std::env;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
extern crate clap;
#[macro_use]
@ -24,7 +25,7 @@ use clap::{App, Arg, ArgMatches};
mod bindgen;
mod logging;
use crate::bindgen::{Bindings, Builder, Cargo, Config, Error, Language, Style};
use crate::bindgen::{Bindings, Builder, Cargo, Config, Error, Language, Profile, Style};
fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
// We allow specifying a language to override the config default. This is
@ -61,6 +62,16 @@ fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
}
}
if let Some(profile) = matches.value_of("profile") {
config.parse.expand.profile = match Profile::from_str(profile) {
Ok(p) => p,
Err(e) => {
error!("{}", e);
return;
}
}
}
if matches.is_present("d") {
config.parse.parse_deps = true;
}
@ -226,6 +237,16 @@ fn main() {
)
.required(false),
)
.arg(
Arg::with_name("profile")
.long("profile")
.value_name("PROFILE")
.help(
"Specify the profile to use when expanding macros. \
Has no effect otherwise."
)
.possible_values(&["Debug", "debug", "Release", "release"]),
)
.arg(
Arg::with_name("quiet")
.short("q")

99
tests/profile.rs Normal file
View File

@ -0,0 +1,99 @@
use cbindgen::*;
use serial_test::serial;
use std::path::{Path, PathBuf};
use std::process::Command;
fn build_using_lib(config: fn(Builder) -> Builder) -> tempfile::TempDir {
let expand_dep_test_dir = {
let mut this_file = PathBuf::from(file!());
this_file.pop();
this_file.extend(&["rust", "expand_dep"]);
this_file
};
let tmp_dir = tempfile::Builder::new()
.prefix("cbindgen-test-output-")
.tempdir()
.expect("Creating tmp dir failed");
std::env::set_var("CARGO_EXPAND_TARGET_DIR", tmp_dir.path());
let builder = Builder::new()
.with_config(Config::from_file(expand_dep_test_dir.join("cbindgen.toml")).unwrap())
.with_crate(expand_dep_test_dir);
let builder = config(builder);
builder.generate().expect("build should succeed");
tmp_dir
}
fn build_using_bin(extra_args: &[&str]) -> tempfile::TempDir {
let expand_dep_test_dir = {
let mut this_file = PathBuf::from(file!());
this_file.pop();
this_file.extend(&["rust", "expand_dep"]);
this_file
};
let tmp_dir = tempfile::Builder::new()
.prefix("cbindgen-test-output-")
.tempdir()
.expect("Creating tmp dir failed");
let cbindgen_path = env!("CARGO_BIN_EXE_cbindgen");
Command::new(cbindgen_path)
.current_dir(expand_dep_test_dir)
.env("CARGO_EXPAND_TARGET_DIR", tmp_dir.path())
.args(extra_args)
.output()
.expect("build should succed");
tmp_dir
}
fn get_contents_of_dir(path: &Path) -> Vec<String> {
path.read_dir()
.unwrap()
.map(|f| f.unwrap().file_name().to_str().unwrap().to_string())
.filter(|name| !name.starts_with("."))
.collect()
}
#[test]
#[serial]
fn lib_default_uses_debug_build() {
let target_dir = build_using_lib(|b| b);
assert_eq!(get_contents_of_dir(target_dir.path()), &["debug"]);
}
#[test]
#[serial]
fn lib_explicit_debug_build() {
let target_dir = build_using_lib(|b| b.with_parse_expand_profile(Profile::Debug));
assert_eq!(get_contents_of_dir(target_dir.path()), &["debug"]);
}
#[test]
#[serial]
fn lib_explicit_release_build() {
let target_dir = build_using_lib(|b| b.with_parse_expand_profile(Profile::Release));
assert_eq!(get_contents_of_dir(target_dir.path()), &["release"]);
}
#[test]
fn bin_default_uses_debug_build() {
let target_dir = build_using_bin(&[]);
assert_eq!(get_contents_of_dir(target_dir.path()), &["debug"]);
}
#[test]
fn bin_explicit_debug_build() {
let target_dir = build_using_bin(&["--profile", "debug"]);
assert_eq!(get_contents_of_dir(target_dir.path()), &["debug"]);
}
#[test]
fn bin_explicit_release_build() {
let target_dir = build_using_bin(&["--profile", "release"]);
assert_eq!(get_contents_of_dir(target_dir.path()), &["release"]);
}