diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index df0a274e9d4895..1c194e79ed8f54 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -330,6 +330,7 @@ public ApkBuilder(TaskLoggingHelper logger) } } string abi; + AndroidProject? project = null; if (IsNativeAOT) { abi = AndroidProject.DetermineAbi(runtimeIdentifier); @@ -400,12 +401,12 @@ public ApkBuilder(TaskLoggingHelper logger) } File.WriteAllText(Path.Combine(OutputDir, monodroidSource), monodroidContent); - AndroidProject project = new AndroidProject("monodroid", runtimeIdentifier, AndroidNdk, logger); - project.GenerateCMake(OutputDir, MinApiLevel, StripDebugSymbols); - project.BuildCMake(OutputDir, StripDebugSymbols); + project = new AndroidProject("monodroid", runtimeIdentifier, AndroidNdk, logger); + project.GenerateCMake(OutputDir, MinApiLevel); + project.BuildCMake(OutputDir); abi = project.Abi; - // TODO: https://github.com/dotnet/runtime/issues/115717 + } // 2. Compile Java files @@ -486,7 +487,9 @@ public ApkBuilder(TaskLoggingHelper logger) // 3. Generate APK - string debugModeArg = StripDebugSymbols ? string.Empty : "--debug-mode"; + // Always keep the app debuggable to enable adb shell run-as access during test runs. + // Symbol stripping is done post-build via llvm-strip rather than by setting android:debuggable=false. + string debugModeArg = "--debug-mode"; string apkFile = Path.Combine(OutputDir, "bin", $"{ProjectName}.unaligned.apk"); Utils.RunProcess(logger, androidSdkHelper.AaptPath, $"package -f -m -F {apkFile} -A assets -M AndroidManifest.xml -I {androidJar} {debugModeArg}", workingDir: OutputDir); @@ -501,15 +504,6 @@ public ApkBuilder(TaskLoggingHelper logger) else { var excludedLibs = new HashSet { "libmonodroid.so" }; - if (IsCoreCLR) - { - if (StripDebugSymbols) - { - // exclude debugger support libs - excludedLibs.Add("libmscordbi.so"); - excludedLibs.Add("libmscordaccore.so"); - } - } if (!StaticLinkedRuntime) dynamicLibs.AddRange(Directory.GetFiles(AppDir, "*.so").Where(file => !excludedLibs.Contains(Path.GetFileName(file)))); } @@ -554,8 +548,13 @@ public ApkBuilder(TaskLoggingHelper logger) } } - // NOTE: we can run android-strip tool from NDK to shrink native binaries here even more. File.Copy(dynamicLib, Path.Combine(OutputDir, destRelative), true); + if (StripDebugSymbols) + { + if (project is null) + throw new InvalidOperationException("StripDebugSymbols is enabled, but no Android project is available to strip native libraries during APK packaging."); + project.StripBinaryInPlace(Path.Combine(OutputDir, destRelative)); + } Utils.RunProcess(logger, androidSdkHelper.AaptPath, $"add {apkFile} {NormalizePathToUnix(destRelative)}", workingDir: OutputDir); } Utils.RunProcess(logger, androidSdkHelper.AaptPath, $"add {apkFile} classes.dex", workingDir: OutputDir); diff --git a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs index b3c3ab55ba85da..d5ce2f8a501f41 100644 --- a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs +++ b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs @@ -20,6 +20,7 @@ public sealed class AndroidProject private TaskLoggingHelper logger; private string abi; + private string androidNdkPath; private string androidToolchainPath; private string projectName; private string targetArchitecture; @@ -33,6 +34,7 @@ public AndroidProject(string projectName, string runtimeIdentifier, TaskLoggingH public AndroidProject(string projectName, string runtimeIdentifier, string androidNdkPath, TaskLoggingHelper logger) { + this.androidNdkPath = androidNdkPath; androidToolchainPath = Path.Combine(androidNdkPath, "build", "cmake", "android.toolchain.cmake").Replace('\\', '/'); abi = DetermineAbi(runtimeIdentifier); targetArchitecture = GetTargetArchitecture(runtimeIdentifier); @@ -50,49 +52,47 @@ public void Build(string workingDir, ClangBuildOptions buildOptions, bool stripD Utils.RunProcess(logger, tools.ClangPath, workingDir: workingDir, args: clangArgs); } - public void GenerateCMake(string workingDir, bool stripDebugSymbols) + public void GenerateCMake(string workingDir) { - GenerateCMake(workingDir, DefaultMinApiLevel, stripDebugSymbols); + GenerateCMake(workingDir, DefaultMinApiLevel); } - public void GenerateCMake(string workingDir, string apiLevel = DefaultMinApiLevel, bool stripDebugSymbols = false) + public void GenerateCMake(string workingDir, string apiLevel = DefaultMinApiLevel) { // force ninja generator on Windows, the VS generator causes issues with the built-in Android support in VS var generator = Utils.IsWindows() ? "-G Ninja" : ""; string cmakeGenArgs = $"{generator} -DCMAKE_TOOLCHAIN_FILE={androidToolchainPath} -DANDROID_ABI=\"{Abi}\" -DANDROID_STL=none -DTARGETS_ANDROID=1 " + $"-DANDROID_PLATFORM=android-{apiLevel} -B {projectName}"; - if (stripDebugSymbols) - { - // Use "-s" to strip debug symbols, it complains it's unused but it works - cmakeGenArgs += " -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_C_FLAGS=\"-s -Wno-unused-command-line-argument\""; - } - else - { - cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug"; - } + // Always build with debug info; symbol stripping is done post-build via StripBinaryInPlace + cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug"; Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeGenArgs); } - public string BuildCMake(string workingDir, bool stripDebugSymbols = false) + public string BuildCMake(string workingDir) { - string cmakeBuildArgs = $"--build {projectName}"; - - if (stripDebugSymbols) - { - cmakeBuildArgs += " --config MinSizeRel"; - } - else - { - cmakeBuildArgs += " --config Debug"; - } + string cmakeBuildArgs = $"--build {projectName} --config Debug"; Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeBuildArgs); return Path.Combine(workingDir, projectName); } + public void StripBinaryInPlace(string filePath) + { + string hostTag = GetHostOS() switch + { + "windows" => "windows-x86_64", + "osx" => "darwin-x86_64", + _ => "linux-x86_64" + }; + string execExt = Utils.IsWindows() ? ".exe" : string.Empty; + string llvmStripPath = Path.Combine(androidNdkPath, "toolchains", "llvm", "prebuilt", hostTag, "bin", $"llvm-strip{execExt}"); + logger.LogMessage(MessageImportance.High, $"Stripping debug symbols from {filePath}"); + Utils.RunProcess(logger, llvmStripPath, args: $"--strip-debug \"{filePath}\""); + } + private static string BuildClangArgs(ClangBuildOptions buildOptions) { StringBuilder ret = new StringBuilder();