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)">