also looking at this
perf(android): ship libcraft.so without its DWARF, and keep the DWARF for symbols
#217
6 files
+235
-14
| @@ -2471,11 +2471,9 @@ pub fn build(b: *std.Build) void { | ||
| 2471 | 2471 | }); |
| 2472 | 2472 | linkAndroidLibc(b, android_arm64_jni, ndk, "aarch64-linux-android"); |
| 2473 | 2473 | |
| 2474 | const android_arm64_jni_install = b.addInstallArtifact(android_arm64_jni, .{ | |
| 2475 | .dest_dir = .{ .override = .{ .custom = "android/arm64-v8a" } }, | |
| 2476 | }); | |
| 2477 | build_android.dependOn(&android_arm64_jni_install.step); | |
| 2478 | build_android_all.dependOn(&android_arm64_jni_install.step); | |
| 2474 | const android_arm64_jni_install = installAndroidJni(b, android_arm64_jni, ndk, "arm64-v8a", debug_build); | |
| 2475 | build_android.dependOn(android_arm64_jni_install); | |
| 2476 | build_android_all.dependOn(android_arm64_jni_install); | |
| 2479 | 2477 | } else { |
| 2480 | 2478 | // Not built at all rather than built without bionic. The library would |
| 2481 | 2479 | // compile, install, ship in the APK and then fail at `dlopen` with two |
| @@ -2521,11 +2519,9 @@ pub fn build(b: *std.Build) void { | ||
| 2521 | 2519 | |
| 2522 | 2520 | // `x86_64`, matching the jniLibs directory the loader looks in — not |
| 2523 | 2521 | // `x86`, which is the 32-bit ABI and would be silently ignored. |
| 2524 | const android_x86_jni_install = b.addInstallArtifact(android_x86_jni, .{ | |
| 2525 | .dest_dir = .{ .override = .{ .custom = "android/x86_64" } }, | |
| 2526 | }); | |
| 2527 | build_android_x86.dependOn(&android_x86_jni_install.step); | |
| 2528 | build_android_all.dependOn(&android_x86_jni_install.step); | |
| 2522 | const android_x86_jni_install = installAndroidJni(b, android_x86_jni, ndk, "x86_64", debug_build); | |
| 2523 | build_android_x86.dependOn(android_x86_jni_install); | |
| 2524 | build_android_all.dependOn(android_x86_jni_install); | |
| 2529 | 2525 | } |
| 2530 | 2526 | |
| 2531 | 2527 | const android_x86_lib = b.addLibrary(.{ |
| @@ -2618,6 +2614,82 @@ fn androidSysroot(b: *std.Build, ndk: []const u8) ?[]const u8 { | ||
| 2618 | 2614 | return null; |
| 2619 | 2615 | } |
| 2620 | 2616 | |
| 2617 | /// A tool from the NDK's LLVM toolchain, or null when this NDK has none for | |
| 2618 | /// this host. Found the way `androidSysroot` finds the sysroot, and for the | |
| 2619 | /// same reason. | |
| 2620 | fn androidTool(b: *std.Build, ndk: []const u8, name: []const u8) ?[]const u8 { | |
| 2621 | const io = b.graph.io; | |
| 2622 | for ([_][]const u8{ "darwin-x86_64", "darwin-arm64", "linux-x86_64", "windows-x86_64" }) |host| { | |
| 2623 | const exe = if (std.mem.startsWith(u8, host, "windows")) b.fmt("{s}.exe", .{name}) else name; | |
| 2624 | const candidate = b.pathJoin(&.{ ndk, "toolchains/llvm/prebuilt", host, "bin", exe }); | |
| 2625 | std.Io.Dir.cwd().access(io, candidate, .{}) catch continue; | |
| 2626 | return candidate; | |
| 2627 | } | |
| 2628 | return null; | |
| 2629 | } | |
| 2630 | ||
| 2631 | /// Install a JNI library where the Android generator looks for it, without its | |
| 2632 | /// DWARF, and install the DWARF beside it for symbolication (#204). | |
| 2633 | /// | |
| 2634 | /// Every generated app's APK carries this library, and at `ReleaseSafe` each | |
| 2635 | /// one was about 5.5 MB, most of it debug info nothing read: AGP strips native | |
| 2636 | /// libraries by running the NDK's strip, and a generated project configures no | |
| 2637 | /// NDK, so it never did. So it happens here, with the same tool AGP would use. | |
| 2638 | /// | |
| 2639 | /// `--strip-debug` and not `--strip-all`. The symbol table stays, which is what | |
| 2640 | /// turns a native crash tombstone's addresses into function names, and it is a | |
| 2641 | /// small fraction of what the DWARF was. The DWARF goes to | |
| 2642 | /// `android-symbols/<abi>/libcraft.so.debug`, linked from the stripped library | |
| 2643 | /// by `.gnu_debuglink`, which is the shape `ndk-stack` and Play Console's | |
| 2644 | /// native debug symbols both take. | |
| 2645 | /// | |
| 2646 | /// `llvm-objcopy` rather than Zig's own ObjCopy step, because ELF to ELF | |
| 2647 | /// copying is `fatal("unimplemented")` in `zig objcopy` at 0.17.0-dev.1963; | |
| 2648 | /// every strip mode fails. The NDK is already required to build these at all. | |
| 2649 | /// | |
| 2650 | /// A Debug build installs the library as built: whoever asked for Debug wants | |
| 2651 | /// the debug info in place. | |
| 2652 | fn installAndroidJni( | |
| 2653 | b: *std.Build, | |
| 2654 | lib: *std.Build.Step.Compile, | |
| 2655 | ndk: []const u8, | |
| 2656 | abi: []const u8, | |
| 2657 | keep_debug_info: bool, | |
| 2658 | ) *std.Build.Step { | |
| 2659 | const dest = b.fmt("android/{s}", .{abi}); | |
| 2660 | if (keep_debug_info) { | |
| 2661 | const install = b.addInstallArtifact(lib, .{ .dest_dir = .{ .override = .{ .custom = dest } } }); | |
| 2662 | return &install.step; | |
| 2663 | } | |
| 2664 | ||
| 2665 | const objcopy = androidTool(b, ndk, "llvm-objcopy") orelse { | |
| 2666 | return &b.addFail(b.fmt( | |
| 2667 | "-Dandroid-ndk={s} has no toolchains/llvm/prebuilt/<host>/bin/llvm-objcopy, " ++ | |
| 2668 | "which strips libcraft.so's debug info for release builds. " ++ | |
| 2669 | "Point it at an NDK root, or build with -Doptimize=Debug to keep it.", | |
| 2670 | .{ndk}, | |
| 2671 | )).step; | |
| 2672 | }; | |
| 2673 | ||
| 2674 | const keep = b.addSystemCommand(&.{ objcopy, "--only-keep-debug" }); | |
| 2675 | keep.addFileArg(lib.getEmittedBin()); | |
| 2676 | const debug_info = keep.addOutputFileArg("libcraft.so.debug"); | |
| 2677 | ||
| 2678 | const strip = b.addSystemCommand(&.{ objcopy, "--strip-debug" }); | |
| 2679 | strip.addPrefixedFileArg("--add-gnu-debuglink=", debug_info); | |
| 2680 | strip.addFileArg(lib.getEmittedBin()); | |
| 2681 | const stripped = strip.addOutputFileArg("libcraft.so"); | |
| 2682 | ||
| 2683 | const install = b.addInstallFileWithDir(stripped, .{ .custom = dest }, "libcraft.so"); | |
| 2684 | const symbols = b.addInstallFileWithDir( | |
| 2685 | debug_info, | |
| 2686 | .{ .custom = b.fmt("android-symbols/{s}", .{abi}) }, | |
| 2687 | "libcraft.so.debug", | |
| 2688 | ); | |
| 2689 | install.step.dependOn(&symbols.step); | |
| 2690 | return &install.step; | |
| 2691 | } | |
| 2692 | ||
| 2621 | 2693 | /// Link one Android artifact against the NDK's bionic. |
| 2622 | 2694 | /// |
| 2623 | 2695 | /// A `--libc` file, not include and library paths. Paths alone are what |