diff --git a/tss-esapi-sys/Cargo.toml b/tss-esapi-sys/Cargo.toml index 9e5d22e8..3b7b0dcb 100644 --- a/tss-esapi-sys/Cargo.toml +++ b/tss-esapi-sys/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/crate/tss-esapi-sys" links = "tss2-esys" [build-dependencies] -bindgen = { version = "0.66.1", optional = true } +bindgen = { version = "0.69.1", optional = true } pkg-config = "0.3.18" target-lexicon = "0.12.0" cfg-if = "1.0.0" diff --git a/tss-esapi-sys/build.rs b/tss-esapi-sys/build.rs index aa183b85..ffb0c00b 100644 --- a/tss-esapi-sys/build.rs +++ b/tss-esapi-sys/build.rs @@ -12,51 +12,54 @@ fn main() { cfg_if::cfg_if! { if #[cfg(feature = "generate-bindings")] { let installation = tpm2_tss::Installation::probe(true); + let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); + installation.generate_bindings(&out_dir.join("tss_esapi_bindings.rs")); } else { - let installation = tpm2_tss::Installation::probe(false); + target::ensure_supported(); + let _ = tpm2_tss::Installation::probe(false); } } } pub mod target { use std::str::FromStr; - use target_lexicon::{Architecture, OperatingSystem, Triple}; + use target_lexicon::{Architecture, OperatingSystem, Triple}; const TARGET_ENV_VAR_NAME: &str = "TARGET"; /// 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."); + 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::X86_64, OperatingSystem::Windows) => {} + (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); + "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 + ); } } } } pub mod tpm2_tss { - use std::path::{PathBuf, Path}; use semver::{Version, VersionReq}; + use std::path::{Path, PathBuf}; 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_sys: Library, tss2_esys: Library, tss2_tctildr: Library, tss2_mu: Library, @@ -65,65 +68,109 @@ pub mod tpm2_tss { impl Installation { /// Probes the system for an installation. - pub fn probe(with_headers: bool) -> Self { + pub fn probe(with_header_files: 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()), + _tss2_sys: Library::probe_required( + "tss2-sys", + install_path.as_ref(), + with_header_files, + false, + ), + tss2_esys: Library::probe_required( + "tss2-esys", + install_path.as_ref(), + with_header_files, + true, + ), + tss2_tctildr: Library::probe_required( + "tss2-tctildr", + install_path.as_ref(), + with_header_files, + false, + ), + tss2_mu: Library::probe_required( + "tss2-mu", + install_path.as_ref(), + with_header_files, + false, + ), + tss2_tcti_tbs: Library::probe_optional( + "tss2-tcti-tbs", + install_path.as_ref(), + with_header_files, + ), } } - /// 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!"); - } + cfg_if::cfg_if! { + if #[cfg(feature = "generate-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()); + /// 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()) + .formatter(bindgen::Formatter::Rustfmt) + .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 + .generate_comments(false) + .blocklist_type("max_align_t") + // Needed for windows + .blocklist_type("IMAGE_TLS_DIRECTORY") + .blocklist_type("PIMAGE_TLS_DIRECTORY") + .blocklist_type("IMAGE_TLS_DIRECTORY64") + .blocklist_type("PIMAGE_TLS_DIRECTORY64") + .blocklist_type("_IMAGE_TLS_DIRECTORY64") + .blocklist_type("MONITORINFOEX") + .blocklist_type("MONITORINFOEXA") + .blocklist_type("MONITORINFOEXW") + .blocklist_type("tagMONITORINFOEXA") + .blocklist_type("tagMONITORINFOEXW") + .blocklist_type("LPMONITORINFOEX") + .blocklist_type("LPMONITORINFOEXA") + .blocklist_type("LPMONITORINFOEXW") + .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 + } } - 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::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); - }, + panic!( + "Invalid `{}` env var: `{:?}`.", + PATH_ENV_VAR_NAME, invalid_value + ); + } std::env::VarError::NotPresent => None, - } - }, |var| { - Some(Installation::ensure_valid_installation_path(var)) - }) + }, + |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: @@ -131,145 +178,255 @@ pub mod tpm2_tss { /// 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()); + 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()); + 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()); + 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_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); + 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, + header_file: Option, version: String, name: String, } impl Library { - /// Probes the different options for a required 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. + /// `lib_name` - The name of the library. + /// `install_path` - Optional path and version of installation. + /// `with_header_files` - Flag indicating if header files are required. /// /// # Returns /// The detected installed library. - /// # Panics + /// # 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)) + pub fn probe_required( + lib_name: &str, + install_path: Option<&(PathBuf, String)>, + with_header_files: bool, + report_version: bool, + ) -> Self { + Self::probe_optional(lib_name, install_path, with_header_files).map_or_else( + || panic!("Failed to find {} library.", lib_name), + |lib| { + if report_version { + println!("cargo:version={}", lib.version); + } + lib + }, + ) } - /// Probes the different options for an optional library. - /// + /// Probes the different options for an optional library. + /// /// # Arguments - /// lib_name - The name of the library. - /// install_path - Optional path and version of installation. + /// `lib_name` - The name of the library. + /// `install_path` - Optional path and version of installation. + /// `with_header_files` - Flag indicating if header files are required. /// /// # 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 { + pub fn probe_optional( + lib_name: &str, + install_path: Option<&(PathBuf, String)>, + with_header_files: bool, + ) -> Option { install_path .map(|(path, version)| { - Self::probe_install_path(lib_name, &path, &version) + Self::probe_install_path(lib_name, path, version, with_header_files) }) - .or_else(|| Self::probe_pkg_config_optional(lib_name)) + .or_else(|| Self::probe_pkg_config_optional(lib_name, with_header_files)) } /// The include dir `clang_arg` bindgen builder argument. - /// + /// /// # Panics + /// - If the library was probe without requiring header files. /// - 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 + .as_ref() + .unwrap_or_else(|| panic!("No header file present for `{}`.", self.name)) .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)) + .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)) - } + self.header_file.as_ref().map_or_else( + || { + panic!("No header file present for `{}`.", self.name); + }, + |v| { + v.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 { + /// + /// # Args + /// `lib_name` - The name of the library. + /// `with_header_files` - Flag indicating if header files are required. + fn probe_pkg_config_optional(lib_name: &str, with_header_files: bool) -> Option { 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()); + if !with_header_files { + return Self { + header_file: None, + version: pkg_config.version, + name: lib_name.to_string(), + }; + } + let include_path = pkg_config.include_paths[0].join("tss2"); + let header_file = Self::header_file(lib_name, &include_path, with_header_files); + Self { + header_file, + version: pkg_config.version, + name: lib_name.to_string(), } - 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")); + fn probe_install_path( + lib_name: &str, + path: &Path, + version: &str, + with_header_files: bool, + ) -> Self { + let lib_path = path.join("lib"); + Self::ensure_lib_file_exist(lib_name, &lib_path); + + let include_path = path.join("include/tss2"); + let header_file = Self::header_file(lib_name, &include_path, with_header_files); + + Self { + header_file, + version: version.to_string(), + name: lib_name.to_string(), + } + } + + fn ensure_lib_file_exist(lib_name: &str, lib_path: &Path) { + let lib_file = lib_path.join(PathBuf::from(lib_name).with_extension("lib")); if !lib_file.is_file() { - panic!("Lib file `{}`, does not exist.", lib_file.to_string_lossy()); + panic!( + "Lib name: {}\n Lib file `{}`, does not exist.", + lib_name, + lib_file.to_string_lossy() + ); + } + } + + /// Creates a PathBuf object for the header file. + /// + /// # Args + /// `lib_name` - Name of the library. + /// `include_path` - The include path to the header file. + /// `with_header_files` - Flag indicating if header files are required. + /// + /// # Returns + /// An optional PathBuf object. + /// + /// # Panics + /// - If `with_header_files` but the combination of `file_name` and `include_path` + /// does not point to an existing file. + fn header_file( + lib_name: &str, + include_path: &Path, + with_header_files: bool, + ) -> Option { + if !with_header_files { + return None; } - 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()); + let file_name = PathBuf::from(lib_name.replace('-', "_")); + let header_file = include_path.join(file_name.with_extension("h")); + if with_header_files && !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()} + Some(header_file) } } }