backbeat_c_sdk 0.5.0-beta

C SDK for Backbeat. Trivially integratable into all good games.
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;

fn git_output(crate_dir: &Path, args: &[&str]) -> Option<String> {
	let output = Command::new("git")
		.args(args)
		.current_dir(crate_dir)
		.output()
		.ok()?;
	if !output.status.success() {
		return None;
	}

	String::from_utf8(output.stdout)
		.ok()
		.map(|value| value.trim().to_owned())
		.filter(|value| !value.is_empty())
}

fn track_git_revision(crate_dir: &Path) {
	for path in [
		git_output(crate_dir, &["rev-parse", "--git-path", "HEAD"]),
		git_output(crate_dir, &["rev-parse", "--git-path", "packed-refs"]),
		git_output(crate_dir, &["symbolic-ref", "--quiet", "HEAD"])
			.and_then(|reference| git_output(crate_dir, &["rev-parse", "--git-path", &reference])),
	]
	.into_iter()
	.flatten()
	{
		println!("cargo:rerun-if-changed={path}");
	}
}

fn escape_c_string(value: &str) -> String {
	value.replace('\\', "\\\\").replace('"', "\\\"")
}

fn main() {
	let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
	let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
	let header_path = out_dir.join("backbeat.h");

	println!("cargo:rerun-if-changed=src/");
	println!("cargo:rerun-if-changed=cbindgen.toml");
	println!("cargo:rerun-if-env-changed=BKB_LIBCOMMIT_HASH");
	println!("cargo:rerun-if-env-changed=BKB_HEADER_PATH");
	track_git_revision(&crate_dir);

	let mut config = cbindgen::Config::from_file(crate_dir.join("cbindgen.toml"))
		.expect("failed to load cbindgen.toml");
	let commit_hash = env::var("BKB_LIBCOMMIT_HASH")
		.ok()
		.or_else(|| git_output(&crate_dir, &["rev-parse", "HEAD"]))
		.unwrap_or_else(|| "unknown".to_owned());
	assert!(
		!commit_hash
			.bytes()
			.any(|byte| matches!(byte, 0 | b'\n' | b'\r')),
		"BKB_LIBCOMMIT_HASH contains a forbidden control character"
	);
	let commit_hash_for_c = escape_c_string(&commit_hash);
	config.after_includes = Some(format!(
		"/* Git commit used to build the library. */\n#define BKB_LIBCOMMIT_HASH \"{commit_hash_for_c}\""
	));
	println!("cargo:rustc-env=BKB_LIBCOMMIT_HASH={commit_hash}");

	cbindgen::Builder::new()
		.with_crate(crate_dir)
		.with_config(config)
		.generate()
		.expect("cbindgen failed")
		.write_to_file(&header_path);

	if let Some(export_path) = env::var_os("BKB_HEADER_PATH").map(PathBuf::from) {
		let parent = export_path
			.parent()
			.expect("BKB_HEADER_PATH must have a parent directory");
		std::fs::create_dir_all(parent).expect("failed to create header output directory");
		std::fs::copy(&header_path, export_path).expect("failed to export generated header");
	}
}