Files
iDescriptor/build.rs
T

447 lines
15 KiB
Rust

use cmake::build;
use core::panic;
use std::fmt::Write;
use std::path::Path;
use std::process::Command;
use walkdir::WalkDir;
use std::env;
use std::path::PathBuf;
fn compile_bridge(qt_include_path: &str) {
println!("compile_bridge");
println!("cargo:rerun-if-changed=src/bridge.cpp");
println!("cargo:rerun-if-changed=src/include/bridge.h");
println!("cargo:rerun-if-changed=src/networkdeviceprovider.h");
println!("cargo:rerun-if-changed=src/core/services/avahi/avahi_service.h");
println!("cargo:rerun-if-changed=src/core/services/avahi/avahi_service.cpp");
let dir_var = env::var("OUT_DIR").unwrap();
// let out_dir = Path::new(&dir_var);
// let moc = find_moc(&env::var("DEP_QT_LIBRARY_PATH").unwrap());
let mut cc_build = cc::Build::new();
cc_build
.cpp(true)
.file("src/bridge.cpp")
// .file("src/core/services/avahi/avahi_service.cpp")
// .include("src")
.include(qt_include_path)
.include(format!("{}/QtCore", qt_include_path))
.include(format!("{}/QtGui", qt_include_path))
.flag_if_supported("-std=c++17")
.flag_if_supported("-Wno-deprecated-declarations");
// for f in env::var("DEP_QT_COMPILE_FLAGS")
// .unwrap()
// .split_terminator(';')
// {
// cc_build.flag(f);
// }
// for header in [
// "src/networkdeviceprovider.h",
// "src/core/services/avahi/avahi_service.h",
// ] {
// if Path::new(header).exists() {
// let moc_cpp = run_moc(&moc, header, out_dir);
// cc_build.file(moc_cpp);
// }
// }
if let Ok(ffmpeg_dir) = std::env::var("FFMPEG_DIR") {
cc_build.include(format!("{}/include", ffmpeg_dir));
println!("cargo:rustc-link-search={}/lib", ffmpeg_dir);
println!("cargo:rustc-link-search={}/lib64", ffmpeg_dir);
} else {
// Fallback to pkg-config on Linux
if let Ok(lib) = pkg_config::Config::new()
.atleast_version("58")
.probe("libavformat")
{
for path in lib.include_paths {
cc_build.include(path);
}
}
let _ = pkg_config::Config::new().probe("libavcodec");
let _ = pkg_config::Config::new().probe("libavutil");
let _ = pkg_config::Config::new().probe("libswscale");
#[cfg(target_os = "linux")]
{
let _ = pkg_config::Config::new().probe("avahi-client");
}
}
cc_build.compile("bridge");
for lib in ["avformat", "avcodec", "avutil", "swscale"] {
println!("cargo:rustc-link-lib={}", lib);
}
}
fn compile_uxplay() {
let uxplay = cmake::Config::new("lib/uxplay")
.build_target("uxplay")
//no need for x11
.define("NO_X11_DEPS", "ON")
.build();
let build = uxplay.display();
println!("cargo:rustc-link-search=native={build}/build");
println!("cargo:rustc-link-search=native={build}/build/lib");
println!("cargo:rustc-link-search=native={build}/build/renderers");
println!("cargo:rustc-link-search=native={build}/build/lib/llhttp");
println!("cargo:rustc-link-search=native={build}/build/lib/playfair");
println!("cargo:rustc-link-lib=static=uxplay");
println!("cargo:rustc-link-lib=static=renderers");
println!("cargo:rustc-link-lib=static=airplay");
println!("cargo:rustc-link-lib=static=llhttp");
println!("cargo:rustc-link-lib=static=playfair");
// TODO: don't depend on Qt6Core
pkg_config::Config::new().probe("Qt6Core").unwrap();
// gst stuff
for pkg in &[
"gstreamer-1.0",
"gstreamer-app-1.0",
"gstreamer-video-1.0",
"gstreamer-audio-1.0",
] {
pkg_config::Config::new().probe(pkg).unwrap();
}
pkg_config::Config::new().probe("openssl").unwrap();
// Linux uses avahi
#[cfg(target_os = "linux")]
{
pkg_config::Config::new()
.probe("avahi-compat-libdns_sd")
.unwrap();
}
// FIXME: macOS and Windows
// println!("cargo:rustc-link-lib=dns_sd");
// glib
pkg_config::Config::new().probe("glib-2.0").unwrap();
pkg_config::Config::new().probe("gobject-2.0").unwrap();
pkg_config::Config::new().probe("libplist-2.0").unwrap();
}
fn add_pkg_includes_cc(build: &mut cc::Build, pkg: &str) {
if let Ok(lib) = pkg_config::Config::new().cargo_metadata(false).probe(pkg) {
for p in lib.include_paths {
build.include(p);
}
}
}
fn dirname_and_filename(path: &str) -> (&str, &str) {
if let Some(pos) = path.rfind(['/']) {
(&path[..pos], &path[pos + 1..])
} else {
("", "")
}
}
fn compile_ccp_codebase() {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let out_dir = std::env::var("OUT_DIR").unwrap();
let moc_dir = format!("{}/{}", out_dir, "moc");
let cpp_out_path = Path::new(&moc_dir);
std::fs::create_dir_all(cpp_out_path).unwrap();
match target_os.as_str() {
"linux" => {
let paths = [
"src/core/services/avahi/avahi_service.cpp",
"src/core/services/avahi/avahi_service.h",
"src/networkdeviceprovider.h",
];
let mut build = cc::Build::new();
build.cpp(true);
// .files([
// moc_out_file_clone,
// "src/core/services/avahi/avahi_service.cpp".to_string(),
// ]);
for path in paths {
let (dir_name, file_name) = dirname_and_filename(path);
// no need moc for cpp files
if file_name.to_ascii_lowercase().ends_with(".cpp") {
build.file(path);
continue;
};
let file_out_dir = cpp_out_path.join(dir_name);
std::fs::create_dir_all(&file_out_dir).unwrap();
let moc_out_file =
file_out_dir.to_str().unwrap().to_owned() + &format!("/moc_{}.cpp", &file_name);
Command::new("/usr/lib/qt6/moc")
.args([path, "-o", &moc_out_file])
.status()
.unwrap();
build.file(moc_out_file);
build.file(path);
}
add_pkg_includes_cc(&mut build, "Qt6Core");
add_pkg_includes_cc(&mut build, "Qt6Gui");
add_pkg_includes_cc(&mut build, "Qt6Qml");
add_pkg_includes_cc(&mut build, "Qt6Quick");
add_pkg_includes_cc(&mut build, "Qt6QuickControls2");
build.compile("cpp_codebase");
pkg_config::Config::new()
.cargo_metadata(true)
.probe("avahi-client")
.unwrap();
}
"windows" => {
let paths = [
"src/core/services/dnssd/dnssd_service.cpp",
"src/core/services/dnssd/dnssd_service.h",
"src/networkdeviceprovider.h",
];
let mut build = cc::Build::new();
build.cpp(true);
for path in paths {
let (dir_name, file_name) = dirname_and_filename(path);
// no need moc for cpp files
if file_name.to_ascii_lowercase().ends_with(".cpp") {
build.file(path);
continue;
};
let file_out_dir = cpp_out_path.join(dir_name);
std::fs::create_dir_all(&file_out_dir).unwrap();
let moc_out_file =
file_out_dir.to_str().unwrap().to_owned() + &format!("/moc_{}.cpp", &file_name);
let status = Command::new("C:\\msys64\\mingw64\\share\\qt6\\bin\\moc.exe")
.args([path, "-o", &moc_out_file])
.status()
.unwrap();
assert!(status.success());
build.file(moc_out_file);
build.file(path);
}
add_pkg_includes_cc(&mut build, "Qt6Core");
add_pkg_includes_cc(&mut build, "Qt6Gui");
add_pkg_includes_cc(&mut build, "Qt6Qml");
add_pkg_includes_cc(&mut build, "Qt6Quick");
add_pkg_includes_cc(&mut build, "Qt6QuickControls2");
let bonjour_sdk = if let Ok(sdk_path) = env::var("BONJOUR_SDK") {
PathBuf::from(sdk_path)
} else {
// Try common Windows installation paths
let possible_paths = vec![
"C:/Program Files/Bonjour SDK",
// "C:/Program Files (x86)/Bonjour SDK",
// "C:/Windows/System32",
];
possible_paths
.iter()
.find(|p| PathBuf::from(p).join("Include/dns_sd.h").exists())
.map(|p| PathBuf::from(p))
.expect("Bonjour SDK not found. Please set BONJOUR_SDK environment variable.")
};
build.include(bonjour_sdk.join("Include"));
// println!("cargo:rustc-link-search=native={}", bonjour_sdk.join("Lib/x64").display()); // add this
// println!("cargo:rustc-link-lib=dnssd");
println!("cargo:rustc-link-arg={}", bonjour_sdk.join("Lib/x64/dnssd.lib").display());
build.compile("cpp_codebase");
}
os_ => panic!("building not supported on this platform"),
};
}
fn main() {
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/live_reload.cpp");
let qt_include_path = env::var("DEP_QT_INCLUDE_PATH").unwrap();
let qt_library_path = env::var("DEP_QT_LIBRARY_PATH").unwrap();
let qt_version = env::var("DEP_QT_VERSION").unwrap();
println!("Qt lib path: {}", qt_library_path);
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let mut config = cpp_build::Config::new();
for f in env::var("DEP_QT_COMPILE_FLAGS")
.unwrap()
.split_terminator(';')
{
config.flag(f);
}
let mut add_pkg_includes = |pkg: &str| {
let lib = pkg_config::Config::new()
.cargo_metadata(false)
.probe(pkg)
.unwrap_or_else(|e| panic!("pkg-config probe failed for {pkg}: {e}"));
for p in lib.include_paths {
config.include(p);
}
};
add_pkg_includes("gstreamer-1.0");
add_pkg_includes("gstreamer-app-1.0");
add_pkg_includes("gstreamer-video-1.0");
add_pkg_includes("gstreamer-audio-1.0");
add_pkg_includes("glib-2.0");
add_pkg_includes("gobject-2.0");
let mut public_include = |name| {
if cfg!(target_os = "macos") {
config.include(format!("{}/{}.framework/Headers/", qt_library_path, name));
}
config.include(format!("{}/{}", qt_include_path, name));
};
public_include("QtCore");
public_include("QtGui");
public_include("QtQuick");
public_include("QtQml");
public_include("QtQuickControls2");
let mut private_include = |name| {
if cfg!(target_os = "macos") {
config.include(format!(
"{}/{}.framework/Headers/{}",
qt_library_path, name, qt_version
));
config.include(format!(
"{}/{}.framework/Headers/{}/{}",
qt_library_path, name, qt_version, name
));
}
config
.include(format!("{}/{}/{}", qt_include_path, name, qt_version))
.include(format!(
"{}/{}/{}/{}",
qt_include_path, name, qt_version, name
));
};
private_include("QtCore");
private_include("QtGui");
private_include("QtQuick");
private_include("QtQml");
if let Ok(time) = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)
{
println!(
"cargo:rustc-env=BUILD_TIME={}",
(time.as_secs() - 1642516578) / 600
);
}
compile_bridge(&qt_include_path);
compile_uxplay();
compile_ccp_codebase();
// workaround for windows for now, we need to handle this in compile_bridge
if target_os == "windows" {
// Look for Bonjour SDK
let bonjour_sdk = if let Ok(sdk_path) = env::var("BONJOUR_SDK") {
PathBuf::from(sdk_path)
} else {
let possible_paths = vec![
"C:/Program Files/Bonjour SDK",
// "C:/Program Files (x86)/Bonjour SDK",
];
possible_paths
.iter()
.find(|p| PathBuf::from(p).join("Include/dns_sd.h").exists())
.map(|p| PathBuf::from(p))
.expect("Bonjour SDK not found. Please set BONJOUR_SDK environment variable.")
};
println!("Found Bonjour SDK at: {}", bonjour_sdk.display());
let include_path = bonjour_sdk.join("Include");
config.include(&include_path);
let out_dir = env::var("OUT_DIR").unwrap();
let moc_dir = format!("{}/moc", out_dir);
std::fs::create_dir_all(&moc_dir).unwrap();
let headers_to_moc = [
"src/core/services/dnssd/dnssd_service.h",
"src/networkdeviceprovider.h",
];
for header in &headers_to_moc {
let file_name = Path::new(header).file_name().unwrap().to_str().unwrap();
let moc_out = format!("{}/moc_{}.cpp", moc_dir, file_name);
let status = Command::new("C:\\msys64\\mingw64\\share\\qt6\\bin\\moc.exe")
.args([*header, "-o", &moc_out])
.status()
.unwrap();
assert!(status.success(), "MOC failed for {}", header);
config.file(&moc_out);
}
config.file("src/core/services/dnssd/dnssd_service.cpp");
config.file("src/bridge.cpp");
config.include(format!("{}/QtGui", qt_include_path));
if let Ok(ffmpeg_dir) = std::env::var("FFMPEG_DIR") {
config.include(format!("{}/include", ffmpeg_dir));
println!("cargo:rustc-link-search={}/lib", ffmpeg_dir);
println!("cargo:rustc-link-search={}/lib64", ffmpeg_dir);
println!("cargo:rustc-link-arg={}/lib/avformat.lib", ffmpeg_dir);
println!("cargo:rustc-link-arg={}/lib/avcodec.lib", ffmpeg_dir);
println!("cargo:rustc-link-arg={}/lib/avutil.lib", ffmpeg_dir);
println!("cargo:rustc-link-arg={}/lib/swscale.lib", ffmpeg_dir);
} else {
if let Ok(lib) = pkg_config::Config::new()
.cargo_metadata(false)
.atleast_version("58")
.probe("libavformat")
{
for path in lib.include_paths {
config.include(path);
}
for path in lib.link_paths {
println!("cargo:rustc-link-search=native={}", path.display());
}
}
for lib in ["avformat", "avcodec", "avutil", "swscale"] {
println!("cargo:rustc-link-arg=-l{}", lib);
}
}
println!("cargo:rustc-link-arg={}", bonjour_sdk.join("Lib/x64/dnssd.lib").display());
println!("cargo:rustc-env=BONJOUR_INCLUDE_PATH={}", include_path.display());
}
config.include(&qt_include_path).build("src/main.rs");
}