Merge pull request #569 from sfackler/expando

Macro-expand OpenSSL headers for feature checks
This commit is contained in:
Steven Fackler 2017-01-28 20:50:10 -08:00 committed by GitHub
commit a27bc0a3ca
4 changed files with 119 additions and 134 deletions

View File

@ -19,7 +19,7 @@ matrix:
# ARM-bit version compat
- env: >
TARGET=arm-unknown-linux-gnueabihf
BUILD_OPENSSL_VERSION=1.0.2h
BUILD_OPENSSL_VERSION=1.0.2k
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf
RUST_TEST_THREADS=1
@ -32,7 +32,7 @@ matrix:
- binfmt-support
- env: >
TARGET=arm-unknown-linux-gnueabihf
BUILD_OPENSSL_VERSION=1.1.0c
BUILD_OPENSSL_VERSION=1.1.0d
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf
RUST_TEST_THREADS=1
@ -52,8 +52,8 @@ matrix:
- rust: nightly
# 64-bit version compat
- env: BUILD_OPENSSL_VERSION=1.0.2h
- env: BUILD_OPENSSL_VERSION=1.1.0c
- env: BUILD_OPENSSL_VERSION=1.0.2k
- env: BUILD_OPENSSL_VERSION=1.1.0d
# 32-bit version compat
- env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.0.1u
@ -61,12 +61,12 @@ matrix:
apt:
packages:
- gcc-multilib
- env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.0.2h
- env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.0.2k
addons:
apt:
packages:
- gcc-multilib
- env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.1.0c
- env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.1.0d
addons:
apt:
packages:

View File

@ -5,20 +5,20 @@ environment:
- TARGET: i686-pc-windows-gnu
BITS: 32
MSYS2: 1
OPENSSL_VERSION: 1_1_0c
OPENSSL_VERSION: 1_1_0d
- TARGET: x86_64-pc-windows-msvc
BITS: 64
OPENSSL_VERSION: 1_1_0c
OPENSSL_VERSION: 1_1_0d
OPENSSL_DIR: C:\OpenSSL
# 1.0.2, 64/32 bit
- TARGET: x86_64-pc-windows-gnu
BITS: 64
MSYS2: 1
OPENSSL_VERSION: 1_0_2j
OPENSSL_VERSION: 1_0_2k
- TARGET: i686-pc-windows-msvc
BITS: 32
OPENSSL_VERSION: 1_0_2j
OPENSSL_VERSION: 1_0_2k
OPENSSL_DIR: C:\OpenSSL
install:
# install OpenSSL

View File

@ -16,11 +16,12 @@ libc = "0.2"
[build-dependencies]
pkg-config = "0.3.9"
gcc = "0.3.42"
[target.'cfg(windows)'.dependencies]
user32-sys = "0.2"
gdi32-sys = "0.2"
# We don't actually use metadeps for annoying reasons but this is still hear for tooling
# We don't actually use metadeps for annoying reasons but this is still here for tooling
[package.metadata.pkg-config]
openssl = "1.0.1"

View File

@ -1,13 +1,37 @@
extern crate pkg_config;
extern crate gcc;
use std::collections::HashSet;
use std::env;
use std::ffi::OsString;
use std::fs::File;
use std::io::Read;
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
// 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_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 {
Openssl110,
Openssl102,
Openssl101,
Libressl,
}
fn main() {
let target = env::var("TARGET").unwrap();
@ -38,17 +62,14 @@ fn main() {
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()],
&[lib_dir.clone().into()]);
let version = validate_headers(&[include_dir.clone().into()]);
let libs = if (version.contains("0x10001") ||
version.contains("0x10002")) &&
target.contains("windows") {
let libs = match version {
Version::Openssl101 | Version::Openssl102 if target.contains("windows") => {
["ssleay32", "libeay32"]
} else if target.contains("windows") {
["libssl", "libcrypto"]
} else {
["ssl", "crypto"]
}
Version::Openssl110 if target.contains("windows") => ["libssl", "libcrypto"],
_ => ["ssl", "crypto"],
};
let kind = determine_mode(Path::new(&lib_dir), &libs);
@ -209,7 +230,7 @@ See rust-openssl README for more information:
");
}
validate_headers(&lib.include_paths, &lib.link_paths);
validate_headers(&lib.include_paths);
for include in lib.include_paths.iter() {
println!("cargo:include={}", include.display());
@ -225,74 +246,11 @@ See rust-openssl README for more information:
/// Validates the header files found in `include_dir` and then returns the
/// version string of OpenSSL.
fn validate_headers(include_dirs: &[PathBuf],
libdirs: &[PathBuf]) -> String {
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.
let mut version_header = String::new();
let mut include = include_dirs.iter()
.map(|p| p.join("openssl/opensslv.h"))
.filter(|p| p.exists());
let mut f = match include.next() {
Some(f) => File::open(f).unwrap(),
None => {
panic!("failed to open header file at `openssl/opensslv.h` to learn
about OpenSSL's version number, looked inside:\n\n{:#?}\n\n",
include_dirs);
}
};
f.read_to_string(&mut version_header).unwrap();
// Do a bit of string parsing to find `#define OPENSSL_VERSION_NUMBER ...`
let version_line = version_header.lines().find(|l| {
l.contains("define ") && l.contains("OPENSSL_VERSION_NUMBER")
}).and_then(|line| {
let start = match line.find("0x") {
Some(start) => start,
None => return None,
};
Some(line[start..].trim())
});
let version_text = match version_line {
Some(text) => text,
None => {
panic!("header file at `{}` did not include `OPENSSL_VERSION_NUMBER` \
that this crate recognized, failed to learn about the \
OpenSSL version number");
}
};
if version_text.contains("0x10001") {
println!("cargo:rustc-cfg=ossl101");
println!("cargo:version=101");
} else if version_text.contains("0x10002") {
println!("cargo:rustc-cfg=ossl102");
println!("cargo:version=102");
} else if version_text.contains("0x10100") {
println!("cargo:rustc-cfg=ossl110");
println!("cargo:version=110");
} else if version_text.contains("0x20000000L") {
// Check if it is really LibreSSL
if version_header.lines().any(|l| {
l.contains("define ") && l.contains("LIBRESSL_VERSION_NUMBER")
}) {
println!("cargo:rustc-cfg=libressl");
println!("cargo:libressl=true");
println!("cargo:version=101");
}
} else {
panic!("
This crate is only compatible with OpenSSL 1.0.1, 1.0.2, and 1.1.0, but a
different version of OpenSSL was found:
{}
The build is now aborting due to this version mismatch.
", version_text);
}
//
// 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.
@ -301,56 +259,82 @@ The build is now aborting due to this version mismatch.
// 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 conf_header = String::new();
let mut include = include_dirs.iter()
.map(|p| p.join("openssl/opensslconf.h"))
.filter(|p| p.exists());
let mut f = match include.next() {
Some(f) => File::open(f).unwrap(),
None => {
// It's been seen that on linux the include dir printed out by
// `pkg-config` doesn't actually have opensslconf.h. Instead
// it's in an architecture-specific include directory.
//
// Try to detect that case to see if it exists.
let mut libdirs = libdirs.iter().map(|p| {
p.iter()
.map(|p| if p == "lib" {"include".as_ref()} else {p})
.collect::<PathBuf>()
}).map(|p| {
p.join("openssl/opensslconf.h")
}).filter(|p| p.exists());
match libdirs.next() {
Some(f) => File::open(f).unwrap(),
None => {
panic!("failed to open header file at
`openssl/opensslconf.h` to learn about \
OpenSSL's version number, looked \
inside:\n\n{:#?}\n\n",
include_dirs);
}
}
}
};
f.read_to_string(&mut conf_header).unwrap();
let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
path.push("expando.c");
let mut file = BufWriter::new(File::create(&path).unwrap());
// Look for `#define OPENSSL_FOO`, print out everything as our own
// #[cfg] flag.
let mut vars = vec![];
for line in conf_header.lines() {
let i = match line.find("define ") {
Some(i) => i,
None => continue,
};
let var = line[i + "define ".len()..].trim();
if var.starts_with("OPENSSL") && !var.contains(" ") {
println!("cargo:rustc-cfg=osslconf=\"{}\"", var);
vars.push(var);
}
}
println!("cargo:conf={}", vars.join(","));
write!(file, "\
#include <openssl/opensslv.h>
#include <openssl/opensslconf.h>
return version_text.to_string()
#ifdef LIBRESSL_VERSION_NUMBER
RUST_LIBRESSL
#elif OPENSSL_VERSION_NUMBER >= 0x10200000
RUST_OPENSSL_NEW
#elif OPENSSL_VERSION_NUMBER >= 0x10100000
RUST_OPENSSL_110
#elif OPENSSL_VERSION_NUMBER >= 0x10002000
RUST_OPENSSL_102
#elif OPENSSL_VERSION_NUMBER >= 0x10001000
RUST_OPENSSL_101
#else
RUST_OPENSSL_OLD
#endif
").unwrap();
for define in DEFINES {
write!(file, "\
#ifdef {define}
RUST_{define}
#endif
", define = define).unwrap();
}
file.flush().unwrap();
drop(file);
let mut gcc = gcc::Config::new();
for include_dir in include_dirs {
gcc.include(include_dir);
}
let expanded = gcc.file(&path).expand();
let expanded = String::from_utf8(expanded).unwrap();
let mut enabled = vec![];
for &define in DEFINES {
if expanded.contains(&format!("RUST_{}", define)) {
println!("cargo:rustc-cfg=osslconf=\"{}\"", define);
enabled.push(define);
}
}
println!("cargo:conf={}", enabled.join(","));
if expanded.contains("RUST_LIBRESSL") {
println!("cargo:rustc-cfg=libressl");
println!("cargo:libressl=true");
println!("cargo:version=101");
Version::Libressl
} else if expanded.contains("RUST_OPENSSL_110") {
println!("cargo:rustc-cfg=ossl110");
println!("cargo:version=110");
Version::Openssl110
} else if expanded.contains("RUST_OPENSSL_102") {
println!("cargo:rustc-cfg=ossl102");
println!("cargo:version=102");
Version::Openssl102
} else if expanded.contains("RUST_OPENSSL_101") {
println!("cargo:rustc-cfg=ossl101");
println!("cargo:version=101");
Version::Openssl101
} else {
panic!("
This crate is only compatible with OpenSSL 1.0.1, 1.0.2, and 1.1.0, or LibreSSL,
but a different version of OpenSSL was found. The build is now aborting due to
this version mismatch.
");
}
}
/// Given a libdir for OpenSSL (where artifacts are located) as well as the name