makepad/platform/build.rs
Kevin Boos 3eae9b762a
Don't always set the app's icon (e.g., for packaged app bundles) (#1082)
* Support overriding the dpi factor at runtime, on all platforms

Add `Cx::set_window_dpi_override` for runtime UI zoom, but dispatch it
in a deferred manner so it's safe to call in an event handler.

For each platform, we connect the dpi override to the click/tap
coordinates to remap it properly, which was done on some platforms
but not most.

* Don't always set the app's icon (e.g., for packaged app bundles)
2026-05-18 22:47:11 +02:00

179 lines
7.7 KiB
Rust

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
/// Best-effort default for the macOS bundle name when MAKEPAD_BUNDLE_NAME isn't
/// set. Cargo doesn't expose the consuming binary's package name to a
/// dependency's build script (`CARGO_PKG_NAME` here is "makepad-platform"), so
/// we walk up from `OUT_DIR` (which is always
/// `<root>/target/<profile>/build/<crate>-<hash>/out`) to the directory that
/// contains `target/` and use that directory's name. For a typical project
/// that's the package or workspace root, which is almost always a meaningful
/// label. Capitalize the first letter so the menu bar shows "Sample app" rather
/// than "sample app". Returns `None` if the path doesn't have the expected shape
/// or the directory name isn't valid UTF-8.
fn detect_app_name(out_dir: &Path) -> Option<String> {
let workspace_root = out_dir.ancestors().nth(5)?;
let dir_name = workspace_root.file_name()?.to_str()?;
if dir_name.is_empty() {
return None;
}
let mut chars = dir_name.chars();
let first = chars.next()?;
Some(first.to_uppercase().collect::<String>() + chars.as_str())
}
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let path = Path::new(&out_dir)
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap();
let cwd = std::env::current_dir().unwrap();
let mut file = File::create(path.join("makepad-platform.path")).unwrap();
file.write_all(format!("{}", cwd.display()).as_bytes())
.unwrap();
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let target = env::var("TARGET").unwrap();
if target_os == "macos" {
// The downstream app can override the bundle name shown in the macOS
// application menu by setting MAKEPAD_BUNDLE_NAME — typically via its
// `.cargo/config.toml` `[env]` section with `force = true`. macOS uses
// CFBundleName from this Info.plist as the first menu bar item title
// for unbundled `cargo run` launches, and it overrides whatever NSMenu
// title we pass to setMainMenu:. When the env var isn't set, we fall
// back to the workspace/package directory name (capitalized), which
// is almost always more meaningful than a hardcoded placeholder.
let bundle_name = env::var("MAKEPAD_BUNDLE_NAME")
.ok()
.or_else(|| detect_app_name(Path::new(&out_dir)))
.unwrap_or_else(|| "Makepad App".to_string());
let bundle_id = env::var("MAKEPAD_BUNDLE_IDENTIFIER")
.unwrap_or_else(|_| format!("dev.makepad.{}", bundle_name.to_lowercase().replace(' ', "-")));
let command_line_plist = format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>{bundle_id}</string>
<key>CFBundleName</key>
<string>{bundle_name}</string>
<key>CFBundleDisplayName</key>
<string>{bundle_name}</string>
<key>GCSupportsControllerUserInteraction</key>
<true/>
<key>GCSupportedGameControllers</key>
<array>
<dict>
<key>ProfileName</key>
<string>ExtendedGamepad</string>
</dict>
</array>
</dict>
</plist>
"#
);
std::fs::write(path.join("Info.plist"), command_line_plist).unwrap();
}
// Per slot: env-var override → auto-discovery in `<workspace_root>/resources/`.
// Workspace root is the dir containing `target/` (5 ancestors up from
// OUT_DIR), same heuristic as `detect_app_name`.
let icons: &[(&str, &str, &str)] = &[
("MAKEPAD_APP_ICON_32", "icon_32.png", "CUSTOM_ICON_PNG_32"),
("MAKEPAD_APP_ICON_64", "icon_64.png", "CUSTOM_ICON_PNG_64"),
("MAKEPAD_APP_ICON_128", "icon_128.png", "CUSTOM_ICON_PNG_128"),
("MAKEPAD_APP_ICON_256", "icon_256.png", "CUSTOM_ICON_PNG_256"),
("MAKEPAD_APP_ICON_512", "icon_512.png", "CUSTOM_ICON_PNG_512"),
("MAKEPAD_APP_ICON_1024", "icon_1024.png", "CUSTOM_ICON_PNG_1024"),
("MAKEPAD_APP_ICON_ICO", "icon.ico", "CUSTOM_ICON_ICO"),
];
let resources_dir = Path::new(&out_dir).ancestors().nth(5).map(|r| r.join("resources"));
let mut icon_gen = String::new();
for &(var, filename, const_name) in icons {
println!("cargo:rerun-if-env-changed={var}");
let path = env::var(var).ok().or_else(|| {
let p = resources_dir.as_ref()?.join(filename);
p.is_file().then(|| p.to_string_lossy().into_owned())
});
let value = match &path {
Some(p) => {
println!("cargo:rerun-if-changed={p}");
format!("include_bytes!(r#\"{p}\"#)")
}
None => "&[]".to_string(),
};
icon_gen.push_str(&format!(
"#[allow(dead_code)] pub static {const_name}: &'static [u8] = {value};\n"
));
}
// Watch the resources dir so new/removed icon files trigger a rebuild
// (rerun-if-changed on a non-existent file is a no-op).
if let Some(dir) = resources_dir.as_ref().filter(|d| d.is_dir()) {
println!("cargo:rerun-if-changed={}", dir.display());
}
std::fs::write(Path::new(&out_dir).join("app_icon_gen.rs"), icon_gen).unwrap();
println!("cargo:rustc-check-cfg=cfg(apple_bundle,apple_sim,lines,use_gles_3,use_vulkan,linux_direct,quest,no_android_choreographer,ohos_sim,headless,use_unstable_unix_socket_ancillary_data_2021)");
println!("cargo:rerun-if-env-changed=MAKEPAD");
println!("cargo:rerun-if-env-changed=MAKEPAD_PACKAGE_DIR");
println!("cargo:rerun-if-env-changed=MAKEPAD_BUNDLE_NAME");
println!("cargo:rerun-if-env-changed=MAKEPAD_BUNDLE_IDENTIFIER");
println!("cargo:rerun-if-env-changed=IPHONEOS_DEPLOYMENT_TARGET");
if let Ok(configs) = env::var("MAKEPAD") {
for config in configs.split(['+', ',']) {
match config {
"lines" => println!("cargo:rustc-cfg=lines"),
"linux_direct" => println!("cargo:rustc-cfg=linux_direct"),
"no_android_choreographer" => println!("cargo:rustc-cfg=no_android_choreographer"),
"quest" => {
println!("cargo:rustc-cfg=quest");
println!("cargo:rustc-cfg=use_gles_3");
println!("cargo:rustc-cfg=use_vulkan");
}
"apple_bundle" => println!("cargo:rustc-cfg=apple_bundle"),
"ohos_sim" => println!("cargo:rustc-cfg=ohos_sim"),
"headless" => println!("cargo:rustc-cfg=headless"),
"use_gles_3" => println!("cargo:rustc-cfg=use_gles_3"),
"vulkan" | "use_vulkan" => println!("cargo:rustc-cfg=use_vulkan"),
_ => {}
}
}
}
match target_os.as_str() {
"macos" => {
println!("cargo:rustc-link-lib=framework=GameController");
}
"ios" => {
if target == "aarch64-apple-ios-sim" {
println!("cargo:rustc-cfg=apple_sim");
}
println!("cargo:rustc-link-lib=framework=MetalKit");
println!("cargo:rustc-link-lib=framework=GameController");
}
"tvos" => {
if target == "aarch64-apple-tvos-sim" {
println!("cargo:rustc-cfg=apple_sim");
}
println!("cargo:rustc-link-lib=framework=MetalKit");
println!("cargo:rustc-link-lib=framework=GameController");
}
"linux" => {
println!("cargo:rustc-cfg=use_gles_3");
println!("cargo:rustc-link-lib=xkbcommon");
}
"android" => {
println!("cargo:rustc-cfg=use_gles_3");
}
_ => (),
}
}