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");
}
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();
}
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");
if let Ok(native) = std::env::var("OHOS_SDK_NATIVE") {
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!(
"'xcomponent' 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;
""
}
};
if !arch_dir.is_empty() {
let lib_dir = format!("{native}/sysroot/usr/lib/{arch_dir}");
if std::path::Path::new(&lib_dir).is_dir() {
println!("cargo:rustc-link-search=native={lib_dir}");
} else {
warn(&format!(
"'xcomponent': expected the OpenHarmony libraries at '{lib_dir}', which does \
not exist. Check that OHOS_SDK_NATIVE points at the SDK's 'native' directory."
));
}
println!("cargo:rustc-link-arg=-Wl,-rpath-link,{lib_dir}");
}
} else {
warn("'xcomponent' is enabled for an OpenHarmony target but OHOS_SDK_NATIVE is not set.");
warn(" The ArkUI bridge links the SDK's 'libace_ndk.z.so':");
warn(" export OHOS_SDK_NATIVE=<sdk>/linux/native");
warn(" and build with, e.g.:");
warn(" cargo ohos build -t aarch64 --features 'harmony xcomponent'");
}
}
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}");
}