Skip to content

Commit

Permalink
WORK IN PROGRESS
Browse files Browse the repository at this point in the history
Signed-off-by: Jesper Brynolf <[email protected]>
  • Loading branch information
Superhepper committed Dec 9, 2023
1 parent 2dfc315 commit afaf9e7
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 93 deletions.
2 changes: 2 additions & 0 deletions tss-esapi-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ links = "tss2-esys"
bindgen = { version = "0.66.1", optional = true }
pkg-config = "0.3.18"
target-lexicon = "0.12.0"
cfg-if = "1.0.0"
semver = "1.0.7"

[features]
generate-bindings = ["bindgen"]
347 changes: 254 additions & 93 deletions tss-esapi-sys/build.rs
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()}
}
}
}

0 comments on commit afaf9e7

Please sign in to comment.