Introduce struct Config in build script
Using a struct improves navigation of the build script, as we can rely on rust-analyzer to help us check how a feature flag or an environment variable is used, as opposed to grepping for multiple env::var calls or #[cfg] attributes. This commit also removes some obsolete blocks of code related to the now defunct ndk-old-gcc and fuzzing features.
This commit is contained in:
parent
84a80c1916
commit
0d25d74cd6
|
|
@ -9,6 +9,7 @@ description = "FFI bindings to BoringSSL"
|
||||||
repository = { workspace = true }
|
repository = { workspace = true }
|
||||||
documentation = "https://docs.rs/boring-sys"
|
documentation = "https://docs.rs/boring-sys"
|
||||||
links = "boringssl"
|
links = "boringssl"
|
||||||
|
build = "build/main.rs"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
categories = ["cryptography", "external-ffi-bindings"]
|
categories = ["cryptography", "external-ffi-bindings"]
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
@ -44,7 +45,7 @@ include = [
|
||||||
"/deps/boringssl-fips/**/CMakeLists.txt",
|
"/deps/boringssl-fips/**/CMakeLists.txt",
|
||||||
"/deps/boringssl-fips/**/sources.cmake",
|
"/deps/boringssl-fips/**/sources.cmake",
|
||||||
"/deps/boringssl-fips/LICENSE",
|
"/deps/boringssl-fips/LICENSE",
|
||||||
"/build.rs",
|
"/build/*",
|
||||||
"/src",
|
"/src",
|
||||||
"/patches",
|
"/patches",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
use std::env;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub(crate) struct Config {
|
||||||
|
// TODO(nox): Use manifest dir instead.
|
||||||
|
pub(crate) pwd: PathBuf,
|
||||||
|
pub(crate) manifest_dir: PathBuf,
|
||||||
|
pub(crate) out_dir: PathBuf,
|
||||||
|
pub(crate) host: String,
|
||||||
|
pub(crate) target: String,
|
||||||
|
pub(crate) target_arch: String,
|
||||||
|
pub(crate) target_env: String,
|
||||||
|
pub(crate) target_os: String,
|
||||||
|
pub(crate) features: Features,
|
||||||
|
pub(crate) env: Env,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Features {
|
||||||
|
pub(crate) no_patches: bool,
|
||||||
|
pub(crate) fips: bool,
|
||||||
|
pub(crate) fips_link_precompiled: bool,
|
||||||
|
pub(crate) pq_experimental: bool,
|
||||||
|
pub(crate) rpk: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Env {
|
||||||
|
pub(crate) path: Option<PathBuf>,
|
||||||
|
pub(crate) include_path: Option<PathBuf>,
|
||||||
|
pub(crate) source_path: Option<PathBuf>,
|
||||||
|
pub(crate) precompiled_bcm_o: Option<PathBuf>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) build_dir: Option<PathBuf>,
|
||||||
|
pub(crate) debug: Option<OsString>,
|
||||||
|
pub(crate) opt_level: Option<OsString>,
|
||||||
|
pub(crate) android_ndk_home: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub(crate) fn from_env() -> Self {
|
||||||
|
let pwd = env::current_dir().unwrap();
|
||||||
|
|
||||||
|
let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap().into();
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap().into();
|
||||||
|
let host = env::var("HOST").unwrap();
|
||||||
|
let target = env::var("TARGET").unwrap();
|
||||||
|
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||||
|
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
|
||||||
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
|
|
||||||
|
let features = Features::from_env();
|
||||||
|
let env = Env::from_env();
|
||||||
|
|
||||||
|
let config = Self {
|
||||||
|
pwd,
|
||||||
|
manifest_dir,
|
||||||
|
out_dir,
|
||||||
|
host,
|
||||||
|
target,
|
||||||
|
target_arch,
|
||||||
|
target_env,
|
||||||
|
target_os,
|
||||||
|
features,
|
||||||
|
env,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.check_feature_compatibility();
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_feature_compatibility(&self) {
|
||||||
|
if self.features.fips && self.features.rpk {
|
||||||
|
panic!("`fips` and `rpk` features are mutually exclusive");
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_precompiled_native_lib = self.env.path.is_some();
|
||||||
|
let is_external_native_lib_source =
|
||||||
|
!is_precompiled_native_lib && self.env.source_path.is_none();
|
||||||
|
|
||||||
|
if self.features.no_patches && is_external_native_lib_source {
|
||||||
|
panic!(
|
||||||
|
"`no-patches` feature is supposed to be used with `BORING_BSSL_PATH`\
|
||||||
|
or `BORING_BSSL_SOURCE_PATH` env variables"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let features_with_patches_enabled = self.features.rpk || self.features.pq_experimental;
|
||||||
|
let patches_required = features_with_patches_enabled && !self.features.no_patches;
|
||||||
|
let build_from_sources_required = self.features.fips_link_precompiled || patches_required;
|
||||||
|
|
||||||
|
if is_precompiled_native_lib && build_from_sources_required {
|
||||||
|
panic!("precompiled BoringSSL was provided, so FIPS configuration or optional patches can't be applied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Features {
|
||||||
|
fn from_env() -> Self {
|
||||||
|
let no_patches = env::var_os("CARGO_FEATURE_NO_PATCHES").is_some();
|
||||||
|
let fips = env::var_os("CARGO_FEATURE_FIPS").is_some();
|
||||||
|
let fips_link_precompiled = env::var_os("CARGO_FEATURE_FIPS_LINK_PRECOMPILED").is_some();
|
||||||
|
let pq_experimental = env::var_os("CARGO_FEATURE_PQ_EXPERIMENTAL").is_some();
|
||||||
|
let rpk = env::var_os("CARGO_FEATURE_RPK").is_some();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
no_patches,
|
||||||
|
fips,
|
||||||
|
fips_link_precompiled,
|
||||||
|
pq_experimental,
|
||||||
|
rpk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
fn from_env() -> Self {
|
||||||
|
Self {
|
||||||
|
path: var("BORING_BSSL_PATH").map(Into::into),
|
||||||
|
include_path: var("BORING_BSSL_INCLUDE_PATH").map(Into::into),
|
||||||
|
source_path: var("BORING_BSSL_SOURCE_PATH").map(Into::into),
|
||||||
|
precompiled_bcm_o: var("BORING_SSL_PRECOMPILED_BCM_O").map(Into::into),
|
||||||
|
build_dir: var("BORINGSSL_BUILD_DIR").map(Into::into),
|
||||||
|
debug: var("DEBUG"),
|
||||||
|
opt_level: var("OPT_LEVEL"),
|
||||||
|
android_ndk_home: var("ANDROID_NDK_HOME").map(Into::into),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var(name: &str) -> Option<OsString> {
|
||||||
|
println!("cargo:rerun-if-env-changed={name}");
|
||||||
|
|
||||||
|
env::var_os(name)
|
||||||
|
}
|
||||||
|
|
@ -1,36 +1,15 @@
|
||||||
use fslock::LockFile;
|
use fslock::LockFile;
|
||||||
use std::env;
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Output};
|
use std::process::{Command, Output};
|
||||||
use std::sync::Once;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
// NOTE: this build script is adopted from quiche (https://github.com/cloudflare/quiche)
|
use crate::config::Config;
|
||||||
|
|
||||||
// Additional parameters for Android build of BoringSSL.
|
mod config;
|
||||||
//
|
|
||||||
// Android NDK < 18 with GCC.
|
|
||||||
const CMAKE_PARAMS_ANDROID_NDK_OLD_GCC: &[(&str, &[(&str, &str)])] = &[
|
|
||||||
(
|
|
||||||
"aarch64",
|
|
||||||
&[("ANDROID_TOOLCHAIN_NAME", "aarch64-linux-android-4.9")],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"arm",
|
|
||||||
&[("ANDROID_TOOLCHAIN_NAME", "arm-linux-androideabi-4.9")],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"x86",
|
|
||||||
&[("ANDROID_TOOLCHAIN_NAME", "x86-linux-android-4.9")],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"x86_64",
|
|
||||||
&[("ANDROID_TOOLCHAIN_NAME", "x86_64-linux-android-4.9")],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Android NDK >= 19.
|
// Android NDK >= 19.
|
||||||
const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
|
const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
|
||||||
|
|
@ -40,15 +19,9 @@ const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
|
||||||
("x86_64", &[("ANDROID_ABI", "x86_64")]),
|
("x86_64", &[("ANDROID_ABI", "x86_64")]),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn cmake_params_android() -> &'static [(&'static str, &'static str)] {
|
fn cmake_params_android(config: &Config) -> &'static [(&'static str, &'static str)] {
|
||||||
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
for (android_arch, params) in CMAKE_PARAMS_ANDROID_NDK {
|
||||||
let cmake_params_android = if cfg!(feature = "ndk-old-gcc") {
|
if *android_arch == config.target_arch {
|
||||||
CMAKE_PARAMS_ANDROID_NDK_OLD_GCC
|
|
||||||
} else {
|
|
||||||
CMAKE_PARAMS_ANDROID_NDK
|
|
||||||
};
|
|
||||||
for (android_arch, params) in cmake_params_android {
|
|
||||||
if *android_arch == arch {
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,71 +68,69 @@ const CMAKE_PARAMS_APPLE: &[(&str, &[(&str, &str)])] = &[
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn cmake_params_apple() -> &'static [(&'static str, &'static str)] {
|
fn cmake_params_apple(config: &Config) -> &'static [(&'static str, &'static str)] {
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
for (next_target, params) in CMAKE_PARAMS_APPLE {
|
for (next_target, params) in CMAKE_PARAMS_APPLE {
|
||||||
if *next_target == target {
|
if *next_target == config.target {
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_apple_sdk_name() -> &'static str {
|
fn get_apple_sdk_name(config: &Config) -> &'static str {
|
||||||
for (name, value) in cmake_params_apple() {
|
for (name, value) in cmake_params_apple(config) {
|
||||||
if *name == "CMAKE_OSX_SYSROOT" {
|
if *name == "CMAKE_OSX_SYSROOT" {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
panic!("cannot find SDK for {} in CMAKE_PARAMS_APPLE", target);
|
panic!(
|
||||||
|
"cannot find SDK for {} in CMAKE_PARAMS_APPLE",
|
||||||
|
config.target
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an absolute path to the BoringSSL source.
|
/// Returns an absolute path to the BoringSSL source.
|
||||||
fn get_boringssl_source_path() -> String {
|
fn get_boringssl_source_path(config: &Config) -> &PathBuf {
|
||||||
#[cfg(feature = "fips")]
|
if let Some(src_path) = &config.env.source_path {
|
||||||
const SUBMODULE_DIR: &str = "boringssl-fips";
|
|
||||||
#[cfg(not(feature = "fips"))]
|
|
||||||
const SUBMODULE_DIR: &str = "boringssl";
|
|
||||||
|
|
||||||
static COPY_SOURCES: Once = Once::new();
|
|
||||||
|
|
||||||
if let Ok(src_path) = env::var("BORING_BSSL_SOURCE_PATH") {
|
|
||||||
return src_path;
|
return src_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
static SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||||
let src_path = Path::new(&out_dir).join(SUBMODULE_DIR);
|
|
||||||
|
|
||||||
COPY_SOURCES.call_once(|| {
|
SOURCE_PATH.get_or_init(|| {
|
||||||
let submodule_path = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let submodule_dir = if config.features.fips {
|
||||||
.join("deps")
|
"boringssl-fips"
|
||||||
.join(SUBMODULE_DIR);
|
} else {
|
||||||
|
"boringssl"
|
||||||
|
};
|
||||||
|
|
||||||
|
let src_path = config.out_dir.join(submodule_dir);
|
||||||
|
|
||||||
|
let submodule_path = config.manifest_dir.join("deps").join(submodule_dir);
|
||||||
|
|
||||||
if !submodule_path.join("CMakeLists.txt").exists() {
|
if !submodule_path.join("CMakeLists.txt").exists() {
|
||||||
println!("cargo:warning=fetching boringssl git submodule");
|
println!("cargo:warning=fetching boringssl git submodule");
|
||||||
|
|
||||||
run_command(Command::new("git").args([
|
run_command(
|
||||||
"submodule",
|
Command::new("git")
|
||||||
"update",
|
.args(["submodule", "update", "--init", "--recursive"])
|
||||||
"--init",
|
.arg(&submodule_path),
|
||||||
"--recursive",
|
)
|
||||||
&submodule_path.display().to_string(),
|
|
||||||
]))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = fs::remove_dir_all(&src_path);
|
let _ = fs::remove_dir_all(&src_path);
|
||||||
fs_extra::dir::copy(submodule_path, &out_dir, &Default::default()).unwrap();
|
fs_extra::dir::copy(submodule_path, &config.out_dir, &Default::default()).unwrap();
|
||||||
|
|
||||||
// NOTE: .git can be both file and dir, depening on whether it was copied from a submodule
|
// NOTE: .git can be both file and dir, depening on whether it was copied from a submodule
|
||||||
// or created by the patches code.
|
// or created by the patches code.
|
||||||
let src_git_path = src_path.join(".git");
|
let src_git_path = src_path.join(".git");
|
||||||
let _ = fs::remove_file(&src_git_path);
|
let _ = fs::remove_file(&src_git_path);
|
||||||
let _ = fs::remove_dir_all(&src_git_path);
|
let _ = fs::remove_dir_all(&src_git_path);
|
||||||
});
|
|
||||||
|
|
||||||
src_path.display().to_string()
|
src_path
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the platform-specific output path for lib.
|
/// Returns the platform-specific output path for lib.
|
||||||
|
|
@ -167,30 +138,38 @@ fn get_boringssl_source_path() -> String {
|
||||||
/// MSVC generator on Windows place static libs in a target sub-folder,
|
/// MSVC generator on Windows place static libs in a target sub-folder,
|
||||||
/// so adjust library location based on platform and build target.
|
/// so adjust library location based on platform and build target.
|
||||||
/// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
|
/// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
|
||||||
fn get_boringssl_platform_output_path() -> String {
|
fn get_boringssl_platform_output_path(config: &Config) -> String {
|
||||||
if cfg!(target_env = "msvc") {
|
if config.target_env == "msvc" {
|
||||||
// Code under this branch should match the logic in cmake-rs
|
// Code under this branch should match the logic in cmake-rs
|
||||||
let debug_env_var = env::var("DEBUG").expect("DEBUG variable not defined in env");
|
let debug_env_var = config
|
||||||
|
.env
|
||||||
|
.debug
|
||||||
|
.as_ref()
|
||||||
|
.expect("DEBUG variable not defined in env");
|
||||||
|
|
||||||
let deb_info = match &debug_env_var[..] {
|
let deb_info = match debug_env_var.to_str() {
|
||||||
"false" => false,
|
Some("false") => false,
|
||||||
"true" => true,
|
Some("true") => true,
|
||||||
unknown => panic!("Unknown DEBUG={} env var.", unknown),
|
_ => panic!("Unknown DEBUG={:?} env var.", debug_env_var),
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt_env_var = env::var("OPT_LEVEL").expect("OPT_LEVEL variable not defined in env");
|
let opt_env_var = config
|
||||||
|
.env
|
||||||
|
.opt_level
|
||||||
|
.as_ref()
|
||||||
|
.expect("OPT_LEVEL variable not defined in env");
|
||||||
|
|
||||||
let subdir = match &opt_env_var[..] {
|
let subdir = match opt_env_var.to_str() {
|
||||||
"0" => "Debug",
|
Some("0") => "Debug",
|
||||||
"1" | "2" | "3" => {
|
Some("1" | "2" | "3") => {
|
||||||
if deb_info {
|
if deb_info {
|
||||||
"RelWithDebInfo"
|
"RelWithDebInfo"
|
||||||
} else {
|
} else {
|
||||||
"Release"
|
"Release"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"s" | "z" => "MinSizeRel",
|
Some("s" | "z") => "MinSizeRel",
|
||||||
unknown => panic!("Unknown OPT_LEVEL={} env var.", unknown),
|
_ => panic!("Unknown OPT_LEVEL={:?} env var.", opt_env_var),
|
||||||
};
|
};
|
||||||
|
|
||||||
subdir.to_string()
|
subdir.to_string()
|
||||||
|
|
@ -202,26 +181,22 @@ fn get_boringssl_platform_output_path() -> String {
|
||||||
/// Returns a new cmake::Config for building BoringSSL.
|
/// Returns a new cmake::Config for building BoringSSL.
|
||||||
///
|
///
|
||||||
/// It will add platform-specific parameters if needed.
|
/// It will add platform-specific parameters if needed.
|
||||||
fn get_boringssl_cmake_config() -> cmake::Config {
|
fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
|
||||||
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
let src_path = get_boringssl_source_path(config);
|
||||||
let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
let mut boringssl_cmake = cmake::Config::new(src_path);
|
||||||
let host = env::var("HOST").unwrap();
|
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
let pwd = std::env::current_dir().unwrap();
|
|
||||||
let src_path = get_boringssl_source_path();
|
|
||||||
|
|
||||||
let mut boringssl_cmake = cmake::Config::new(&src_path);
|
if config.host != config.target {
|
||||||
if host != target {
|
|
||||||
// Add platform-specific parameters for cross-compilation.
|
// Add platform-specific parameters for cross-compilation.
|
||||||
match os.as_ref() {
|
match &*config.target_os {
|
||||||
"android" => {
|
"android" => {
|
||||||
// We need ANDROID_NDK_HOME to be set properly.
|
// We need ANDROID_NDK_HOME to be set properly.
|
||||||
println!("cargo:rerun-if-env-changed=ANDROID_NDK_HOME");
|
let android_ndk_home = config
|
||||||
let android_ndk_home = env::var("ANDROID_NDK_HOME")
|
.env
|
||||||
|
.android_ndk_home
|
||||||
|
.as_ref()
|
||||||
.expect("Please set ANDROID_NDK_HOME for Android build");
|
.expect("Please set ANDROID_NDK_HOME for Android build");
|
||||||
let android_ndk_home = std::path::Path::new(&android_ndk_home);
|
for (name, value) in cmake_params_android(config) {
|
||||||
for (name, value) in cmake_params_android() {
|
eprintln!("android arch={} add {}={}", config.target_arch, name, value);
|
||||||
eprintln!("android arch={} add {}={}", arch, name, value);
|
|
||||||
boringssl_cmake.define(name, value);
|
boringssl_cmake.define(name, value);
|
||||||
}
|
}
|
||||||
let toolchain_file = android_ndk_home.join("build/cmake/android.toolchain.cmake");
|
let toolchain_file = android_ndk_home.join("build/cmake/android.toolchain.cmake");
|
||||||
|
|
@ -235,15 +210,15 @@ fn get_boringssl_cmake_config() -> cmake::Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
"macos" => {
|
"macos" => {
|
||||||
for (name, value) in cmake_params_apple() {
|
for (name, value) in cmake_params_apple(config) {
|
||||||
eprintln!("macos arch={} add {}={}", arch, name, value);
|
eprintln!("macos arch={} add {}={}", config.target_arch, name, value);
|
||||||
boringssl_cmake.define(name, value);
|
boringssl_cmake.define(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"ios" => {
|
"ios" => {
|
||||||
for (name, value) in cmake_params_apple() {
|
for (name, value) in cmake_params_apple(config) {
|
||||||
eprintln!("ios arch={} add {}={}", arch, name, value);
|
eprintln!("ios arch={} add {}={}", config.target_arch, name, value);
|
||||||
boringssl_cmake.define(name, value);
|
boringssl_cmake.define(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +226,7 @@ fn get_boringssl_cmake_config() -> cmake::Config {
|
||||||
let bitcode_cflag = "-fembed-bitcode";
|
let bitcode_cflag = "-fembed-bitcode";
|
||||||
|
|
||||||
// Hack for Xcode 10.1.
|
// Hack for Xcode 10.1.
|
||||||
let target_cflag = if arch == "x86_64" {
|
let target_cflag = if config.target_arch == "x86_64" {
|
||||||
"-target x86_64-apple-ios-simulator"
|
"-target x86_64-apple-ios-simulator"
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
|
|
@ -263,18 +238,20 @@ fn get_boringssl_cmake_config() -> cmake::Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
"windows" => {
|
"windows" => {
|
||||||
if host.contains("windows") {
|
if config.host.contains("windows") {
|
||||||
// BoringSSL's CMakeLists.txt isn't set up for cross-compiling using Visual Studio.
|
// BoringSSL's CMakeLists.txt isn't set up for cross-compiling using Visual Studio.
|
||||||
// Disable assembly support so that it at least builds.
|
// Disable assembly support so that it at least builds.
|
||||||
boringssl_cmake.define("OPENSSL_NO_ASM", "YES");
|
boringssl_cmake.define("OPENSSL_NO_ASM", "YES");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"linux" => match arch.as_str() {
|
"linux" => match &*config.target_arch {
|
||||||
"x86" => {
|
"x86" => {
|
||||||
boringssl_cmake.define(
|
boringssl_cmake.define(
|
||||||
"CMAKE_TOOLCHAIN_FILE",
|
"CMAKE_TOOLCHAIN_FILE",
|
||||||
pwd.join(&src_path)
|
config
|
||||||
|
.pwd
|
||||||
|
.join(src_path)
|
||||||
.join("src/util/32-bit-toolchain.cmake")
|
.join("src/util/32-bit-toolchain.cmake")
|
||||||
.as_os_str(),
|
.as_os_str(),
|
||||||
);
|
);
|
||||||
|
|
@ -282,19 +259,19 @@ fn get_boringssl_cmake_config() -> cmake::Config {
|
||||||
"aarch64" => {
|
"aarch64" => {
|
||||||
boringssl_cmake.define(
|
boringssl_cmake.define(
|
||||||
"CMAKE_TOOLCHAIN_FILE",
|
"CMAKE_TOOLCHAIN_FILE",
|
||||||
pwd.join("cmake/aarch64-linux.cmake").as_os_str(),
|
config.pwd.join("cmake/aarch64-linux.cmake").as_os_str(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"arm" => {
|
"arm" => {
|
||||||
boringssl_cmake.define(
|
boringssl_cmake.define(
|
||||||
"CMAKE_TOOLCHAIN_FILE",
|
"CMAKE_TOOLCHAIN_FILE",
|
||||||
pwd.join("cmake/armv7-linux.cmake").as_os_str(),
|
config.pwd.join("cmake/armv7-linux.cmake").as_os_str(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"warning: no toolchain file configured by boring-sys for {}",
|
"warning: no toolchain file configured by boring-sys for {}",
|
||||||
target
|
config.target
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -381,18 +358,16 @@ fn pick_best_android_ndk_toolchain(toolchains_dir: &Path) -> std::io::Result<OsS
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_extra_clang_args_for_bindgen() -> Vec<String> {
|
fn get_extra_clang_args_for_bindgen(config: &Config) -> Vec<String> {
|
||||||
let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
||||||
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
// Add platform-specific parameters.
|
// Add platform-specific parameters.
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match os.as_ref() {
|
match &*config.target_os {
|
||||||
"ios" | "macos" => {
|
"ios" | "macos" => {
|
||||||
// When cross-compiling for Apple targets, tell bindgen to use SDK sysroot,
|
// When cross-compiling for Apple targets, tell bindgen to use SDK sysroot,
|
||||||
// and *don't* use system headers of the host macOS.
|
// and *don't* use system headers of the host macOS.
|
||||||
let sdk = get_apple_sdk_name();
|
let sdk = get_apple_sdk_name(config);
|
||||||
let output = std::process::Command::new("xcrun")
|
let output = std::process::Command::new("xcrun")
|
||||||
.args(["--show-sdk-path", "--sdk", sdk])
|
.args(["--show-sdk-path", "--sdk", sdk])
|
||||||
.output()
|
.output()
|
||||||
|
|
@ -414,10 +389,14 @@ fn get_extra_clang_args_for_bindgen() -> Vec<String> {
|
||||||
params.push(sysroot);
|
params.push(sysroot);
|
||||||
}
|
}
|
||||||
"android" => {
|
"android" => {
|
||||||
let android_ndk_home = env::var("ANDROID_NDK_HOME")
|
let mut android_sysroot = config
|
||||||
|
.env
|
||||||
|
.android_ndk_home
|
||||||
|
.clone()
|
||||||
.expect("Please set ANDROID_NDK_HOME for Android build");
|
.expect("Please set ANDROID_NDK_HOME for Android build");
|
||||||
let mut android_sysroot = std::path::PathBuf::from(android_ndk_home);
|
|
||||||
android_sysroot.extend(["toolchains", "llvm", "prebuilt"]);
|
android_sysroot.extend(["toolchains", "llvm", "prebuilt"]);
|
||||||
|
|
||||||
let toolchain = match pick_best_android_ndk_toolchain(&android_sysroot) {
|
let toolchain = match pick_best_android_ndk_toolchain(&android_sysroot) {
|
||||||
Ok(toolchain) => toolchain,
|
Ok(toolchain) => toolchain,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -432,8 +411,6 @@ fn get_extra_clang_args_for_bindgen() -> Vec<String> {
|
||||||
android_sysroot.push(toolchain);
|
android_sysroot.push(toolchain);
|
||||||
android_sysroot.push("sysroot");
|
android_sysroot.push("sysroot");
|
||||||
params.push("--sysroot".to_string());
|
params.push("--sysroot".to_string());
|
||||||
// If ANDROID_NDK_HOME weren't a valid UTF-8 string,
|
|
||||||
// we'd already know from env::var.
|
|
||||||
params.push(android_sysroot.into_os_string().into_string().unwrap());
|
params.push(android_sysroot.into_os_string().into_string().unwrap());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -442,48 +419,43 @@ fn get_extra_clang_args_for_bindgen() -> Vec<String> {
|
||||||
params
|
params
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_patches_applied() -> io::Result<()> {
|
fn ensure_patches_applied(config: &Config) -> io::Result<()> {
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let mut lock_file = LockFile::open(&config.out_dir.join(".patch_lock"))?;
|
||||||
let mut lock_file = LockFile::open(&PathBuf::from(&out_dir).join(".patch_lock"))?;
|
let src_path = get_boringssl_source_path(config);
|
||||||
let src_path = get_boringssl_source_path();
|
let has_git = src_path.join(".git").exists();
|
||||||
let has_git = Path::new(&src_path).join(".git").exists();
|
|
||||||
|
|
||||||
lock_file.lock()?;
|
lock_file.lock()?;
|
||||||
|
|
||||||
// NOTE: init git in the copied files, so we can apply patches
|
// NOTE: init git in the copied files, so we can apply patches
|
||||||
if !has_git {
|
if !has_git {
|
||||||
run_command(Command::new("git").args(["init"]).current_dir(&src_path))?;
|
run_command(Command::new("git").arg("init").current_dir(src_path))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(feature = "pq-experimental") {
|
if config.features.pq_experimental {
|
||||||
println!("cargo:warning=applying experimental post quantum crypto patch to boringssl");
|
println!("cargo:warning=applying experimental post quantum crypto patch to boringssl");
|
||||||
apply_patch("boring-pq.patch")?;
|
apply_patch(config, "boring-pq.patch")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(feature = "rpk") {
|
if config.features.rpk {
|
||||||
println!("cargo:warning=applying RPK patch to boringssl");
|
println!("cargo:warning=applying RPK patch to boringssl");
|
||||||
apply_patch("rpk.patch")?;
|
apply_patch(config, "rpk.patch")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_patch(patch_name: &str) -> io::Result<()> {
|
fn apply_patch(config: &Config, patch_name: &str) -> io::Result<()> {
|
||||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
let src_path = get_boringssl_source_path(config);
|
||||||
let src_path = get_boringssl_source_path();
|
let cmd_path = config
|
||||||
let cmd_path = manifest_dir
|
.manifest_dir
|
||||||
.join("patches")
|
.join("patches")
|
||||||
.join(patch_name)
|
.join(patch_name)
|
||||||
.canonicalize()?;
|
.canonicalize()?;
|
||||||
|
|
||||||
run_command(
|
run_command(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args([
|
.args(["apply", "-v", "--whitespace=fix"])
|
||||||
"apply",
|
.arg(cmd_path)
|
||||||
"-v",
|
|
||||||
"--whitespace=fix",
|
|
||||||
&cmd_path.display().to_string(),
|
|
||||||
])
|
|
||||||
.current_dir(src_path),
|
.current_dir(src_path),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -508,58 +480,66 @@ fn run_command(command: &mut Command) -> io::Result<Output> {
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_boring_from_sources() -> String {
|
fn built_boring_source_path(config: &Config) -> &PathBuf {
|
||||||
if cfg!(feature = "no-patches") {
|
if let Some(path) = &config.env.path {
|
||||||
println!(
|
return path;
|
||||||
"cargo:warning=skipping git patches application, provided\
|
|
||||||
native BoringSSL is expected to have the patches included"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ensure_patches_applied().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cfg = get_boringssl_cmake_config();
|
static BUILD_SOURCE_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||||
|
|
||||||
if cfg!(feature = "fuzzing") {
|
BUILD_SOURCE_PATH.get_or_init(|| {
|
||||||
cfg.cxxflag("-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE")
|
if config.features.no_patches {
|
||||||
.cxxflag("-DBORINGSSL_UNSAFE_FUZZER_MODE");
|
println!(
|
||||||
}
|
"cargo:warning=skipping git patches application, provided\
|
||||||
|
native BoringSSL is expected to have the patches included"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ensure_patches_applied(config).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if cfg!(feature = "fips") {
|
let mut cfg = get_boringssl_cmake_config(config);
|
||||||
let (clang, clangxx) = verify_fips_clang_version();
|
|
||||||
cfg.define("CMAKE_C_COMPILER", clang);
|
|
||||||
cfg.define("CMAKE_CXX_COMPILER", clangxx);
|
|
||||||
cfg.define("CMAKE_ASM_COMPILER", clang);
|
|
||||||
cfg.define("FIPS", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg!(feature = "fips-link-precompiled") {
|
if config.features.fips {
|
||||||
cfg.define("FIPS", "1");
|
let (clang, clangxx) = verify_fips_clang_version();
|
||||||
}
|
cfg.define("CMAKE_C_COMPILER", clang)
|
||||||
|
.define("CMAKE_CXX_COMPILER", clangxx)
|
||||||
|
.define("CMAKE_ASM_COMPILER", clang)
|
||||||
|
.define("FIPS", "1");
|
||||||
|
}
|
||||||
|
|
||||||
cfg.build_target("ssl").build();
|
if config.features.fips_link_precompiled {
|
||||||
cfg.build_target("crypto").build().display().to_string()
|
cfg.define("FIPS", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.build_target("ssl").build();
|
||||||
|
cfg.build_target("crypto").build()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_in_precompiled_bcm_o(bssl_dir: &str) {
|
fn link_in_precompiled_bcm_o(config: &Config) {
|
||||||
println!("cargo:warning=linking in precompiled `bcm.o` module");
|
println!("cargo:warning=linking in precompiled `bcm.o` module");
|
||||||
|
|
||||||
let bcm_o_src_path = env::var("BORING_SSL_PRECOMPILED_BCM_O")
|
let bssl_dir = built_boring_source_path(config);
|
||||||
|
let bcm_o_src_path = config.env.precompiled_bcm_o.as_ref()
|
||||||
.expect("`fips-link-precompiled` requires `BORING_SSL_PRECOMPILED_BCM_O` env variable to be specified");
|
.expect("`fips-link-precompiled` requires `BORING_SSL_PRECOMPILED_BCM_O` env variable to be specified");
|
||||||
|
|
||||||
let libcrypto_path = PathBuf::from(bssl_dir)
|
let libcrypto_path = bssl_dir
|
||||||
.join("build/crypto/libcrypto.a")
|
.join("build/crypto/libcrypto.a")
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.display()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let bcm_o_dst_path = PathBuf::from(bssl_dir).join("build/bcm-fips.o");
|
let bcm_o_dst_path = bssl_dir.join("build/bcm-fips.o");
|
||||||
|
|
||||||
fs::copy(bcm_o_src_path, &bcm_o_dst_path).unwrap();
|
fs::copy(bcm_o_src_path, &bcm_o_dst_path).unwrap();
|
||||||
|
|
||||||
// check that fips module is named as expected
|
// check that fips module is named as expected
|
||||||
let out = run_command(Command::new("ar").args(["t", &libcrypto_path, "bcm.o"])).unwrap();
|
let out = run_command(
|
||||||
|
Command::new("ar")
|
||||||
|
.arg("t")
|
||||||
|
.arg(&libcrypto_path)
|
||||||
|
.arg("bcm.o"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
String::from_utf8(out.stdout).unwrap().trim(),
|
String::from_utf8(out.stdout).unwrap().trim(),
|
||||||
|
|
@ -572,90 +552,62 @@ fn link_in_precompiled_bcm_o(bssl_dir: &str) {
|
||||||
// (this causes the need for extra linker flags to deal with duplicate symbols)
|
// (this causes the need for extra linker flags to deal with duplicate symbols)
|
||||||
// (as long as the newer module does not define new symbols, one may also remove it,
|
// (as long as the newer module does not define new symbols, one may also remove it,
|
||||||
// but once there are new symbols it would cause missing symbols at linking stage)
|
// but once there are new symbols it would cause missing symbols at linking stage)
|
||||||
run_command(Command::new("ar").args([
|
run_command(
|
||||||
"rb",
|
Command::new("ar")
|
||||||
"bcm.o",
|
.args(["rb", "bcm.o"])
|
||||||
&libcrypto_path,
|
.args([&libcrypto_path, &bcm_o_dst_path]),
|
||||||
bcm_o_dst_path.display().to_string().as_str(),
|
)
|
||||||
]))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_feature_compatibility() {
|
|
||||||
#[cfg(all(feature = "fips", feature = "rpk"))]
|
|
||||||
compile_error!("`fips` and `rpk` features are mutually exclusive");
|
|
||||||
|
|
||||||
let no_patches_enabled = cfg!(feature = "no-patches");
|
|
||||||
let is_external_native_lib_source =
|
|
||||||
env::var("BORING_BSSL_PATH").is_err() && env::var("BORING_BSSL_SOURCE_PATH").is_err();
|
|
||||||
|
|
||||||
if no_patches_enabled && is_external_native_lib_source {
|
|
||||||
panic!(
|
|
||||||
"`no-patches` feature is supposed to be used with `BORING_BSSL_PATH`\
|
|
||||||
or `BORING_BSSL_SOURCE_PATH` env variables"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let features_with_patches_enabled = cfg!(any(feature = "rpk", feature = "pq-experimental"));
|
|
||||||
let patches_required = features_with_patches_enabled && !no_patches_enabled;
|
|
||||||
let build_from_sources_required = cfg!(feature = "fips-link-precompiled") || patches_required;
|
|
||||||
let is_precompiled_native_lib = env::var("BORING_BSSL_PATH").is_ok();
|
|
||||||
|
|
||||||
if is_precompiled_native_lib && build_from_sources_required {
|
|
||||||
panic!("precompiled BoringSSL was provided, so FIPS configuration or optional patches can't be applied");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH");
|
let config = Config::from_env();
|
||||||
println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH");
|
let bssl_dir = built_boring_source_path(&config);
|
||||||
println!("cargo:rerun-if-env-changed=BORING_BSSL_SOURCE_PATH");
|
let build_path = get_boringssl_platform_output_path(&config);
|
||||||
println!("cargo:rerun-if-env-changed=BORING_SSL_PRECOMPILED_BCM_O");
|
|
||||||
println!("cargo:rerun-if-env-changed=BORINGSSL_BUILD_DIR");
|
|
||||||
|
|
||||||
check_feature_compatibility();
|
if config.features.fips || config.features.fips_link_precompiled {
|
||||||
|
|
||||||
let bssl_dir = env::var("BORING_BSSL_PATH").unwrap_or_else(|_| build_boring_from_sources());
|
|
||||||
let build_path = get_boringssl_platform_output_path();
|
|
||||||
|
|
||||||
if cfg!(any(feature = "fips", feature = "fips-link-precompiled")) {
|
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-link-search=native={}/build/crypto/{}",
|
"cargo:rustc-link-search=native={}/build/crypto/{}",
|
||||||
bssl_dir, build_path
|
bssl_dir.display(),
|
||||||
|
build_path
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-link-search=native={}/build/ssl/{}",
|
"cargo:rustc-link-search=native={}/build/ssl/{}",
|
||||||
bssl_dir, build_path
|
bssl_dir.display(),
|
||||||
|
build_path
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-link-search=native={}/lib/{}",
|
"cargo:rustc-link-search=native={}/lib/{}",
|
||||||
bssl_dir, build_path
|
bssl_dir.display(),
|
||||||
|
build_path
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-link-search=native={}/build/{}",
|
"cargo:rustc-link-search=native={}/build/{}",
|
||||||
bssl_dir, build_path
|
bssl_dir.display(),
|
||||||
|
build_path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(feature = "fips-link-precompiled") {
|
if config.features.fips_link_precompiled {
|
||||||
link_in_precompiled_bcm_o(&bssl_dir);
|
link_in_precompiled_bcm_o(&config);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:rustc-link-lib=static=crypto");
|
println!("cargo:rustc-link-lib=static=crypto");
|
||||||
println!("cargo:rustc-link-lib=static=ssl");
|
println!("cargo:rustc-link-lib=static=ssl");
|
||||||
|
|
||||||
let include_path = env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| {
|
let include_path = config.env.include_path.clone().unwrap_or_else(|| {
|
||||||
if let Ok(bssl_path) = env::var("BORING_BSSL_PATH") {
|
if let Some(bssl_path) = &config.env.path {
|
||||||
return format!("{}/include", bssl_path);
|
return bssl_path.join("include");
|
||||||
}
|
}
|
||||||
|
|
||||||
let src_path = get_boringssl_source_path();
|
let src_path = get_boringssl_source_path(&config);
|
||||||
|
let candidate = src_path.join("include");
|
||||||
|
|
||||||
if Path::new(&src_path).join("include").exists() {
|
if candidate.exists() {
|
||||||
format!("{}/include", &src_path)
|
candidate
|
||||||
} else {
|
} else {
|
||||||
format!("{}/src/include", &src_path)
|
src_path.join("src").join("include")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -674,11 +626,11 @@ fn main() {
|
||||||
.size_t_is_usize(true)
|
.size_t_is_usize(true)
|
||||||
.layout_tests(true)
|
.layout_tests(true)
|
||||||
.prepend_enum_name(true)
|
.prepend_enum_name(true)
|
||||||
.clang_args(get_extra_clang_args_for_bindgen())
|
.clang_args(get_extra_clang_args_for_bindgen(&config))
|
||||||
.clang_args(&["-I", &include_path]);
|
.clang_arg("-I")
|
||||||
|
.clang_arg(include_path.display().to_string());
|
||||||
|
|
||||||
let target = env::var("TARGET").unwrap();
|
match &*config.target {
|
||||||
match target.as_ref() {
|
|
||||||
// bindgen produces alignment tests that cause undefined behavior [1]
|
// bindgen produces alignment tests that cause undefined behavior [1]
|
||||||
// when applied to explicitly unaligned types like OSUnalignedU64.
|
// when applied to explicitly unaligned types like OSUnalignedU64.
|
||||||
//
|
//
|
||||||
|
|
@ -726,18 +678,11 @@ fn main() {
|
||||||
"x509v3.h",
|
"x509v3.h",
|
||||||
];
|
];
|
||||||
for header in &headers {
|
for header in &headers {
|
||||||
builder = builder.header(
|
builder = builder.header(include_path.join("openssl").join(header).to_str().unwrap());
|
||||||
Path::new(&include_path)
|
|
||||||
.join("openssl")
|
|
||||||
.join(header)
|
|
||||||
.to_str()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let bindings = builder.generate().expect("Unable to generate bindings");
|
let bindings = builder.generate().expect("Unable to generate bindings");
|
||||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
||||||
bindings
|
bindings
|
||||||
.write_to_file(out_path.join("bindings.rs"))
|
.write_to_file(config.out_dir.join("bindings.rs"))
|
||||||
.expect("Couldn't write bindings!");
|
.expect("Couldn't write bindings!");
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue