forked from parallaxsecond/rust-tss-esapi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Jesper Brynolf <[email protected]>
- Loading branch information
1 parent
2dfc315
commit afaf9e7
Showing
2 changed files
with
256 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,114 +1,275 @@ | ||
// Copyright 2021 Contributors to the Parsec project. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#[cfg(feature = "generate-bindings")] | ||
use std::path::PathBuf; | ||
|
||
const MINIMUM_VERSION: &str = "2.4.6"; | ||
//#[cfg(feature = "generate-bindings")] | ||
|
||
fn main() { | ||
if std::env::var("DOCS_RS").is_ok() { | ||
// Nothing to be done for docs.rs builds. | ||
return; | ||
} | ||
|
||
#[cfg(feature = "generate-bindings")] | ||
{ | ||
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); | ||
let esys_path = out_path.join("tss_esapi_bindings.rs"); | ||
generate_from_system(esys_path); | ||
cfg_if::cfg_if! { | ||
if #[cfg(feature = "generate-bindings")] { | ||
let installation = tpm2_tss::Installation::probe(true); | ||
} else { | ||
let installation = tpm2_tss::Installation::probe(false); | ||
} | ||
} | ||
} | ||
|
||
#[cfg(not(feature = "generate-bindings"))] | ||
{ | ||
use std::str::FromStr; | ||
use target_lexicon::{Architecture, OperatingSystem, Triple}; | ||
pub mod target { | ||
use std::str::FromStr; | ||
use target_lexicon::{Architecture, OperatingSystem, Triple}; | ||
const TARGET_ENV_VAR_NAME: &str = "TARGET"; | ||
|
||
let target = Triple::from_str(&std::env::var("TARGET").unwrap()) | ||
.expect("Failed to parse target triple"); | ||
/// Ensures that the `TARGET` is valid for cross compilation. | ||
pub fn ensure_supported() { | ||
let target = Triple::from_str( | ||
&std::env::var(TARGET_ENV_VAR_NAME) | ||
.unwrap_or_else(|_| { | ||
panic!("Missing environment variable `{}`.", TARGET_ENV_VAR_NAME); | ||
})) | ||
.expect("Failed to parse target triple."); | ||
match (target.architecture, target.operating_system) { | ||
(Architecture::Arm(_), OperatingSystem::Linux) => {} | ||
(Architecture::Aarch64(_), OperatingSystem::Linux) => {} | ||
(Architecture::X86_64, OperatingSystem::Darwin) => {} | ||
(Architecture::X86_64, OperatingSystem::Linux) => {} | ||
(Architecture::Arm(_), OperatingSystem::Linux) | | ||
(Architecture::Aarch64(_), OperatingSystem::Linux) | | ||
(Architecture::X86_64, OperatingSystem::Darwin) | | ||
(Architecture::X86_64, OperatingSystem::Linux) | | ||
(Architecture::X86_64, OperatingSystem::Windows) => {} | ||
(arch, os) => { | ||
panic!("Compilation target (architecture, OS) tuple ({}, {}) is not part of the supported tuples. Please compile with the \"generate-bindings\" feature or add support for your platform :)", arch, os); | ||
panic!( | ||
"Compilation target (architecture, OS) tuple ({}, {}) is not part of the supported tuples. Please compile with the \"generate-bindings\" feature or add support for your platform.", | ||
arch, | ||
os); | ||
} | ||
} | ||
|
||
pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-sys") | ||
.expect("Failed to find tss2-sys library."); | ||
let tss2_esys = pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-esys") | ||
.expect("Failed to find tss2-esys library."); | ||
pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-tctildr") | ||
.expect("Failed to find tss2-tctildr library."); | ||
pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-mu") | ||
.expect("Failed to find tss2-mu library."); | ||
|
||
println!("cargo:version={}", tss2_esys.version); | ||
} | ||
} | ||
|
||
#[cfg(feature = "generate-bindings")] | ||
pub fn generate_from_system(esapi_out: PathBuf) { | ||
pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-sys") | ||
.expect("Failed to find tss2-sys library."); | ||
let tss2_esys = pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-esys") | ||
.expect("Failed to find tss2-esys"); | ||
let tss2_tctildr = pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-tctildr") | ||
.expect("Failed to find tss2-tctildr"); | ||
let tss2_mu = pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe("tss2-mu") | ||
.expect("Failed to find tss2-mu"); | ||
|
||
println!("cargo:version={}", tss2_esys.version); | ||
|
||
// These three pkg-config files should contain only one include/lib path. | ||
let tss2_esys_include_path = tss2_esys.include_paths[0] | ||
.clone() | ||
.into_os_string() | ||
.into_string() | ||
.expect("Error converting OsString to String."); | ||
let tss2_tctildr_include_path = tss2_tctildr.include_paths[0] | ||
.clone() | ||
.into_os_string() | ||
.into_string() | ||
.expect("Error converting OsString to String."); | ||
let tss2_mu_include_path = tss2_mu.include_paths[0] | ||
.clone() | ||
.into_os_string() | ||
.into_string() | ||
.expect("Error converting OsString to String."); | ||
|
||
bindgen::Builder::default() | ||
.size_t_is_usize(false) | ||
.clang_arg(format!("-I{}/tss2/", tss2_esys_include_path)) | ||
.clang_arg(format!("-I{}/tss2/", tss2_tctildr_include_path)) | ||
.clang_arg(format!("-I{}/tss2/", tss2_mu_include_path)) | ||
.header(format!("{}/tss2/tss2_esys.h", tss2_esys_include_path)) | ||
.header(format!("{}/tss2/tss2_tctildr.h", tss2_tctildr_include_path)) | ||
.header(format!("{}/tss2/tss2_mu.h", tss2_mu_include_path)) | ||
// See this issue: https://github.com/parallaxsecond/rust-cryptoki/issues/12 | ||
.blocklist_type("max_align_t") | ||
.generate_comments(false) | ||
.derive_default(true) | ||
.generate() | ||
.expect("Unable to generate bindings to TSS2 ESYS APIs.") | ||
.write_to_file(esapi_out) | ||
.expect("Couldn't write ESYS bindings!"); | ||
pub mod tpm2_tss { | ||
use std::path::{PathBuf, Path}; | ||
use semver::{Version, VersionReq}; | ||
const MINIMUM_VERSION: &str = "2.4.6"; | ||
const PATH_ENV_VAR_NAME: &str = "TPM2_TSS_PATH"; | ||
|
||
/// The installed tpm2-tss libraries that are of | ||
/// interest. | ||
pub struct Installation { | ||
tss2_sys: Library, | ||
tss2_esys: Library, | ||
tss2_tctildr: Library, | ||
tss2_mu: Library, | ||
tss2_tcti_tbs: Option<Library>, | ||
} | ||
|
||
impl Installation { | ||
/// Probes the system for an installation. | ||
pub fn probe(with_headers: bool) -> Self { | ||
let install_path = Installation::installation_path_from_env_var(); | ||
Installation { | ||
tss2_sys: Library::probe_required("tss2-sys", install_path.as_ref()), | ||
tss2_esys: Library::probe_required("tss2-esys", install_path.as_ref()), | ||
tss2_tctildr: Library::probe_required("tss2-tctildr", install_path.as_ref()), | ||
tss2_mu: Library::probe_required("tss2-mu", install_path.as_ref()), | ||
tss2_tcti_tbs: Library::probe_optional("tss2-tcti-tbs", install_path.as_ref()), | ||
} | ||
} | ||
|
||
/// Generates bindings for the Installation. | ||
pub fn generate_bindings(&self, esapi_out: &Path) { | ||
self.bindgen_builder() | ||
.generate() | ||
.expect("Unable to generate bindings to TSS2 ESYS APIs.") | ||
.write_to_file(esapi_out) | ||
.expect("Couldn't write ESYS bindings!"); | ||
} | ||
|
||
/// The bindgen builder to use. | ||
fn bindgen_builder(&self) -> bindgen::Builder { | ||
let mut builder = bindgen::Builder::default() | ||
.size_t_is_usize(false) | ||
.clang_arg(self.tss2_esys.include_dir_arg()) | ||
.clang_arg(self.tss2_tctildr.include_dir_arg()) | ||
.clang_arg(self.tss2_mu.include_dir_arg()) | ||
.rustfmt_bindings(true) | ||
.header(self.tss2_esys.header_file_arg()) | ||
.header(self.tss2_tctildr.header_file_arg()) | ||
.header(self.tss2_mu.header_file_arg()) | ||
//See this issue: https://github.com/parallaxsecond/rust-cryptoki/issues/12 | ||
.blocklist_type("max_align_t") | ||
.generate_comments(false) | ||
.derive_default(true); | ||
if let Some(tss2_tcti_tbs) = &self.tss2_tcti_tbs { | ||
builder = builder | ||
.clang_arg(tss2_tcti_tbs.include_dir_arg()) | ||
.header(tss2_tcti_tbs.header_file_arg()); | ||
} | ||
builder | ||
} | ||
|
||
/// Retrieves the installation path from the environment variable and validates it. | ||
fn installation_path_from_env_var() -> Option<(PathBuf, String)> { | ||
std::env::var(PATH_ENV_VAR_NAME).map_or_else(|e| { | ||
match e { | ||
std::env::VarError::NotUnicode(invalid_value) => { | ||
panic!("Invalid `{}` env var: `{:?}`.", PATH_ENV_VAR_NAME, invalid_value); | ||
}, | ||
std::env::VarError::NotPresent => None, | ||
} | ||
}, |var| { | ||
Some(Installation::ensure_valid_installation_path(var)) | ||
}) | ||
} | ||
|
||
/// Ensures that the installation path is valid. | ||
/// | ||
/// # Details | ||
/// In order to be considered valid the following | ||
/// requirements needs to be full filled: | ||
/// 1. The directory must exist. | ||
/// 2. Sub directories `include` and `lib` must exist. | ||
/// 3. A `VERSION` file must be present in the directory and it needs to be | ||
/// be specifying a version that is greater then the minimum supported version. | ||
/// | ||
/// # Arguments | ||
/// env_var - The value of the environment variable that contains the installation path. | ||
/// | ||
/// # Returns | ||
/// A tuple containing the validated installation path and the version associated with it. | ||
fn ensure_valid_installation_path(env_var: String) -> (PathBuf, String) { | ||
let install_path = PathBuf::from(env_var); | ||
if !install_path.is_dir() { | ||
panic!("`{}` specifies a path `{}`, that does not exist", PATH_ENV_VAR_NAME, install_path.to_string_lossy()); | ||
} | ||
if !install_path.join("include").is_dir() { | ||
panic!("`{}` specifies a path `{}`, that does not contain an `include` directory", PATH_ENV_VAR_NAME, install_path.to_string_lossy()); | ||
} | ||
if !install_path.join("lib").is_dir() { | ||
panic!("`{}` specifies a path `{}`, that does not contain an `lib` directory", PATH_ENV_VAR_NAME, install_path.to_string_lossy()); | ||
} | ||
let version_str = std::fs::read_to_string(install_path.join("VERSION")) | ||
.expect("Failed to read VERSION file."); | ||
let version = Version::parse(version_str.as_str()) | ||
.expect("Failed to parse the content of the VERSION file."); | ||
let min_version_req_str = format!(">={}",MINIMUM_VERSION); | ||
let min_version_req = VersionReq::parse(&min_version_req_str) | ||
.expect("Failed to parse minimum version requirement."); | ||
if !min_version_req.matches(&version) { | ||
panic!("Minimum supported version is {}, found installed version {}", MINIMUM_VERSION, version_str); | ||
} | ||
(install_path, version_str) | ||
} | ||
} | ||
|
||
|
||
/// Struct holding the information for a library. | ||
struct Library { | ||
header_file: PathBuf, | ||
version: String, | ||
name: String, | ||
} | ||
|
||
impl Library { | ||
/// Probes the different options for a required library. | ||
/// | ||
/// # Arguments | ||
/// lib_name - The name of the library. | ||
/// install_path - Optional path and version of installation. | ||
/// | ||
/// # Returns | ||
/// The detected installed library. | ||
/// # Panics | ||
/// - If the library is not found. | ||
pub fn probe_required(lib_name: &str, install_path: Option<&(PathBuf, String)>) -> Self { | ||
Self::probe_optional(lib_name, install_path) | ||
.unwrap_or_else(|| panic!("Failed to find {} library.", lib_name)) | ||
} | ||
|
||
/// Probes the different options for an optional library. | ||
/// | ||
/// # Arguments | ||
/// lib_name - The name of the library. | ||
/// install_path - Optional path and version of installation. | ||
/// | ||
/// # Returns | ||
/// The detected installed library or None if no library was found. | ||
pub fn probe_optional(lib_name: &str, install_path: Option<&(PathBuf, String)>) -> Option<Self> { | ||
install_path | ||
.map(|(path, version)| { | ||
Self::probe_install_path(lib_name, &path, &version) | ||
}) | ||
.or_else(|| Self::probe_pkg_config_optional(lib_name)) | ||
} | ||
|
||
/// The include dir `clang_arg` bindgen builder argument. | ||
/// | ||
/// # Panics | ||
/// - If the library specifies a header file does not have a parent directory. | ||
/// - If the library specifies a header file path that contain invalid utf-8 characters. | ||
pub fn include_dir_arg(&self) -> String { | ||
self.header_file | ||
.parent() | ||
.unwrap_or_else(|| panic!("Inconsistent `{}` header file path.", self.name)) | ||
.as_os_str() | ||
.to_str() | ||
.map_or_else(|| { | ||
panic!("Error converting OsString to &str when processing `{}` include dir.", self.name); | ||
}, |v| format!("-I{}", v)) | ||
} | ||
|
||
/// The header file path to a `header` bindgen argument. | ||
/// | ||
/// # Panics | ||
/// - If the library specifies a header file path that contain invalid utf-8 characters. | ||
pub fn header_file_arg(&self) -> &str { | ||
self.header_file | ||
.as_os_str() | ||
.to_str() | ||
.unwrap_or_else(|| panic!("Error converting OsString to &str when processing `{}` include dir.", self.name)) | ||
} | ||
|
||
/// Probe the system for an optional library using pkg-config. | ||
fn probe_pkg_config_optional(lib_name: &str) -> Option<Self> { | ||
pkg_config::Config::new() | ||
.atleast_version(MINIMUM_VERSION) | ||
.probe(lib_name) | ||
.ok() | ||
.map(|pkg_config| { | ||
let header_file = pkg_config.include_paths[0] | ||
.join("tss2") | ||
.join(lib_name.replace("-", "_") + ".h"); | ||
if !header_file.is_file() { | ||
panic!("Header file `{}` does not exist.", header_file.to_string_lossy()); | ||
} | ||
Self {header_file, version: pkg_config.version, name: lib_name.to_string()} | ||
}) | ||
} | ||
|
||
/// Probe the install path for a library. | ||
/// | ||
/// # Arguments | ||
/// `lib_name` - The name of the library to probe for. | ||
/// `path` - The path to probe for the library. | ||
/// `version` - The version of the library. | ||
/// | ||
/// # Returns | ||
/// A `Library` object containing the information retrieved. | ||
/// | ||
/// # Panics | ||
/// - If no `.lib` file for the library was found. | ||
/// - If no `.h` file for the library was found. | ||
fn probe_install_path(lib_name: &str, path: &Path, version: &str) -> Self { | ||
let file_name = PathBuf::from(lib_name.replace("-", "_")); | ||
let lib_file = path.join("lib").join(file_name.with_extension("lib")); | ||
if !lib_file.is_file() { | ||
panic!("Lib file `{}`, does not exist.", lib_file.to_string_lossy()); | ||
} | ||
let header_file = path.join("include").join(file_name.with_extension("h")); | ||
if !header_file.is_file() { | ||
panic!("Header file `{}`, does not exist.", header_file.to_string_lossy()); | ||
} | ||
Self {header_file, version: version.to_string(), name: lib_name.to_string()} | ||
} | ||
} | ||
} |