| // This file is dual licensed under the terms of the Apache License, Version |
| // 2.0, and the BSD License. See the LICENSE file in the root of this repository |
| // for complete details. |
| |
| use std::env; |
| use std::path::Path; |
| use std::process::Command; |
| |
| fn main() { |
| let target = env::var("TARGET").unwrap(); |
| let openssl_static = env::var("OPENSSL_STATIC") |
| .map(|x| x == "1") |
| .unwrap_or(false); |
| if target.contains("apple") && openssl_static { |
| // On (older) OSX we need to link against the clang runtime, |
| // which is hidden in some non-default path. |
| // |
| // More details at https://github.com/alexcrichton/curl-rust/issues/279. |
| if let Some(path) = macos_link_search_path() { |
| println!("cargo:rustc-link-lib=clang_rt.osx"); |
| println!("cargo:rustc-link-search={path}"); |
| } |
| } |
| |
| let out_dir = env::var("OUT_DIR").unwrap(); |
| // FIXME: maybe pyo3-build-config should provide a way to do this? |
| let python = env::var("PYO3_PYTHON").unwrap_or_else(|_| "python3".to_string()); |
| println!("cargo:rerun-if-env-changed=PYO3_PYTHON"); |
| println!("cargo:rerun-if-changed=../../_cffi_src/"); |
| println!("cargo:rerun-if-changed=../../cryptography/__about__.py"); |
| let output = Command::new(&python) |
| .env("OUT_DIR", &out_dir) |
| .arg("../../_cffi_src/build_openssl.py") |
| .output() |
| .expect("failed to execute build_openssl.py"); |
| if !output.status.success() { |
| panic!( |
| "failed to run build_openssl.py, stdout: \n{}\nstderr: \n{}\n", |
| String::from_utf8(output.stdout).unwrap(), |
| String::from_utf8(output.stderr).unwrap() |
| ); |
| } |
| |
| let python_impl = run_python_script( |
| &python, |
| "import platform; print(platform.python_implementation(), end='')", |
| ) |
| .unwrap(); |
| println!("cargo:rustc-cfg=python_implementation=\"{python_impl}\""); |
| let python_includes = run_python_script( |
| &python, |
| "import os; \ |
| import setuptools.dist; \ |
| import setuptools.command.build_ext; \ |
| b = setuptools.command.build_ext.build_ext(setuptools.dist.Distribution()); \ |
| b.finalize_options(); \ |
| print(os.pathsep.join(b.include_dirs), end='')", |
| ) |
| .unwrap(); |
| let openssl_c = Path::new(&out_dir).join("_openssl.c"); |
| |
| let mut build = cc::Build::new(); |
| build |
| .file(openssl_c) |
| .includes(std::env::var_os("DEP_OPENSSL_INCLUDE")) |
| .flag_if_supported("-Wconversion") |
| .flag_if_supported("-Wno-error=sign-conversion") |
| .flag_if_supported("-Wno-unused-parameter"); |
| |
| // We use the `-fmacro-prefix-map` option to replace the output directory in macros with a dot. |
| // This is because we don't want a potentially random build path to end up in the binary because |
| // CFFI generated code uses the __FILE__ macro in its debug messages. |
| if let Some(out_dir_str) = Path::new(&out_dir).to_str() { |
| build.flag_if_supported(format!("-fmacro-prefix-map={out_dir_str}").as_str()); |
| } |
| |
| for python_include in env::split_paths(&python_includes) { |
| build.include(python_include); |
| } |
| |
| // Enable abi3 mode if we're not using PyPy. |
| if python_impl != "PyPy" { |
| // cp37 (Python 3.7 to help our grep when we some day drop 3.7 support) |
| build.define("Py_LIMITED_API", "0x030700f0"); |
| } |
| |
| if cfg!(windows) { |
| build.define("WIN32_LEAN_AND_MEAN", None); |
| } |
| |
| build.compile("_openssl.a"); |
| } |
| |
| /// Run a python script using the specified interpreter binary. |
| fn run_python_script(interpreter: impl AsRef<Path>, script: &str) -> Result<String, String> { |
| let interpreter = interpreter.as_ref(); |
| let out = Command::new(interpreter) |
| .env("PYTHONIOENCODING", "utf-8") |
| .arg("-c") |
| .arg(script) |
| .output(); |
| |
| match out { |
| Err(err) => Err(format!( |
| "failed to run the Python interpreter at {}: {}", |
| interpreter.display(), |
| err |
| )), |
| Ok(ok) if !ok.status.success() => Err(format!( |
| "Python script failed: {}", |
| String::from_utf8(ok.stderr).expect("failed to parse Python script stderr as utf-8") |
| )), |
| Ok(ok) => Ok( |
| String::from_utf8(ok.stdout).expect("failed to parse Python script stdout as utf-8") |
| ), |
| } |
| } |
| |
| fn macos_link_search_path() -> Option<String> { |
| let output = Command::new("clang") |
| .arg("--print-search-dirs") |
| .output() |
| .ok()?; |
| if !output.status.success() { |
| println!( |
| "failed to run 'clang --print-search-dirs', continuing without a link search path" |
| ); |
| return None; |
| } |
| |
| let stdout = String::from_utf8_lossy(&output.stdout); |
| for line in stdout.lines() { |
| if line.contains("libraries: =") { |
| let path = line.split('=').nth(1)?; |
| return Some(format!("{path}/lib/darwin")); |
| } |
| } |
| |
| println!("failed to determine link search path, continuing without it"); |
| None |
| } |