fn main() {
declare_cfg_aliases();
check_system_dependencies();
}
fn declare_cfg_aliases() {
println!("cargo:rustc-check-cfg=cfg(device_profile)");
println!("cargo:rustc-check-cfg=cfg(full_widgets)");
println!("cargo:rustc-check-cfg=cfg(stripped_widgets)");
println!("cargo:rustc-check-cfg=cfg(widgets_unstripped)");
println!("cargo:rustc-check-cfg=cfg(alloc_frugal)");
println!("cargo:rustc-check-cfg=cfg(embedded_surface)");
println!("cargo:rustc-check-cfg=cfg(declarative_view)");
println!("cargo:rustc-check-cfg=cfg(designer_tooling)");
println!("cargo:rustc-check-cfg=cfg(cjk_outline_face)");
let has_profile =
["desktop", "tablet", "mobile"].iter().any(|feature| feature_enabled(feature));
let is_mini = feature_enabled("mini");
let is_embedded = feature_enabled("embedded");
let is_stripped = is_mini || is_embedded;
let view_opted_out = feature_enabled("no-declarative-view");
if is_mini {
println!("cargo:rustc-cfg=alloc_frugal");
}
if is_embedded {
println!("cargo:rustc-cfg=embedded_surface");
}
if has_profile {
println!("cargo:rustc-cfg=device_profile");
}
if !is_stripped {
println!("cargo:rustc-cfg=widgets_unstripped");
}
if has_profile && !is_stripped {
println!("cargo:rustc-cfg=full_widgets");
}
if is_stripped {
println!("cargo:rustc-cfg=stripped_widgets");
}
if has_profile && !is_stripped && !view_opted_out {
println!("cargo:rustc-cfg=declarative_view");
}
let designer_opted_in = feature_enabled("designer");
if has_profile && !is_stripped && designer_opted_in {
println!("cargo:rustc-cfg=designer_tooling");
}
let cjk_outline = feature_enabled("fonts-cjk")
|| feature_enabled("fonts-cjk-shard-latin")
|| feature_enabled("fonts-cjk-shard-symbols")
|| feature_enabled("fonts-cjk-shard-kana")
|| feature_enabled("fonts-cjk-shard-fullwidth")
|| feature_enabled("fonts-cjk-shard-han");
if cjk_outline {
println!("cargo:rustc-cfg=cjk_outline_face");
}
const TARGET_TRIPLES: &[&str] = &[
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
"aarch64-apple-ios",
"aarch64-apple-ios-sim",
"aarch64-unknown-linux-ohos",
"armv7-unknown-linux-ohos",
"x86_64-unknown-linux-ohos",
"aarch64-linux-android",
"x86_64-linux-android",
"wasm32-unknown-unknown",
];
let mut values = String::new();
for (index, triple) in TARGET_TRIPLES.iter().enumerate() {
if index > 0 {
values.push_str(", ");
}
values.push('"');
values.push_str(triple);
values.push('"');
}
println!("cargo:rustc-check-cfg=cfg(target_triple, values({values}))");
println!("cargo:rustc-check-cfg=cfg(target_triple_captured)");
if let Ok(target) = std::env::var("TARGET") {
println!("cargo:rustc-cfg=target_triple=\"{target}\"");
if TARGET_TRIPLES.contains(&target.as_str()) {
println!("cargo:rustc-cfg=target_triple_captured");
}
}
for feature in [
"desktop",
"tablet",
"mobile",
"mini",
"embedded",
"portable",
"no-declarative-view",
"declarative-view",
"designer",
] {
println!(
"cargo:rerun-if-env-changed=CARGO_FEATURE_{}",
feature.to_uppercase().replace('-', "_")
);
}
}
fn feature_enabled(name: &str) -> bool {
let env_name = format!("CARGO_FEATURE_{}", name.to_uppercase().replace('-', "_"));
std::env::var(env_name).is_ok()
}
fn check_system_dependencies() {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
match target_os.as_str() {
"linux" => check_linux(),
"macos" => check_macos(),
"windows" => check_windows(),
_ => {}
}
check_xcomponent();
check_ohos_pasteboard();
}
fn check_linux() {
if feature_enabled("audio-output") {
check_pkg("alsa", "libasound2-dev", "sudo apt-get install -y libasound2-dev");
}
if feature_enabled("linux-gtk") {
check_pkg("gtk+-3.0", "libgtk-3-dev", "sudo apt-get install -y libgtk-3-dev");
}
if feature_enabled("video-codecs") {
check_ffmpeg();
}
}
fn check_macos() {
if feature_enabled("video-codecs") {
check_ffmpeg();
}
}
fn check_windows() {
if feature_enabled("video-codecs") {
warn("'video-codecs' feature requires FFmpeg. Install: vcpkg install ffmpeg");
}
}
fn check_xcomponent() {
if !feature_enabled("xcomponent") {
return;
}
println!("cargo:rerun-if-env-changed=OHOS_SDK_NATIVE");
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
if target_env != "ohos" {
warn(&format!(
"'xcomponent' is enabled for target_os='{target_os}' target_env='{target_env}', which is \
not an OpenHarmony target. The bridge compiles but is inert here: it will not link \
the SDK's libace_ndk, so no accessibility provider is attached and no key, touch or \
mouse callback can fire. Build for a '*-unknown-linux-ohos' target to use it."
));
return;
}
println!("cargo:rustc-link-lib=ace_ndk.z");
let _ = ohos_sysroot_lib_dir("xcomponent");
}
fn ohos_sysroot_lib_dir(consumer: &str) -> Option<String> {
use std::sync::atomic::{AtomicBool, Ordering};
static EMITTED: AtomicBool = AtomicBool::new(false);
let native = match std::env::var("OHOS_SDK_NATIVE") {
Ok(native) => native,
Err(_) => {
warn(&format!(
"'{consumer}' is enabled for an OpenHarmony target but OHOS_SDK_NATIVE is not set."
));
return None;
}
};
let target = std::env::var("TARGET").unwrap_or_default();
let arch_dir = match target.split('-').next().unwrap_or_default() {
"aarch64" => "aarch64-linux-ohos",
"armv7" | "arm" => "arm-linux-ohos",
"x86_64" => "x86_64-linux-ohos",
other => {
warn(&format!(
"'{consumer}' is enabled for target '{target}', whose OHOS library directory \
this build script does not know; expected one of aarch64/armv7/x86_64. \
Link will fail unless the SDK's lib directory is passed with -L."
));
let _ = other;
return None;
}
};
let lib_dir = format!("{native}/sysroot/usr/lib/{arch_dir}");
if !std::path::Path::new(&lib_dir).is_dir() {
warn(&format!(
"'{consumer}': expected the OpenHarmony libraries at '{lib_dir}', which does not \
exist. Check that OHOS_SDK_NATIVE points at the SDK's 'native' directory."
));
return None;
}
if !EMITTED.swap(true, Ordering::SeqCst) {
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:rustc-link-arg=-Wl,-rpath-link,{lib_dir}");
}
Some(lib_dir)
}
fn check_ohos_pasteboard() {
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
if target_env != "ohos" {
return;
}
println!("cargo:rerun-if-env-changed=OHOS_SDK_NATIVE");
if ohos_sysroot_lib_dir("pasteboard").is_none() {
return;
}
println!("cargo:rustc-link-lib=pasteboard");
println!("cargo:rustc-link-lib=udmf");
}
fn check_pkg(name: &str, dev_pkg: &str, install_cmd: &str) {
let ok = std::process::Command::new("pkg-config")
.args(["--exists", name])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if ok {
} else {
warn(&format!("Missing system library: {name} ({dev_pkg})"));
warn(&format!(" Install: {install_cmd}"));
}
}
fn check_ffmpeg() {
let mut libs = vec![
"libavcodec",
"libavformat",
"libavutil",
"libavfilter",
"libavdevice",
"libswscale",
"libswresample",
"libpostproc",
];
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
libs.retain(|lib| *lib != "libpostproc");
}
let mut all_ok = true;
for lib in &libs {
let ok = std::process::Command::new("pkg-config")
.args(["--exists", lib])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if ok {
} else {
println!("cargo:warning= ❌ Missing: {lib}");
all_ok = false;
}
}
if !all_ok {
warn("Missing FFmpeg development libraries. Install all:");
warn(" sudo apt-get install -y libavcodec-dev libavformat-dev \\");
warn(" libavutil-dev libavfilter-dev libavdevice-dev \\");
warn(" libswscale-dev libswresample-dev libpostproc-dev");
}
}
fn warn(msg: &str) {
println!("cargo:warning={msg}");
}