Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
<ItemGroup>
<RuntimeHostConfigurationOption Include="Microsoft.Android.Runtime.RuntimeFeature.TrimmableTypeMap"
Value="true" Trim="true" />
<RuntimeHostConfigurationOption Include="Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration"
Value="false" Trim="true" />
<RuntimeHostConfigurationOption Include="System.Runtime.InteropServices.TypeMappingEntryAssembly"
Value="$(_TypeMapAssemblyName)" />
<!-- [Export] metadata is still needed at compile time, but the legacy runtime helper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NUnit.Framework;
using Xamarin.Android.Tasks;
using Xamarin.ProjectTools;
using Xamarin.Tools.Zip;

namespace Xamarin.Android.Build.Tests {
[TestFixture]
Expand Down Expand Up @@ -229,6 +231,15 @@ public void TrimmableTypeMap_RuntimeArtifacts_ArePackagedInSdk ()
FileAssert.Exists (Path.Combine (toolsDir, file), $"{file} should exist in the SDK pack.");
}

var trimmableJar = Path.Combine (toolsDir, "java_runtime_trimmable.jar");
using (var zip = ZipArchive.Open (trimmableJar, FileMode.Open)) {
zip.AssertDoesNotContainEntry (trimmableJar, "net/dot/jni/ManagedPeer.class");
}

var trimmableDex = Path.Combine (toolsDir, "java_runtime_trimmable.dex");
Assert.IsFalse (
FileContainsAscii (trimmableDex, "Lnet/dot/jni/ManagedPeer;"),
"java_runtime_trimmable.dex should not contain the Java ManagedPeer type descriptor.");
}

// T1: end-to-end build coverage for [Export] and [ExportField] under trimmable.
Expand Down Expand Up @@ -284,10 +295,11 @@ class ExportShapes : Java.Lang.Object {
string? exportShapesText = null;
foreach (var f in allJavaFiles) {
var text = File.ReadAllText (f);
if (text.Contains ("EchoString") && text.Contains ("InitialFoo")) {
StringAssert.DoesNotContain ("net.dot.jni.ManagedPeer", text,
$"Trimmable generated Java source should not reference net.dot.jni.ManagedPeer: {f}");
if (exportShapesJava == null && text.Contains ("EchoString") && text.Contains ("InitialFoo")) {
exportShapesJava = f;
exportShapesText = text;
break;
}
}
Assert.IsNotNull (exportShapesJava,
Expand Down Expand Up @@ -318,6 +330,9 @@ class ExportShapes : Java.Lang.Object {
var typemapDir = builder.Output.GetIntermediaryPath ("typemap");
var typemapDlls = Directory.GetFiles (typemapDir, "*.TypeMap.dll");
Assert.IsNotEmpty (typemapDlls, "Trimmable typemap should produce at least one *.TypeMap.dll.");

var apk = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk");
AssertApkDexDoesNotContain (apk, "Lnet/dot/jni/ManagedPeer;");
}

// T6: trim-warning baseline for [Export] under trimmable.
Expand Down Expand Up @@ -425,5 +440,40 @@ public ConcreteProvider (Android.Content.Context context) : base (context) { }
using var builder = CreateApkBuilder ();
Assert.IsTrue (builder.Build (proj), "Build should have succeeded — abstract types with protected ctors should not cause XAGTT7009.");
}

static void AssertApkDexDoesNotContain (string apk, string value)
{
FileAssert.Exists (apk);
using var zip = ZipArchive.Open (apk, FileMode.Open);
var dexEntries = zip
.Where (entry => entry.FullName.StartsWith ("classes", StringComparison.Ordinal) &&
entry.FullName.EndsWith (".dex", StringComparison.Ordinal))
.ToArray ();
Assert.IsNotEmpty (dexEntries, $"{apk} should contain at least one dex file.");

foreach (var entry in dexEntries) {
Assert.IsFalse (
EntryContainsAscii (entry, value),
$"{entry.FullName} should not contain {value}.");
}
}

static bool EntryContainsAscii (ZipEntry entry, string value)
{
using var stream = new MemoryStream ();
entry.Extract (stream);
return ContainsAscii (stream.ToArray (), value);
}

static bool FileContainsAscii (string file, string value)
{
return ContainsAscii (File.ReadAllBytes (file), value);
}

static bool ContainsAscii (byte [] data, string value)
{
var pattern = Encoding.ASCII.GetBytes (value);
return data.AsSpan ().IndexOf (pattern) >= 0;
}
}
}
2 changes: 1 addition & 1 deletion src/java-runtime/java-runtime.targets
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<OutputDex>$(OutputPath)java_runtime_trimmable.dex</OutputDex>
<IntermediateRuntimeOutputPath>$(IntermediateOutputPath)release-trimmable</IntermediateRuntimeOutputPath>
<IntermediateRuntimeClassesTxt>$(IntermediateOutputPath)release-trimmable.txt</IntermediateRuntimeClassesTxt>
<RemoveItems>..\..\src-ThirdParty\bazel\java\mono\android\debug\MultiDexLoader.java;java\mono\android\debug-net6\BuildConfig.java;java\mono\android\debug\BuildConfig.java;java\mono\android\release\BuildConfig.java;java\mono\android\MonoPackageManager.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyObject.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyThrowable.java</RemoveItems>
<RemoveItems>..\..\src-ThirdParty\bazel\java\mono\android\debug\MultiDexLoader.java;java\mono\android\debug-net6\BuildConfig.java;java\mono\android\debug\BuildConfig.java;java\mono\android\release\BuildConfig.java;java\mono\android\MonoPackageManager.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\ManagedPeer.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyObject.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyThrowable.java</RemoveItems>
<AddItems>java-trimmable\net\dot\jni\internal\JavaProxyObject.java;java-trimmable\net\dot\jni\internal\JavaProxyThrowable.java</AddItems>
</_RuntimeOutput>
<_RuntimeOutput Include="$(OutputPath)java_runtime_fastdev.jar">
Expand Down