aboutsummaryrefslogtreecommitdiff
path: root/openssl-sys/build
diff options
context:
space:
mode:
authorSteven Fackler <[email protected]>2018-05-19 19:43:02 -0700
committerSteven Fackler <[email protected]>2018-05-19 19:57:12 -0700
commitd991566f2b1b6803ad214fe6bf531d5870ab43fd (patch)
treed1d6e42fef37739dde443d8e5a66851a59af1c74 /openssl-sys/build
parentMerge pull request #921 from eonil/master (diff)
downloadrust-openssl-d991566f2b1b6803ad214fe6bf531d5870ab43fd.tar.xz
rust-openssl-d991566f2b1b6803ad214fe6bf531d5870ab43fd.zip
Support min/max version in LibreSSL
Their implementations of the accessors don't behave expected with no bounds, so we ignore those bits of the tests.
Diffstat (limited to 'openssl-sys/build')
-rw-r--r--openssl-sys/build/cfgs.rs45
-rw-r--r--openssl-sys/build/main.rs572
2 files changed, 617 insertions, 0 deletions
diff --git a/openssl-sys/build/cfgs.rs b/openssl-sys/build/cfgs.rs
new file mode 100644
index 00000000..3e063aa3
--- /dev/null
+++ b/openssl-sys/build/cfgs.rs
@@ -0,0 +1,45 @@
+pub fn get(openssl_version: Option<u64>, libressl_version: Option<u64>) -> Vec<&'static str> {
+ let mut cfgs = vec![];
+
+ if let Some(libressl_version) = libressl_version {
+ cfgs.push("libressl");
+
+ if libressl_version >= 0x2_05_01_00_0 {
+ cfgs.push("libressl251");
+ }
+
+ if libressl_version >= 0x2_06_01_00_0 {
+ cfgs.push("libressl261");
+ }
+
+ if libressl_version >= 0x2_07_00_00_0 {
+ cfgs.push("libressl270");
+ }
+ } else {
+ let openssl_version = openssl_version.unwrap();
+
+ if openssl_version >= 0x1_00_02_08_0 {
+ cfgs.push("ossl102h");
+ }
+
+ if openssl_version >= 0x1_01_00_07_0 {
+ cfgs.push("ossl110g");
+ }
+
+ if openssl_version >= 0x1_01_01_00_0 {
+ cfgs.push("ossl111");
+ cfgs.push("ossl110");
+ } else if openssl_version >= 0x1_01_00_06_0 {
+ cfgs.push("ossl110");
+ cfgs.push("ossl110f");
+ } else if openssl_version >= 0x1_01_00_00_0 {
+ cfgs.push("ossl110");
+ } else if openssl_version >= 0x1_00_02_00_0 {
+ cfgs.push("ossl102");
+ } else if openssl_version >= 0x1_00_01_00_0 {
+ cfgs.push("ossl101");
+ }
+ }
+
+ cfgs
+}
diff --git a/openssl-sys/build/main.rs b/openssl-sys/build/main.rs
new file mode 100644
index 00000000..e2b3108c
--- /dev/null
+++ b/openssl-sys/build/main.rs
@@ -0,0 +1,572 @@
+extern crate cc;
+extern crate pkg_config;
+#[cfg(target_env = "msvc")]
+extern crate vcpkg;
+
+use std::collections::HashSet;
+use std::env;
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::{BufWriter, Write};
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+mod cfgs;
+
+// The set of `OPENSSL_NO_<FOO>`s that we care about.
+const DEFINES: &'static [&'static str] = &[
+ "OPENSSL_NO_BUF_FREELISTS",
+ "OPENSSL_NO_COMP",
+ "OPENSSL_NO_EC",
+ "OPENSSL_NO_EC2M",
+ "OPENSSL_NO_ENGINE",
+ "OPENSSL_NO_KRB5",
+ "OPENSSL_NO_NEXTPROTONEG",
+ "OPENSSL_NO_PSK",
+ "OPENSSL_NO_RFC3779",
+ "OPENSSL_NO_SHA",
+ "OPENSSL_NO_SRP",
+ "OPENSSL_NO_SSL3_METHOD",
+ "OPENSSL_NO_TLSEXT",
+];
+
+enum Version {
+ Openssl11x,
+ Openssl10x,
+ Libressl,
+}
+
+fn env(name: &str) -> Option<OsString> {
+ let prefix = env::var("TARGET").unwrap().to_uppercase().replace("-", "_");
+ let prefixed = format!("{}_{}", prefix, name);
+ println!("cargo:rerun-if-env-changed={}", prefixed);
+
+ if let Some(var) = env::var_os(&prefixed) {
+ return Some(var);
+ }
+
+ println!("cargo:rerun-if-env-changed={}", name);
+ env::var_os(name)
+}
+
+fn main() {
+ let target = env::var("TARGET").unwrap();
+
+ let lib_dir = env("OPENSSL_LIB_DIR").map(PathBuf::from);
+ let include_dir = env("OPENSSL_INCLUDE_DIR").map(PathBuf::from);
+
+ let (lib_dir, include_dir) = if lib_dir.is_none() || include_dir.is_none() {
+ let openssl_dir = env("OPENSSL_DIR").unwrap_or_else(|| find_openssl_dir(&target));
+ let openssl_dir = Path::new(&openssl_dir);
+ let lib_dir = lib_dir.unwrap_or_else(|| openssl_dir.join("lib"));
+ let include_dir = include_dir.unwrap_or_else(|| openssl_dir.join("include"));
+ (lib_dir, include_dir)
+ } else {
+ (lib_dir.unwrap(), include_dir.unwrap())
+ };
+
+ if !Path::new(&lib_dir).exists() {
+ panic!(
+ "OpenSSL library directory does not exist: {}",
+ lib_dir.to_string_lossy()
+ );
+ }
+ if !Path::new(&include_dir).exists() {
+ panic!(
+ "OpenSSL include directory does not exist: {}",
+ include_dir.to_string_lossy()
+ );
+ }
+
+ println!(
+ "cargo:rustc-link-search=native={}",
+ lib_dir.to_string_lossy()
+ );
+ println!("cargo:include={}", include_dir.to_string_lossy());
+
+ let version = validate_headers(&[include_dir.clone().into()]);
+
+ let libs_env = env("OPENSSL_LIBS");
+ let libs = match libs_env.as_ref().and_then(|s| s.to_str()) {
+ Some(ref v) => v.split(":").collect(),
+ None => match version {
+ Version::Openssl10x if target.contains("windows") => vec!["ssleay32", "libeay32"],
+ Version::Openssl11x if target.contains("windows") => vec!["libssl", "libcrypto"],
+ _ => vec!["ssl", "crypto"],
+ },
+ };
+
+ let kind = determine_mode(Path::new(&lib_dir), &libs);
+ for lib in libs.into_iter() {
+ println!("cargo:rustc-link-lib={}={}", kind, lib);
+ }
+}
+
+fn find_openssl_dir(target: &str) -> OsString {
+ let host = env::var("HOST").unwrap();
+
+ if host == target && target.contains("apple-darwin") {
+ // Check up default Homebrew installation location first
+ // for quick resolution if possible.
+ let homebrew = Path::new("/usr/local/opt/[email protected]");
+ if homebrew.exists() {
+ return homebrew.to_path_buf().into();
+ }
+ let homebrew = Path::new("/usr/local/opt/openssl");
+ if homebrew.exists() {
+ return homebrew.to_path_buf().into();
+ }
+ // Calling `brew --prefix <package>` command usually slow and
+ // takes seconds, and will be used only as a last resort.
+ let output = execute_command_and_get_output("brew", &["--prefix", "[email protected]"]);
+ if let Some(ref output) = output {
+ let homebrew = Path::new(&output);
+ if homebrew.exists() {
+ return homebrew.to_path_buf().into();
+ }
+ }
+ let output = execute_command_and_get_output("brew", &["--prefix", "openssl"]);
+ if let Some(ref output) = output {
+ let homebrew = Path::new(&output);
+ if homebrew.exists() {
+ return homebrew.to_path_buf().into();
+ }
+ }
+ }
+
+ try_pkg_config();
+ try_vcpkg();
+
+ // FreeBSD ships with OpenSSL but doesn't include a pkg-config file :(
+ if host == target && target.contains("freebsd") {
+ return OsString::from("/usr");
+ }
+
+ let mut msg = format!(
+ "
+
+Could not find directory of OpenSSL installation, and this `-sys` crate cannot
+proceed without this knowledge. If OpenSSL is installed and this crate had
+trouble finding it, you can set the `OPENSSL_DIR` environment variable for the
+compilation process.
+
+Make sure you also have the development packages of openssl installed.
+For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora.
+
+If you're in a situation where you think the directory *should* be found
+automatically, please open a bug at https://github.com/sfackler/rust-openssl
+and include information about your system as well as this message.
+
+ $HOST = {}
+ $TARGET = {}
+ openssl-sys = {}
+
+",
+ host,
+ target,
+ env!("CARGO_PKG_VERSION")
+ );
+
+ if host.contains("apple-darwin") && target.contains("apple-darwin") {
+ let system = Path::new("/usr/lib/libssl.0.9.8.dylib");
+ if system.exists() {
+ msg.push_str(&format!(
+ "
+
+It looks like you're compiling on macOS, where the system contains a version of
+OpenSSL 0.9.8. This crate no longer supports OpenSSL 0.9.8.
+
+As a consumer of this crate, you can fix this error by using Homebrew to
+install the `openssl` package, or as a maintainer you can use the openssl-sys
+0.7 crate for support with OpenSSL 0.9.8.
+
+Unfortunately though the compile cannot continue, so aborting.
+
+"
+ ));
+ }
+ }
+
+ if host.contains("unknown-linux") && target.contains("unknown-linux-gnu") {
+ if Command::new("pkg-config").output().is_err() {
+ msg.push_str(&format!(
+ "
+It looks like you're compiling on Linux and also targeting Linux. Currently this
+requires the `pkg-config` utility to find OpenSSL but unfortunately `pkg-config`
+could not be found. If you have OpenSSL installed you can likely fix this by
+installing `pkg-config`.
+
+"
+ ));
+ }
+ }
+
+ if host.contains("windows") && target.contains("windows-gnu") {
+ msg.push_str(&format!(
+ "
+It looks like you're compiling for MinGW but you may not have either OpenSSL or
+pkg-config installed. You can install these two dependencies with:
+
+ pacman -S openssl-devel pkg-config
+
+and try building this crate again.
+
+"
+ ));
+ }
+
+ if host.contains("windows") && target.contains("windows-msvc") {
+ msg.push_str(&format!(
+ "
+It looks like you're compiling for MSVC but we couldn't detect an OpenSSL
+installation. If there isn't one installed then you can try the rust-openssl
+README for more information about how to download precompiled binaries of
+OpenSSL:
+
+ https://github.com/sfackler/rust-openssl#windows
+
+"
+ ));
+ }
+
+ panic!(msg);
+}
+
+/// Attempt to find OpenSSL through pkg-config.
+///
+/// Note that if this succeeds then the function does not return as pkg-config
+/// typically tells us all the information that we need.
+fn try_pkg_config() {
+ let target = env::var("TARGET").unwrap();
+ let host = env::var("HOST").unwrap();
+
+ // If we're going to windows-gnu we can use pkg-config, but only so long as
+ // we're coming from a windows host.
+ //
+ // Otherwise if we're going to windows we probably can't use pkg-config.
+ if target.contains("windows-gnu") && host.contains("windows") {
+ env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
+ } else if target.contains("windows") {
+ return;
+ }
+
+ let lib = match pkg_config::Config::new()
+ .print_system_libs(false)
+ .find("openssl")
+ {
+ Ok(lib) => lib,
+ Err(e) => {
+ println!("run pkg_config fail: {:?}", e);
+ return;
+ }
+ };
+
+ validate_headers(&lib.include_paths);
+
+ for include in lib.include_paths.iter() {
+ println!("cargo:include={}", include.display());
+ }
+
+ std::process::exit(0);
+}
+
+/// Attempt to find OpenSSL through vcpkg.
+///
+/// Note that if this succeeds then the function does not return as vcpkg
+/// should emit all of the cargo metadata that we need.
+#[cfg(target_env = "msvc")]
+fn try_vcpkg() {
+ // vcpkg will not emit any metadata if it can not find libraries
+ // appropriate for the target triple with the desired linkage.
+
+ let mut lib = vcpkg::Config::new()
+ .emit_includes(true)
+ .lib_name("libcrypto")
+ .lib_name("libssl")
+ .probe("openssl");
+
+ if let Err(e) = lib {
+ println!(
+ "note: vcpkg did not find openssl as libcrypto and libssl : {:?}",
+ e
+ );
+ lib = vcpkg::Config::new()
+ .emit_includes(true)
+ .lib_name("libeay32")
+ .lib_name("ssleay32")
+ .probe("openssl");
+ }
+ if let Err(e) = lib {
+ println!(
+ "note: vcpkg did not find openssl as ssleay32 and libeay32: {:?}",
+ e
+ );
+ return;
+ }
+
+ let lib = lib.unwrap();
+ validate_headers(&lib.include_paths);
+
+ println!("cargo:rustc-link-lib=user32");
+ println!("cargo:rustc-link-lib=gdi32");
+ println!("cargo:rustc-link-lib=crypt32");
+
+ std::process::exit(0);
+}
+
+#[cfg(not(target_env = "msvc"))]
+fn try_vcpkg() {}
+
+/// Validates the header files found in `include_dir` and then returns the
+/// version string of OpenSSL.
+fn validate_headers(include_dirs: &[PathBuf]) -> Version {
+ // This `*-sys` crate only works with OpenSSL 1.0.1, 1.0.2, and 1.1.0. To
+ // correctly expose the right API from this crate, take a look at
+ // `opensslv.h` to see what version OpenSSL claims to be.
+ //
+ // OpenSSL has a number of build-time configuration options which affect
+ // various structs and such. Since OpenSSL 1.1.0 this isn't really a problem
+ // as the library is much more FFI-friendly, but 1.0.{1,2} suffer this problem.
+ //
+ // To handle all this conditional compilation we slurp up the configuration
+ // file of OpenSSL, `opensslconf.h`, and then dump out everything it defines
+ // as our own #[cfg] directives. That way the `ossl10x.rs` bindings can
+ // account for compile differences and such.
+ let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ path.push("expando.c");
+ let mut file = BufWriter::new(File::create(&path).unwrap());
+
+ write!(
+ file,
+ "\
+#include <openssl/opensslv.h>
+#include <openssl/opensslconf.h>
+
+#define VERSION2(n, v) RUST_VERSION_ ## n ## _ ## v
+#define VERSION(n, v) VERSION2(n, v)
+
+VERSION(OPENSSL, OPENSSL_VERSION_NUMBER)
+
+#ifdef LIBRESSL_VERSION_NUMBER
+VERSION(LIBRESSL, LIBRESSL_VERSION_NUMBER)
+#endif
+"
+ ).unwrap();
+
+ for define in DEFINES {
+ write!(
+ file,
+ "\
+#ifdef {define}
+RUST_CONF_{define}
+#endif
+",
+ define = define
+ ).unwrap();
+ }
+
+ file.flush().unwrap();
+ drop(file);
+
+ let mut gcc = cc::Build::new();
+ for include_dir in include_dirs {
+ gcc.include(include_dir);
+ }
+ // https://github.com/alexcrichton/gcc-rs/issues/133
+ let expanded = match gcc.file(&path).try_expand() {
+ Ok(expanded) => expanded,
+ Err(e) => {
+ panic!(
+ "
+Header expansion error:
+{:?}
+
+Failed to find OpenSSL development headers.
+
+You can try fixing this setting the `OPENSSL_DIR` environment variable
+pointing to your OpenSSL installation or installing OpenSSL headers package
+specific to your distribution:
+
+ # On Ubuntu
+ sudo apt-get install libssl-dev
+ # On Arch Linux
+ sudo pacman -S openssl
+ # On Fedora
+ sudo dnf install openssl-devel
+
+See rust-openssl README for more information:
+
+ https://github.com/sfackler/rust-openssl#linux
+",
+ e
+ );
+ }
+ };
+ let expanded = String::from_utf8(expanded).unwrap();
+
+ let mut enabled = vec![];
+ let mut openssl_version = None;
+ let mut libressl_version = None;
+ for line in expanded.lines() {
+ let line = line.trim();
+
+ let openssl_prefix = "RUST_VERSION_OPENSSL_";
+ let libressl_prefix = "RUST_VERSION_LIBRESSL_";
+ let conf_prefix = "RUST_CONF_";
+ if line.starts_with(openssl_prefix) {
+ let version = &line[openssl_prefix.len()..];
+ openssl_version = Some(parse_version(version));
+ } else if line.starts_with(libressl_prefix) {
+ let version = &line[libressl_prefix.len()..];
+ libressl_version = Some(parse_version(version));
+ } else if line.starts_with(conf_prefix) {
+ enabled.push(&line[conf_prefix.len()..]);
+ }
+ }
+
+ for enabled in &enabled {
+ println!("cargo:rustc-cfg=osslconf=\"{}\"", enabled);
+ }
+ println!("cargo:conf={}", enabled.join(","));
+
+ for cfg in cfgs::get(openssl_version, libressl_version) {
+ println!("cargo:rustc-cfg={}", cfg);
+ }
+
+ if let Some(libressl_version) = libressl_version {
+ println!("cargo:libressl_version_number={:x}", libressl_version);
+
+ let minor = (libressl_version >> 20) as u8;
+ let fix = (libressl_version >> 12) as u8;
+ let (minor, fix) = match (minor, fix) {
+ (5, 0) => ('5', '0'),
+ (5, 1) => ('5', '1'),
+ (5, 2) => ('5', '2'),
+ (5, _) => ('5', 'x'),
+ (6, 0) => ('6', '0'),
+ (6, 1) => ('6', '1'),
+ (6, 2) => ('6', '2'),
+ (6, _) => ('6', 'x'),
+ (7, _) => ('7', 'x'),
+ _ => version_error(),
+ };
+
+ println!("cargo:libressl=true");
+ println!("cargo:libressl_version=2{}{}", minor, fix);
+ println!("cargo:version=101");
+ Version::Libressl
+ } else {
+ let openssl_version = openssl_version.unwrap();
+ println!("cargo:version_number={:x}", openssl_version);
+
+ if openssl_version >= 0x1_01_02_00_0 {
+ version_error()
+ } else if openssl_version >= 0x1_01_01_00_0 {
+ println!("cargo:version=111");
+ Version::Openssl11x
+ } else if openssl_version >= 0x1_01_00_06_0 {
+ println!("cargo:version=110");
+ println!("cargo:patch=f");
+ Version::Openssl11x
+ } else if openssl_version >= 0x1_01_00_00_0 {
+ println!("cargo:version=110");
+ Version::Openssl11x
+ } else if openssl_version >= 0x1_00_02_00_0 {
+ println!("cargo:version=102");
+ Version::Openssl10x
+ } else if openssl_version >= 0x1_00_01_00_0 {
+ println!("cargo:version=101");
+ Version::Openssl10x
+ } else {
+ version_error()
+ }
+ }
+}
+
+fn version_error() -> ! {
+ panic!(
+ "
+
+This crate is only compatible with OpenSSL 1.0.1 through 1.1.1, or LibreSSL 2.5
+through 2.7, but a different version of OpenSSL was found. The build is now aborting
+due to this version mismatch.
+
+"
+ );
+}
+
+// parses a string that looks like "0x100020cfL"
+fn parse_version(version: &str) -> u64 {
+ // cut off the 0x prefix
+ assert!(version.starts_with("0x"));
+ let version = &version[2..];
+
+ // and the type specifier suffix
+ let version = version.trim_right_matches(|c: char| match c {
+ '0'...'9' | 'a'...'f' | 'A'...'F' => false,
+ _ => true,
+ });
+
+ u64::from_str_radix(version, 16).unwrap()
+}
+
+/// Given a libdir for OpenSSL (where artifacts are located) as well as the name
+/// of the libraries we're linking to, figure out whether we should link them
+/// statically or dynamically.
+fn determine_mode(libdir: &Path, libs: &[&str]) -> &'static str {
+ // First see if a mode was explicitly requested
+ let kind = env("OPENSSL_STATIC");
+ match kind.as_ref().and_then(|s| s.to_str()).map(|s| &s[..]) {
+ Some("0") => return "dylib",
+ Some(_) => return "static",
+ None => {}
+ }
+
+ // Next, see what files we actually have to link against, and see what our
+ // possibilities even are.
+ let files = libdir
+ .read_dir()
+ .unwrap()
+ .map(|e| e.unwrap())
+ .map(|e| e.file_name())
+ .filter_map(|e| e.into_string().ok())
+ .collect::<HashSet<_>>();
+ let can_static = libs
+ .iter()
+ .all(|l| files.contains(&format!("lib{}.a", l)) || files.contains(&format!("{}.lib", l)));
+ let can_dylib = libs.iter().all(|l| {
+ files.contains(&format!("lib{}.so", l))
+ || files.contains(&format!("{}.dll", l))
+ || files.contains(&format!("lib{}.dylib", l))
+ });
+ match (can_static, can_dylib) {
+ (true, false) => return "static",
+ (false, true) => return "dylib",
+ (false, false) => {
+ panic!(
+ "OpenSSL libdir at `{}` does not contain the required files \
+ to either statically or dynamically link OpenSSL",
+ libdir.display()
+ );
+ }
+ (true, true) => {}
+ }
+
+ // Ok, we've got not explicit preference and can *either* link statically or
+ // link dynamically. In the interest of "security upgrades" and/or "best
+ // practices with security libs", let's link dynamically.
+ "dylib"
+}
+
+fn execute_command_and_get_output(cmd: &str, args: &[&str]) -> Option<String> {
+ let out = Command::new(cmd).args(args).output();
+ if let Ok(ref r1) = out {
+ if r1.status.success() {
+ let r2 = String::from_utf8(r1.stdout.clone());
+ if let Ok(r3) = r2 {
+ return Some(r3.trim().to_string());
+ }
+ }
+ }
+ return None;
+}