Skip to content

Commit

Permalink
Merge branch 'main' into mm-cleanup-and-enable
Browse files Browse the repository at this point in the history
* main:
  [Xamarin.Android.Build.Tasks] Add AndroidPackagingOptionsExclude (dotnet#7356)
  [linker] preserve Java interfaces on non-bound Java types (dotnet#7204)
  [Xamarin.Android.Build.Tasks] AndroidLinkResources and Styleables (dotnet#7306)
  • Loading branch information
grendello committed Sep 14, 2022
2 parents 2b33563 + 2726a38 commit 94d9bea
Show file tree
Hide file tree
Showing 16 changed files with 377 additions and 4 deletions.
20 changes: 20 additions & 0 deletions Documentation/guides/building-apps/build-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ used to specify the ABI that the library targets. Thus, if you add
</ItemGroup>
```

## AndroidPackagingOptionsExclude

A set of file glob compatible items which will allow for items to be
excluded from the final package. The default values are as follows

```
<ItemGroup>
<AndroidPackagingOptionsExclude Include="DebugProbesKt.bin" />
<AndroidPackagingOptionsExclude Include="$([MSBuild]::Escape('*.kotlin_*')" />
</ItemGroup>
```
Items can use file blob characters for wildcards such as `*` and `?`.
However these Items MUST use URL encoding or '$([MSBuild]::Escape(''))'.
This is so MSBuild does not try to interpret them as actual file wildcards.

NOTE: `*`, `?` and `.` will be replaced in the `BuildApk` task with the
appropriate RegEx expressions.

Added in Xamarin.Android 13.1 and .NET 7.

## AndroidResource

All files with an *AndroidResource* build action are compiled into
Expand Down
3 changes: 3 additions & 0 deletions samples/HelloWorld/HelloLibrary/HelloLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<FileAlignment>512</FileAlignment>
<AndroidApplication>false</AndroidApplication>
<DebugType>portable</DebugType>
<AndroidUseIntermediateDesignerFile>True</AndroidUseIntermediateDesignerFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
</PropertyGroup>
<Import
Condition="Exists('..\..\..\Configuration.props')"
Expand Down Expand Up @@ -71,5 +73,6 @@
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\Case_Check.png" />
<AndroidResource Include="Resources\values\Attr.xml" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion samples/HelloWorld/HelloLibrary/LibraryActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
using Android.Views;
using Android.Widget;

namespace Mono.Samples.Hello
namespace HelloLibrary
{
[Activity(Label = "Library Activity", Name="mono.samples.hello.LibraryActivity")]
public class LibraryActivity : Activity
Expand Down
7 changes: 7 additions & 0 deletions samples/HelloWorld/HelloLibrary/Resources/values/Attr.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyLibraryWidget">
<attr name="library_bool_attr" format="boolean" />
<attr name="library_int_attr" format="integer" />
</declare-styleable>
</resources>
1 change: 1 addition & 0 deletions samples/HelloWorld/HelloWorld/HelloWorld.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<ItemGroup>
<AndroidResource Include="Resources\layout\Main.axml" />
<AndroidResource Include="Resources\values\Strings.xml" />
<AndroidResource Include="Resources\values\Attr.xml" />
<AndroidResource Include="Resources\mipmap-hdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-mdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\Icon.png" />
Expand Down
7 changes: 7 additions & 0 deletions samples/HelloWorld/HelloWorld/Resources/values/Attr.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyWidget">
<attr name="bool_attr" format="boolean" />
<attr name="int_attr" format="integer" />
</declare-styleable>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using Mono.Cecil.Cil;
using System.Text.RegularExpressions;
using Mono.Collections.Generic;
#if ILLINK
using Microsoft.Android.Sdk.ILLink;
#endif
Expand Down Expand Up @@ -69,8 +70,17 @@ protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainAppli
protected void ClearDesignerClass (TypeDefinition designer)
{
LogMessage ($" TryRemoving {designer.FullName}");
designer.NestedTypes.Clear ();
designer.Methods.Clear ();
// for each of the nested types clear all but the
// int[] fields.
for (int i = designer.NestedTypes.Count -1; i >= 0; i--) {
var nestedType = designer.NestedTypes [i];
RemoveFieldsFromType (nestedType, designer.Module);
if (nestedType.Fields.Count == 0) {
// no fields we do not need this class at all.
designer.NestedTypes.RemoveAt (i);
}
}
RemoveUpdateIdValues (designer);
designer.Fields.Clear ();
designer.Properties.Clear ();
designer.CustomAttributes.Clear ();
Expand Down Expand Up @@ -117,6 +127,48 @@ protected void FixType (TypeDefinition type, TypeDefinition localDesigner)
}
}

protected void RemoveFieldsFromType (TypeDefinition type, ModuleDefinition module)
{
for (int i = type.Fields.Count - 1; i >= 0; i--) {
var field = type.Fields [i];
if (field.FieldType.IsArray) {
continue;
}
LogMessage ($"Removing {type.Name}::{field.Name}");
type.Fields.RemoveAt (i);
}
}

protected void RemoveUpdateIdValues (TypeDefinition type)
{
foreach (var method in type.Methods) {
if (method.Name.Contains ("UpdateIdValues")) {
FixUpdateIdValuesBody (method);
} else {
FixBody (method.Body, type);
}
}

foreach (var nestedType in type.NestedTypes) {
RemoveUpdateIdValues (nestedType);
}
}

protected void FixUpdateIdValuesBody (MethodDefinition method)
{
List<Instruction> finalInstructions = new List<Instruction> ();
Collection<Instruction> instructions = method.Body.Instructions;
for (int i = 0; i < method.Body.Instructions.Count-1; i++) {
Instruction instruction = instructions[i];
string line = instruction.ToString ();
bool found = line.Contains ("Int32[]") || instruction.OpCode == OpCodes.Ret;
if (!found) {
method.Body.Instructions.Remove (instruction);
i--;
}
}
}

protected void FixupAssemblyTypes (AssemblyDefinition assembly, TypeDefinition designer)
{
foreach (ModuleDefinition module in assembly.Modules)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ void PreserveJavaObjectImplementation (TypeDefinition type)
PreserveIntPtrConstructor (type);
PreserveAttributeSetConstructor (type);
PreserveInvoker (type);
#if ILLINK
PreserveInterfaces (type);
#endif // ILLINK
}

void PreserveAttributeSetConstructor (TypeDefinition type)
Expand Down Expand Up @@ -196,7 +199,63 @@ void PreserveInvoker (TypeDefinition type)
PreserveConstructors (type, invoker);
PreserveIntPtrConstructor (invoker);
PreserveInterfaceMethods (type, invoker);
#if ILLINK
PreserveInterfaces (invoker);
#endif // ILLINK
}

#if ILLINK
void PreserveInterfaces (TypeDefinition type)
{
if (!type.HasInterfaces)
return;

if (!ShouldPreserveInterfaces (type))
return;

foreach (var iface in type.Interfaces) {
var td = iface.InterfaceType.Resolve ();
if (!td.ImplementsIJavaPeerable ())
continue;
Annotations.Mark (td);
}
}

// False if [Register(DoNotGenerateAcw=true)] is on the type
// False if [JniTypeSignature(GenerateJavaPeer=false)] is on the type
bool ShouldPreserveInterfaces (TypeDefinition type)
{
if (!type.HasCustomAttributes)
return true;

foreach (var attr in type.CustomAttributes) {
switch (attr.AttributeType.FullName) {
case "Android.Runtime.RegisterAttribute":
foreach (var property in attr.Properties) {
if (property.Name == "DoNotGenerateAcw") {
if (property.Argument.Value is bool value && value)
return false;
break;
}
}
break;
case "Java.Interop.JniTypeSignatureAttribute":
foreach (var property in attr.Properties) {
if (property.Name == "GenerateJavaPeer") {
if (property.Argument.Value is bool value && !value)
return false;
break;
}
}
break;
default:
break;
}
}

return true;
}
#endif // ILLINK

TypeDefinition GetInvokerType (TypeDefinition type)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,19 @@ protected override void FixBody (MethodBody body, TypeDefinition designer)
Dictionary<Instruction, int> instructions = new Dictionary<Instruction, int>();
var processor = body.GetILProcessor ();
string designerFullName = $"{designer.FullName}/";
bool isDesignerMethod = designerFullName.Contains (body.Method.DeclaringType.FullName);
string declaringTypeName = body.Method.DeclaringType.Name;
foreach (var i in body.Instructions)
{
string line = i.ToString ();
if (line.Contains (designerFullName) && !instructions.ContainsKey (i))
if ((line.Contains (designerFullName) || (isDesignerMethod && i.OpCode == OpCodes.Stsfld)) && !instructions.ContainsKey (i))
{
var match = opCodeRegex.Match (line);
if (match.Success && match.Groups.Count == 5) {
string key = match.Groups[4].Value.Replace (designerFullName, string.Empty);
if (isDesignerMethod) {
key = declaringTypeName +"::" + key;
}
if (designerConstants.ContainsKey (key) && !instructions.ContainsKey (i))
instructions.Add(i, designerConstants [key]);
}
Expand Down
33 changes: 33 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public class BuildApk : AndroidTask

public string[] DoNotPackageJavaLibraries { get; set; }

public string [] ExcludeFiles { get; set; }

public string Debug { get; set; }

public string AndroidSequencePointsMode { get; set; }
Expand Down Expand Up @@ -122,6 +124,8 @@ protected virtual void FixupArchive (ZipArchiveEx zip) { }

List<string> existingEntries = new List<string> ();

List<Regex> excludePatterns = new List<Regex> ();

void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary<string, CompressedAssemblyInfo> compressedAssembliesInfo, string assemblyStoreApkName)
{
ArchiveFileList files = new ArchiveFileList ();
Expand Down Expand Up @@ -248,6 +252,13 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
Log.LogDebugMessage ($"Skipping {path} as the archive file is up to date.");
continue;
}
// check for ignored items
foreach (var pattern in excludePatterns) {
if(pattern.IsMatch (path)) {
Log.LogDebugMessage ($"Ignoring jar entry '{name}' from '{Path.GetFileName (jarFile)}'. Filename matched the exclude pattern '{pattern}'.");
continue;
}
}
if (string.Compare (Path.GetFileName (name), "AndroidManifest.xml", StringComparison.OrdinalIgnoreCase) == 0) {
Log.LogDebugMessage ("Ignoring jar entry {0} from {1}: the same file already exists in the apk", name, Path.GetFileName (jarFile));
continue;
Expand Down Expand Up @@ -293,6 +304,10 @@ public override bool RunTask ()

existingEntries.Clear ();

foreach (var pattern in ExcludeFiles ?? Array.Empty<string> ()) {
excludePatterns.Add (FileGlobToRegEx (pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled));
}

bool debug = _Debug;
bool compress = !debug && EnableCompression;
IDictionary<string, CompressedAssemblyInfo> compressedAssembliesInfo = null;
Expand Down Expand Up @@ -326,6 +341,24 @@ public override bool RunTask ()
return !Log.HasLoggedErrors;
}

static Regex FileGlobToRegEx (string fileGlob, RegexOptions options)
{
StringBuilder sb = new StringBuilder ();
foreach (char c in fileGlob) {
switch (c) {
case '*': sb.Append (".*");
break;
case '?': sb.Append (".");
break;
case '.': sb.Append (@"\.");
break;
default: sb.Append (c);
break;
}
}
return new Regex (sb.ToString (), options);
}

void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<string, CompressedAssemblyInfo> compressedAssembliesInfo, string assemblyStoreApkName)
{
string sourcePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,26 @@ public class Test
}
}

[Test]
public void CheckExcludedFilesAreMissing ()
{

var proj = new XamarinAndroidApplicationProject () {
IsRelease = true,
};
proj.PackageReferences.Add (KnownPackages.Xamarin_Kotlin_StdLib_Common);
using (var b = CreateApkBuilder ()) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
var apk = Path.Combine (Root, b.ProjectDirectory,
proj.OutputPath, $"{proj.PackageName}-Signed.apk");
string expected = $"Ignoring jar entry 'kotlin/Error.kotlin_metadata'";
Assert.IsTrue (b.LastBuildOutput.ContainsText (expected), $"Error.kotlin_metadata should have been ignored.");
using (var zip = ZipHelper.OpenZip (apk)) {
Assert.IsFalse (zip.ContainsEntry ("Error.kotlin_metadata"), "Error.kotlin_metadata should have been ignored.");
}
}
}

[Test]
public void ExtractNativeLibsTrue ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ public static class KnownPackages
Id = "Xamarin.GooglePlayServices.Maps",
Version = "117.0.1.2",
};
public static Package Xamarin_Kotlin_StdLib_Common = new Package {
Id = "Xamarin.Kotlin.Stdlib.Common",
Version = "1.6.20.1"
};
public static Package Acr_UserDialogs = new Package {
Id = "Acr.UserDialogs",
Version = "6.5.1",
Expand Down
12 changes: 12 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>

<ItemGroup>
<AndroidPackagingOptionsExclude Include="DebugProbesKt.bin" />
<AndroidPackagingOptionsExclude Include="$([MSBuild]::Escape('*.kotlin_*'))" />
</ItemGroup>

<!--
*******************************************
Imports
Expand Down Expand Up @@ -2067,6 +2072,11 @@ because xbuild doesn't support framework reference assemblies.
Outputs="$(_BuildApkEmbedOutputs)"
Condition="'$(EmbedAssembliesIntoApk)' == 'True'">
<!-- Put the assemblies and native libraries in the apk -->
<!--
NOTE: Adding Arguments to BuildApk or BuildBaseAppBundle
also need to have the args added to Xamarin.Android.Common.Debugging.targets
in monodroid.
-->
<BuildApk
Condition=" '$(AndroidPackageFormat)' != 'aab' "
AndroidNdkDirectory="$(_AndroidNdkDirectory)"
Expand Down Expand Up @@ -2097,6 +2107,7 @@ because xbuild doesn't support framework reference assemblies.
IncludeWrapSh="$(AndroidIncludeWrapSh)"
CheckedBuild="$(_AndroidCheckedBuild)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
ExcludeFiles="@(AndroidPackagingOptionsExclude)"
UseAssemblyStore="$(AndroidUseAssemblyStore)">
<Output TaskParameter="OutputFiles" ItemName="ApkFiles" />
</BuildApk>
Expand Down Expand Up @@ -2130,6 +2141,7 @@ because xbuild doesn't support framework reference assemblies.
IncludeWrapSh="$(AndroidIncludeWrapSh)"
CheckedBuild="$(_AndroidCheckedBuild)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
ExcludeFiles="@(AndroidPackagingOptionsExclude)"
UseAssemblyStore="$(AndroidUseAssemblyStore)">
<Output TaskParameter="OutputFiles" ItemName="BaseZipFile" />
</BuildBaseAppBundle>
Expand Down
Loading

0 comments on commit 94d9bea

Please sign in to comment.