pocketstation 1.0.0

Source-aware desktop audio Session SDK
fn main() {
    #[cfg(target_os = "macos")]
    {
        build_macos_capture_bridge();
        if std::env::var_os("CARGO_FEATURE_MACOS_ASP_DRIVER_ARTIFACT").is_some() {
            build_macos_asp_driver_artifact();
        }
    }
}

#[cfg(target_os = "macos")]
fn build_macos_capture_bridge() {
    use std::path::PathBuf;

    let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("manifest dir"));
    let asp = manifest.join("native/macos/asp");

    cc::Build::new()
        .file(asp.join("shm_reader.c"))
        .include(&asp)
        .compile("pks_asp_reader");
    cc::Build::new()
        .file(asp.join("source_discovery.m"))
        .flag("-fobjc-arc")
        .flag("-ObjC")
        .include(&asp)
        .compile("pks_tap_source");
    cc::Build::new()
        .file(asp.join("authorization.m"))
        .flag("-fobjc-arc")
        .include(&asp)
        .compile("pks_capture_authorization");

    for file in [
        "shm_reader.c",
        "bridge.h",
        "SharedRing.h",
        "source_discovery.m",
        "source_discovery.h",
        "authorization.m",
        "authorization.h",
    ] {
        println!("cargo:rerun-if-changed={}", asp.join(file).display());
    }

    println!("cargo:rustc-link-lib=framework=CoreAudio");
    println!("cargo:rustc-link-lib=framework=AVFoundation");
    println!("cargo:rustc-link-lib=framework=CoreMedia");
    println!("cargo:rustc-link-lib=framework=Foundation");
    println!("cargo:rustc-link-lib=framework=AppKit");
}

#[cfg(target_os = "macos")]
fn build_macos_asp_driver_artifact() {
    use std::path::PathBuf;

    let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR"));
    let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("manifest dir"));
    let asp = manifest.join("native/macos/asp");
    for file in ["Plugin.cpp", "Info.plist", "SharedRing.h"] {
        println!("cargo:rerun-if-changed={}", asp.join(file).display());
    }

    let dylib = out_dir.join("PocketStationLoopback.dylib");
    let contents = out_dir.join("PocketStationLoopback.driver/Contents");
    let macos_dir = contents.join("MacOS");
    let sdk = std::env::var("SDKROOT").unwrap_or_else(|_| {
        let output = std::process::Command::new("xcrun")
            .args(["--show-sdk-path"])
            .output()
            .expect("xcrun not found — install Xcode command-line tools");
        String::from_utf8(output.stdout)
            .expect("xcrun output is not UTF-8")
            .trim()
            .to_owned()
    });
    let coreaudio_headers = format!("{sdk}/System/Library/Frameworks/CoreAudio.framework/Headers");
    let status = std::process::Command::new("clang++")
        .args([
            "-std=c++17",
            "-O2",
            "-dynamiclib",
            "-isysroot",
            &sdk,
            "-I",
            &coreaudio_headers,
            "-framework",
            "CoreAudio",
            "-framework",
            "CoreFoundation",
            "-I",
            asp.to_str().expect("native path is UTF-8"),
            asp.join("Plugin.cpp")
                .to_str()
                .expect("plugin path is UTF-8"),
            "-o",
            dylib.to_str().expect("output path is UTF-8"),
        ])
        .status()
        .expect("clang++ not found — install Xcode command-line tools");
    assert!(status.success(), "Plugin.cpp compilation failed");

    std::fs::create_dir_all(&macos_dir).expect("create driver bundle");
    std::fs::copy(&dylib, macos_dir.join("PocketStationLoopback")).expect("copy driver binary");
    std::fs::copy(asp.join("Info.plist"), contents.join("Info.plist")).expect("copy driver plist");

    println!(
        "cargo:rustc-env=PKS_DRIVER_DYLIB={}",
        macos_dir.join("PocketStationLoopback").display()
    );
    println!(
        "cargo:rustc-env=PKS_DRIVER_PLIST={}",
        contents.join("Info.plist").display()
    );
}