const DLT_WRAPPER: &str = "dlt-wrapper";
const DLT_HEADER: &str = "dlt-wrapper.h";
const DLT_SRC: &str = "dlt-wrapper.c";
const DLT_INCLUDE_DIR: &str = "DLT_INCLUDE_DIR";
const DLT_USER_INCLUDE_DIR: &str = "DLT_USER_INCLUDE_DIR";
const DLT_LIB_DIR: &str = "DLT_LIB_DIR";
const DLT_LIB_NAME: &str = "DLT_LIB_NAME";
const COPYRIGHT_HEADER: &str = r"/*
* Copyright (c) 2025 The Contributors to Eclipse OpenSOVD (see CONTRIBUTORS)
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*/
";
fn env_non_empty(var: &str) -> Option<String> {
std::env::var(var)
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
}
fn add_include_dir(build: &mut cc::Build, include: &str) {
build.include(include).include(format!("{include}/dlt"));
}
fn add_bindgen_include(builder: bindgen::Builder, include: &str) -> bindgen::Builder {
builder
.clang_arg(format!("-I{include}"))
.clang_arg(format!("-I{include}/dlt"))
}
fn main() {
println!("cargo:rerun-if-env-changed={DLT_INCLUDE_DIR}");
println!("cargo:rerun-if-env-changed={DLT_USER_INCLUDE_DIR}");
println!("cargo:rerun-if-env-changed={DLT_LIB_DIR}");
println!("cargo:rerun-if-env-changed={DLT_LIB_NAME}");
let project_dir = std::env::var("CARGO_MANIFEST_DIR")
.expect("CARGO_MANIFEST_DIR environment variable not set");
let wrapper_dir = format!("{project_dir}/wrapper");
let mut build = cc::Build::new();
build.cpp(false).file(format!("{wrapper_dir}/{DLT_SRC}"));
if let Some(include) = env_non_empty(DLT_INCLUDE_DIR) {
add_include_dir(&mut build, &include);
}
if let Some(user_include) = env_non_empty(DLT_USER_INCLUDE_DIR) {
add_include_dir(&mut build, &user_include);
}
#[cfg(feature = "trace_load_ctrl")]
build.define("DLT_TRACE_LOAD_CTRL_ENABLE", None);
build.compile(DLT_WRAPPER);
if let Some(lib_path) = env_non_empty(DLT_LIB_DIR) {
println!("cargo:rustc-link-search=native={lib_path}");
}
let lib_name = env_non_empty(DLT_LIB_NAME).unwrap_or_else(|| "dlt".to_string());
println!("cargo:rustc-link-lib=dylib={lib_name}");
if let Ok(target_os) = std::env::var("CARGO_CFG_TARGET_OS")
&& (target_os == "linux" || target_os == "android")
{
println!("cargo:rustc-link-lib=dylib=dl");
}
println!("cargo:rerun-if-changed={wrapper_dir}/{DLT_HEADER}");
println!("cargo:rerun-if-changed={wrapper_dir}/{DLT_SRC}");
generate_bindings(&wrapper_dir);
}
fn generate_bindings(wrapper_dir: &str) {
let mut builder = bindgen::Builder::default()
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.header(format!("{wrapper_dir}/{DLT_HEADER}"));
if let Some(include) = env_non_empty(DLT_INCLUDE_DIR) {
builder = add_bindgen_include(builder, &include);
}
if let Some(user_include) = env_non_empty(DLT_USER_INCLUDE_DIR) {
builder = add_bindgen_include(builder, &user_include);
}
if cfg!(feature = "trace_load_ctrl") {
builder = builder.clang_arg("-DDLT_TRACE_LOAD_CTRL_ENABLE");
}
let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR is not set by Cargo");
let target_file = std::path::PathBuf::from(out_dir).join("dlt_bindings.rs");
builder
.allowlist_type("DltContext")
.allowlist_type("DltContextData")
.allowlist_type("DltLogLevelType")
.allowlist_type("DltTimestampType")
.allowlist_type("DltReturnValue")
.allowlist_type("DltTraceStatusType")
.allowlist_var("DLT_ID_SIZE")
.allowlist_var("DLT_LOG_.*")
.allowlist_var("DLT_RETURN_.*")
.allowlist_function("registerApplication")
.allowlist_function("unregisterApplicationFlushBufferedLogs")
.allowlist_function("dltFree")
.allowlist_function("registerContext")
.allowlist_function("unregisterContext")
.allowlist_function("createContext")
.allowlist_function("freeContext")
.allowlist_function("getContextId")
.allowlist_function("getContextLogLevel")
.allowlist_function("getContextTraceStatus")
.allowlist_function("logDlt")
.allowlist_function("logDltString")
.allowlist_function("logDltUint")
.allowlist_function("logDltInt")
.allowlist_function("createContextData")
.allowlist_function("freeContextData")
.allowlist_function("dltUserLogWriteStart")
.allowlist_function("dltUserLogWriteFinish")
.allowlist_function("setContextDataUserTimestamp")
.allowlist_function("dltUserLogWriteString")
.allowlist_function("dltUserLogWriteUint")
.allowlist_function("dltUserLogWriteInt")
.allowlist_function("dltUserLogWriteUint64")
.allowlist_function("dltUserLogWriteInt64")
.allowlist_function("dltUserLogWriteFloat32")
.allowlist_function("dltUserLogWriteFloat64")
.allowlist_function("dltUserLogWriteBool")
.allowlist_function("registerLogLevelChangedCallback")
.generate()
.unwrap_or_else(|err| panic!("Error generating bindings: {err}"))
.write_to_file(&target_file)
.unwrap_or_else(|err| panic!("Error writing bindings: {err}"));
prepend_copyright(
target_file
.to_str()
.expect("Invalid generated bindings path"),
)
.expect("Error prepending copyright header");
}
fn prepend_copyright(file_path: &str) -> std::io::Result<()> {
let content = std::fs::read_to_string(file_path)?;
let new_content = format!("{COPYRIGHT_HEADER}{content}");
std::fs::write(file_path, new_content)?;
Ok(())
}