diff --git a/Documentation/guides/building-apps/build-items.md b/Documentation/guides/building-apps/build-items.md
index 1191532ceb2..305d34ca957 100644
--- a/Documentation/guides/building-apps/build-items.md
+++ b/Documentation/guides/building-apps/build-items.md
@@ -204,6 +204,26 @@ used to specify the ABI that the library targets. Thus, if you add
```
+## 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
+
+```
+
+
+
+
+```
+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
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
index acb9ab380d8..28cca622f9b 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
@@ -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; }
@@ -122,6 +124,8 @@ protected virtual void FixupArchive (ZipArchiveEx zip) { }
List existingEntries = new List ();
+ List excludePatterns = new List ();
+
void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary compressedAssembliesInfo, string assemblyStoreApkName)
{
ArchiveFileList files = new ArchiveFileList ();
@@ -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;
@@ -293,6 +304,10 @@ public override bool RunTask ()
existingEntries.Clear ();
+ foreach (var pattern in ExcludeFiles ?? Array.Empty ()) {
+ excludePatterns.Add (FileGlobToRegEx (pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled));
+ }
+
bool debug = _Debug;
bool compress = !debug && EnableCompression;
IDictionary compressedAssembliesInfo = null;
@@ -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 compressedAssembliesInfo, string assemblyStoreApkName)
{
string sourcePath;
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
index 6a4b9917b44..3e480fd8d6c 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
@@ -915,6 +915,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 ()
{
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/KnownPackages.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/KnownPackages.cs
index 3757f53f860..3cac7b400be 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/KnownPackages.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/KnownPackages.cs
@@ -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",
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index 686fb70c67d..5abab6d0d89 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -351,6 +351,11 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
+
+
+
+
+
+
@@ -2096,6 +2107,7 @@ because xbuild doesn't support framework reference assemblies.
IncludeWrapSh="$(AndroidIncludeWrapSh)"
CheckedBuild="$(_AndroidCheckedBuild)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
+ ExcludeFiles="@(AndroidPackagingOptionsExclude)"
UseAssemblyStore="$(AndroidUseAssemblyStore)">