diff --git a/Documentation/guides/building-apps/build-items.md b/Documentation/guides/building-apps/build-items.md
index 3e0ff017700..cd78fd4fa02 100644
--- a/Documentation/guides/building-apps/build-items.md
+++ b/Documentation/guides/building-apps/build-items.md
@@ -233,15 +233,62 @@ 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.
+For example
+
+```
+
+
+
+
+```
+
NOTE: `*`, `?` and `.` will be replaced in the `BuildApk` task with the
-appropriate RegEx expressions.
+appropriate file globs.
+
+If the default file glob is too restrictive you can remove it by adding the
+following to your csproj
+
+```
+
+
+
+```
Added in Xamarin.Android 13.1 and .NET 7.
+## AndroidPackagingOptionsInclude
+
+A set of file glob compatible items which will allow for items to be
+included 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.
+For example
+
+```
+
+
+
+
+```
+
+NOTE: `*`, `?` and `.` will be replaced in the `BuildApk` task with the
+appropriate file globs.
+
+Added in .NET 9.
+
## AndroidResource
All files with an *AndroidResource* build action are compiled into
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/Sdk/AutoImport.props b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/Sdk/AutoImport.props
index 7bde84e8ee2..24e3ed08a61 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/Sdk/AutoImport.props
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/Sdk/AutoImport.props
@@ -61,4 +61,10 @@ https://github.com/dotnet/designs/blob/4703666296f5e59964961464c25807c727282cae/
+
+
+
+
+
+
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
index fbcaa1e0944..0a15a2b8a26 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
@@ -76,6 +76,8 @@ public class BuildApk : AndroidTask
public string [] ExcludeFiles { get; set; }
+ public string [] IncludeFiles { get; set; }
+
public string Debug { get; set; }
public string AndroidSequencePointsMode { get; set; }
@@ -130,6 +132,8 @@ protected virtual void FixupArchive (ZipArchiveEx zip) { }
List excludePatterns = new List ();
+ List includePatterns = new List ();
+
void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOutputPath, bool debug, bool compress, IDictionary compressedAssembliesInfo, string assemblyStoreApkName)
{
ArchiveFileList files = new ArchiveFileList ();
@@ -258,13 +262,22 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
}
// check for ignored items
bool exclude = false;
- 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}'.");
- exclude = true;
+ bool forceInclude = false;
+ foreach (var include in includePatterns) {
+ if (include.IsMatch (path)) {
+ forceInclude = true;
break;
}
}
+ if (!forceInclude) {
+ 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}'.");
+ exclude = true;
+ break;
+ }
+ }
+ }
if (exclude)
continue;
if (string.Compare (Path.GetFileName (name), "AndroidManifest.xml", StringComparison.OrdinalIgnoreCase) == 0) {
@@ -310,6 +323,9 @@ public override bool RunTask ()
foreach (var pattern in ExcludeFiles ?? Array.Empty ()) {
excludePatterns.Add (FileGlobToRegEx (pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled));
}
+ foreach (var pattern in IncludeFiles ?? Array.Empty ()) {
+ includePatterns.Add (FileGlobToRegEx (pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled));
+ }
bool debug = _Debug;
bool compress = !debug && EnableCompression;
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 df2a878cd99..526f8dbe9b8 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
@@ -585,6 +585,51 @@ public void CheckExcludedFilesAreMissing ()
}
}
+ [Test]
+ public void CheckExcludedFilesCanBeModified ()
+ {
+
+ 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 ("kotlin/Error.kotlin_metadata"), "Error.kotlin_metadata should have been ignored.");
+ }
+ proj.OtherBuildItems.Add (new BuildItem ("AndroidPackagingOptionsExclude") {
+ Remove = () => "$([MSBuild]::Escape('*.kotlin*'))",
+ });
+ Assert.IsTrue (b.Clean (proj), "Clean should have succeeded.");
+ Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
+ using (var zip = ZipHelper.OpenZip (apk)) {
+ Assert.IsTrue (zip.ContainsEntry ("kotlin/Error.kotlin_metadata"), "Error.kotlin_metadata should have been included.");
+ }
+ }
+ }
+
+ [Test]
+ public void CheckIncludedFilesArePresent ()
+ {
+ var proj = new XamarinAndroidApplicationProject () {
+ IsRelease = true,
+ };
+ proj.PackageReferences.Add (KnownPackages.Xamarin_Kotlin_Reflect);
+ 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");
+ using (var zip = ZipHelper.OpenZip (apk)) {
+ Assert.IsTrue (zip.ContainsEntry ("kotlin/reflect/reflect.kotlin_builtins"), "reflect.kotlin_builtins should have been included.");
+ }
+ }
+ }
+
[Test]
[TestCase (1, -1)]
[TestCase (5, -1)]
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 304721be811..cacbe31e528 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
@@ -523,6 +523,10 @@ public static class KnownPackages
Id = "Xamarin.Kotlin.Stdlib.Common",
Version = "1.6.20.1"
};
+ public static Package Xamarin_Kotlin_Reflect = new Package {
+ Id = "Xamarin.Kotlin.Reflect",
+ Version = "1.9.10.2"
+ };
public static Package Acr_UserDialogs = new Package {
Id = "Acr.UserDialogs",
Version = "8.0.1",
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index 330b30f9a7e..ad337416ebb 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -358,11 +358,6 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
-
-
-
-
-
$(IntermediateOutputPath)assets\
@@ -2136,6 +2131,7 @@ because xbuild doesn't support framework reference assemblies.
CheckedBuild="$(_AndroidCheckedBuild)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
ExcludeFiles="@(AndroidPackagingOptionsExclude)"
+ IncludeFiles="@(AndroidPackagingOptionsInclude)"
ZipFlushFilesLimit="$(_ZipFlushFilesLimit)"
ZipFlushSizeLimit="$(_ZipFlushSizeLimit)"
UseAssemblyStore="$(AndroidUseAssemblyStore)">
@@ -2171,6 +2167,7 @@ because xbuild doesn't support framework reference assemblies.
CheckedBuild="$(_AndroidCheckedBuild)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
ExcludeFiles="@(AndroidPackagingOptionsExclude)"
+ IncludeFiles="@(AndroidPackagingOptionsInclude)"
ZipFlushFilesLimit="$(_ZipFlushFilesLimit)"
ZipFlushSizeLimit="$(_ZipFlushSizeLimit)"
UseAssemblyStore="$(AndroidUseAssemblyStore)">