Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions src/tasks/AndroidAppBuilder/ApkBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ public ApkBuilder(TaskLoggingHelper logger)
}
}
string abi;
AndroidProject? project = null;
if (IsNativeAOT)
{
abi = AndroidProject.DetermineAbi(runtimeIdentifier);
Expand Down Expand Up @@ -408,12 +409,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
Expand Down Expand Up @@ -494,7 +495,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);

Expand All @@ -509,15 +512,6 @@ public ApkBuilder(TaskLoggingHelper logger)
else
{
var excludedLibs = new HashSet<string> { "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))));
}
Expand Down Expand Up @@ -562,8 +556,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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard turns a previously-working configuration into a hard failure for NativeAOT. When IsNativeAOT is true, project is never assigned (it stays null), so any NativeAOT Android build that enables StripDebugSymbols will now throw InvalidOperationException here instead of packaging. The .so files for NativeAOT come straight from AppDir and previously were packaged as-is regardless of StripDebugSymbols. Two options: (1) construct a lightweight AndroidProject (or factor StripBinaryInPlace so it doesn't require the CMake project state) so NativeAOT can also strip, or (2) scope the stripping/guard to the non-NativeAOT path so NativeAOT keeps its prior behavior. As written, this is a functional regression for NativeAOT + stripping rather than a defensive check for an impossible state.

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);
Expand Down
46 changes: 23 additions & 23 deletions src/tasks/MobileBuildTasks/Android/AndroidProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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();
Expand Down
Loading