From 941e04c22546227c5e1ae0eca9cda30740d14f9e Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Thu, 1 Jun 2023 11:13:49 -0400 Subject: [PATCH] [XABT] Prefer `SupportedOSPlatformVersion` over `minSdkVersion` (#8026) Fixes: https://github.com/xamarin/xamarin-android/issues/8040 I noticed that we were not writing a supported (by us) `//uses-sdk/@android:minSdkVersion` value to the generated `AndroidManifest.xml` file for projects which included a manifest that declared a `targetSdkVersion`. In these cases, we would always write a `//uses-sdk/@android:minSdkVersion` value of `19` to `AndroidManifest.xml`, as that was the minimum API level the Android NDK supported. (This was also a mistake, as *MonoVM* has API-21 as the minimum supported target. This mistake is corrected.) Fix this to always use the value of `$(SupportedOSPlatformVersion)` as the `minSdkVersion` attribute in the `AndroidManifest.xml` file. If this value is not explicitly set in the project file, it will now default to `$(AndroidMinimumSupportedApiLevel)` instead of `$(TargetPlatformVersion)`. The `XA4216` error/warning code has been expanded+repurposed. The warning that would display when the `minSdkVersion` attribute in `AndroidManifest.xml` was less than our minimum supported API level has been converted into an error: error XA4216: The deployment target '19' is not supported (the minimum is '21'). Please increase (or remove) the //uses-sdk/@android:minSdkVersion value in your project file. A similar error is now reported when `$(SupportedOSPlatformVersion)` is less than `$(AndroidMinimumSupportedApiLevel)` error XA4216: The deployment target '19' is not supported (the minimum is '21'). Please increase the $(SupportedOSPlatformVersion) property value in your project file. An error condition has been added for when `//uses-sdk/@android:minSdkVersion` in `AndroidManifest.xml` does not match `$(SupportedOSPlatformVersion)`: error XA1036: AndroidManifest.xml //uses-sdk/@android:minSdkVersion '19' does not match the $(SupportedOSPlatformVersion) value '21.0' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed). Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist). A few multidex related tests have been updated/removed as they are only valid with a `minSdkVersion` of 20 or lower. --- Configuration.props | 2 +- Documentation/guides/messages/README.md | 4 +- Documentation/guides/messages/xa1036.md | 27 +++ Documentation/guides/messages/xa4216.md | 34 +++- build-tools/scripts/NDKVersionInfo.targets | 15 -- build-tools/scripts/XABuildConfig.cs.in | 2 +- .../xaprepare/Application/KnownProperties.cs | 1 + .../Application/Properties.Defaults.cs.in | 1 + .../xaprepare/Steps/Step_GenerateFiles.cs | 2 +- .../xaprepare/xaprepare/xaprepare.targets | 1 + ...soft.Android.Sdk.DefaultProperties.targets | 3 + .../Properties/Resources.Designer.cs | 21 ++- .../Properties/Resources.resx | 21 ++- .../Tasks/GenerateJavaStubs.cs | 8 +- .../Tasks/GetJavaPlatformJar.cs | 29 ++-- .../Xamarin.Android.Build.Tests/AotTests.cs | 2 +- .../Xamarin.Android.Build.Tests/BuildTest.cs | 23 +-- .../Xamarin.Android.Build.Tests/BuildTest2.cs | 60 +------ .../ManifestTest.cs | 157 +++++++++++++++++- .../PackagingTest.cs | 2 +- .../Tasks/GetDependenciesTests.cs | 2 +- .../Xamarin.Android.Build.Tests/XASdkTests.cs | 30 ---- .../Android/XASdkProject.cs | 2 - .../XamarinAndroidApplicationProject.cs | 15 +- .../Common/DotNetStandard.cs | 4 - .../Common/DotNetXamarinProject.cs | 16 +- .../Common/XamarinProject.cs | 7 +- .../Utilities/ManifestDocument.cs | 31 ++-- .../Utilities/MonoAndroidHelper.cs | 17 ++ .../Xamarin.Android.Tooling.targets | 1 + .../BuildTests/Properties/AndroidManifest.xml | 2 +- .../Tests/DebuggingTest.cs | 3 +- .../Tests/InstallAndRunTests.cs | 10 +- .../Tests/MonoAndroidExportTest.cs | 4 +- 34 files changed, 355 insertions(+), 204 deletions(-) create mode 100644 Documentation/guides/messages/xa1036.md delete mode 100644 build-tools/scripts/NDKVersionInfo.targets diff --git a/Configuration.props b/Configuration.props index 4e40ffde653..46cf077de5f 100644 --- a/Configuration.props +++ b/Configuration.props @@ -22,7 +22,7 @@ v4.7.2 v4.7.1 26 - + 21 33 diff --git a/Documentation/guides/messages/README.md b/Documentation/guides/messages/README.md index 235d1d866f9..af06e1bd243 100644 --- a/Documentation/guides/messages/README.md +++ b/Documentation/guides/messages/README.md @@ -135,6 +135,8 @@ or 'Help->Report a Problem' in Visual Studio for Mac. + [XA1032](xa1032.md):Failed to resolve '{0}' from '{1}'. Please check your `AndroidHttpClientHandlerType` setting. + [XA1033](xa1033.md): Could not resolve '{0}'. Please check your `AndroidHttpClientHandlerType` setting. + [XA1035](xa1035.md): The 'BundleAssemblies' property is deprecated and it has no effect on the application build. Equivalent functionality is implemented by the 'AndroidUseAssemblyStore' and 'AndroidEnableAssemblyCompression' properties. ++ [XA1036](xa1036.md): AndroidManifest.xml //uses-sdk/@android:minSdkVersion '29' does not match the $(SupportedOSPlatformVersion) value '21' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed). +Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist). ## XA2xxx: Linker @@ -163,7 +165,7 @@ or 'Help->Report a Problem' in Visual Studio for Mac. + XA4213: The type '{type}' must provide a public default constructor + [XA4214](xa4214.md): The managed type \`Library1.Class1\` exists in multiple assemblies: Library1, Library2. Please refactor the managed type names in these assemblies so that they are not identical. + [XA4215](xa4215.md): The Java type \`com.contoso.library1.Class1\` is generated by more than one managed type. Please change the \[Register\] attribute so that the same Java type is not emitted. -+ [XA4216](xa4216.md): AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{min_sdk?.Value}' is less than API-{XABuildConfig.NDKMinimumApiAvailable}, this configuration is not supported. ++ [XA4216](xa4216.md): The deployment target '19' is not supported (the minimum is '21'). Please increase the $(SupportedOSPlatformVersion) property value in your project file. + XA4217: Cannot override Kotlin-generated method '{method}' because it is not a valid Java method name. This method can only be overridden from Kotlin. + [XA4218](xa4218.md): Unable to find //manifest/application/uses-library at path: {path} + XA4219: Cannot find binding generator for language {language} or {defaultLanguage}. diff --git a/Documentation/guides/messages/xa1036.md b/Documentation/guides/messages/xa1036.md new file mode 100644 index 00000000000..d566ebafdbb --- /dev/null +++ b/Documentation/guides/messages/xa1036.md @@ -0,0 +1,27 @@ +--- +title: Xamarin.Android error XA1036 +description: XA1036 error code +ms.date: 5/22/2023 +--- +# Xamarin.Android error XA1036 + +## Example messages + +``` +error XA1036: AndroidManifest.xml //uses-sdk/@android:minSdkVersion '29' does not match the $(SupportedOSPlatformVersion) value '21' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed). +Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist). +``` + +## Issue + +This error indicates that you have a mismatch between the minSdkVersion +element set in your AndroidManifest.xml and the $(SupportedOSPlatformVersion) +property value in your project file. + + +## Solution + +Raise the value of `//uses-sdk/@android:minSdkVersion` in *AndroidManifest.xml* +to match the $(SupportedOSPlatformVersion), or remove `//uses-sdk/@android:minSdkVersion` +from *AndroidManifest.xml* and set $(SupportedOSPlatformVersion) in your project +file if it is unset. diff --git a/Documentation/guides/messages/xa4216.md b/Documentation/guides/messages/xa4216.md index 32c28b67048..bae557715fb 100644 --- a/Documentation/guides/messages/xa4216.md +++ b/Documentation/guides/messages/xa4216.md @@ -1,23 +1,39 @@ --- -title: Xamarin.Android warning XA4216 -description: XA4216 warning code +title: Xamarin.Android error XA4216 +description: XA4216 error code ms.date: 02/07/2019 --- -# Xamarin.Android warning XA4216 +# Xamarin.Android error XA4216 ## Example messages ``` -warning XA4216: AndroidManifest.xml //uses-sdk/@android:minSdkVersion '15' is less than API-16, this configuration is not supported. +error XA4216: The deployment target '19' is not supported (the minimum is '21'). Please increase the $(SupportedOSPlatformVersion) property value in your project file. +``` + +``` +error XA4216: The deployment target '19' is not supported (the minimum is '21'). Please increase (or remove) the //uses-sdk/@android:minSdkVersion value in your AndroidManifest.xml. +``` + +``` +warning XA4216: AndroidManifest.xml //uses-sdk/@android:targetSdkVersion '19' is less than API-21, this configuration is not supported. ``` ## Issue -This warning indicates your application is targeting an API level that -Xamarin.Android does not support. +This error or warning indicates your application is targeting an unsupported API level. ## Solution -Raise the value of `//uses-sdk/@android:minSdkVersion` or -`//uses-sdk/@android:targetSdkVersion` in *AndroidManifest.xml* to a -higher API level that is supported. +Edit your csproj and increase the '$(SupportedOSPlatformVersion)' +property value to something greater than or equal to the minimum supported version. + +or + +Edit your AndroidManifest.xml and remove `//uses-sdk/@android:minSdkVersion`, +or increase it to something greater than or equal to the minimum supported version. + +or + +Edit your AndroidManifest.xml and remove `//uses-sdk/@android:targetSdkVersion`, +or increase it to something greater than or equal to the minimum supported version. diff --git a/build-tools/scripts/NDKVersionInfo.targets b/build-tools/scripts/NDKVersionInfo.targets deleted file mode 100644 index 8cdccd8b95f..00000000000 --- a/build-tools/scripts/NDKVersionInfo.targets +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - diff --git a/build-tools/scripts/XABuildConfig.cs.in b/build-tools/scripts/XABuildConfig.cs.in index dcd82345540..1dc9f717dc7 100644 --- a/build-tools/scripts/XABuildConfig.cs.in +++ b/build-tools/scripts/XABuildConfig.cs.in @@ -11,7 +11,7 @@ namespace Xamarin.Android.Tools public const string XamarinAndroidVersion = "@XAMARIN_ANDROID_VERSION@"; public const string XamarinAndroidCommitHash = "@XAMARIN_ANDROID_COMMIT_HASH@"; public const string XamarinAndroidBranch = "@XAMARIN_ANDROID_BRANCH@"; - public const int NDKMinimumApiAvailable = @NDK_MINIMUM_API_AVAILABLE@; + public const int AndroidMinimumDotNetApiLevel = @ANDROID_DEFAULT_MINIMUM_DOTNET_API_LEVEL@; public const int AndroidLatestStableApiLevel = @ANDROID_LATEST_STABLE_API_LEVEL@; public const int AndroidLatestUnstableApiLevel = @ANDROID_LATEST_UNSTABLE_API_LEVEL@; public const int AndroidDefaultTargetDotnetApiLevel = @ANDROID_DEFAULT_TARGET_DOTNET_API_LEVEL@; diff --git a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs index 50768d4a6a4..a4df4d587f1 100644 --- a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs +++ b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs @@ -5,6 +5,7 @@ static class KnownProperties public const string AndroidCmakeUrlPrefix = "AndroidCmakeUrlPrefix"; public const string AndroidCmakeVersion = "AndroidCmakeVersion"; public const string AndroidCmakeVersionPath = "AndroidCmakeVersionPath"; + public const string AndroidMinimumDotNetApiLevel = "AndroidMinimumDotNetApiLevel"; public const string AndroidDefaultTargetDotnetApiLevel = "AndroidDefaultTargetDotnetApiLevel"; public const string AndroidLatestStableApiLevel = "AndroidLatestStableApiLevel"; public const string AndroidLatestUnstableApiLevel = "AndroidLatestUnstableApiLevel"; diff --git a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in index 486b859d669..4f461e18369 100644 --- a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in +++ b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in @@ -9,6 +9,7 @@ namespace Xamarin.Android.Prepare properties.Add (KnownProperties.AndroidCmakeUrlPrefix, StripQuotes ("@AndroidCmakeUrlPrefix@")); properties.Add (KnownProperties.AndroidCmakeVersion, StripQuotes ("@AndroidCmakeVersion@")); properties.Add (KnownProperties.AndroidCmakeVersionPath, StripQuotes (@"@AndroidCmakeVersionPath@")); + properties.Add (KnownProperties.AndroidMinimumDotNetApiLevel, StripQuotes ("@AndroidMinimumDotNetApiLevel@")); properties.Add (KnownProperties.AndroidDefaultTargetDotnetApiLevel, StripQuotes ("@AndroidDefaultTargetDotnetApiLevel@")); properties.Add (KnownProperties.AndroidLatestStableApiLevel, StripQuotes ("@AndroidLatestStableApiLevel@")); properties.Add (KnownProperties.AndroidLatestUnstableApiLevel, StripQuotes ("@AndroidLatestUnstableApiLevel@")); diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs index 07684b87ff9..6fe698f44ea 100644 --- a/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs +++ b/build-tools/xaprepare/xaprepare/Steps/Step_GenerateFiles.cs @@ -168,7 +168,6 @@ GeneratedFile Get_XABuildConfig_cs (Context context) var replacements = new Dictionary (StringComparer.Ordinal) { { "@NDK_REVISION@", context.BuildInfo.NDKRevision }, { "@NDK_RELEASE@", BuildAndroidPlatforms.AndroidNdkVersion }, - { "@NDK_MINIMUM_API_AVAILABLE@", context.BuildInfo.NDKMinimumApiAvailable }, { "@NDK_VERSION_MAJOR@", context.BuildInfo.NDKVersionMajor }, { "@NDK_VERSION_MINOR@", context.BuildInfo.NDKVersionMinor }, { "@NDK_VERSION_MICRO@", context.BuildInfo.NDKVersionMicro }, @@ -177,6 +176,7 @@ GeneratedFile Get_XABuildConfig_cs (Context context) { "@NDK_X86_API@", BuildAndroidPlatforms.NdkMinimumAPILegacy32.ToString ().ToString () }, { "@NDK_X86_64_API@", BuildAndroidPlatforms.NdkMinimumAPI.ToString ().ToString () }, { "@XA_SUPPORTED_ABIS@", context.Properties.GetRequiredValue (KnownProperties.AndroidSupportedTargetJitAbis).Replace (':', ';') }, + { "@ANDROID_DEFAULT_MINIMUM_DOTNET_API_LEVEL@", context.Properties.GetRequiredValue (KnownProperties.AndroidMinimumDotNetApiLevel) }, { "@ANDROID_DEFAULT_TARGET_DOTNET_API_LEVEL@", context.Properties.GetRequiredValue (KnownProperties.AndroidDefaultTargetDotnetApiLevel) }, { "@ANDROID_LATEST_STABLE_API_LEVEL@", context.Properties.GetRequiredValue (KnownProperties.AndroidLatestStableApiLevel) }, { "@ANDROID_LATEST_UNSTABLE_API_LEVEL@", context.Properties.GetRequiredValue (KnownProperties.AndroidLatestUnstableApiLevel) }, diff --git a/build-tools/xaprepare/xaprepare/xaprepare.targets b/build-tools/xaprepare/xaprepare/xaprepare.targets index 37ecd5712bf..3ba0ccfd582 100644 --- a/build-tools/xaprepare/xaprepare/xaprepare.targets +++ b/build-tools/xaprepare/xaprepare/xaprepare.targets @@ -43,6 +43,7 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index 7767b0e23c8..0221899748f 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -25,6 +25,9 @@ --> <_GetChildProjectCopyToPublishDirectoryItems>false true + + + $(AndroidMinimumSupportedApiLevel) $(SupportedOSPlatformVersion).0 diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index a086eaa35f3..a5f6eb2399f 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs @@ -945,7 +945,7 @@ public static string XA4215_Details { } /// - /// Looks up a localized string similar to AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{0}' is less than API-{1}, this configuration is not supported.. + /// Looks up a localized string similar to The deployment target '{1}' is not supported (the minimum is '{2}'). Please increase (or remove) the //uses-sdk/@android:minSdkVersion value in your AndroidManifest.xml.. /// public static string XA4216_MinSdkVersion { get { @@ -953,6 +953,15 @@ public static string XA4216_MinSdkVersion { } } + /// + /// Looks up a localized string similar to The deployment target '{1}' is not supported (the minimum is '{2}'). Please increase the $(SupportedOSPlatformVersion) property value in your project file.. + /// + public static string XA4216_SupportedOSPlatformVersion { + get { + return ResourceManager.GetString("XA4216_SupportedOSPlatformVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to AndroidManifest.xml //uses-sdk/@android:targetSdkVersion '{0}' is less than API-{1}, this configuration is not supported.. /// @@ -1485,5 +1494,15 @@ public static string XA1034 { return ResourceManager.GetString("XA1034", resourceCulture); } } + + /// + /// Looks up a localized string similar to AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{0}' does not match the $(SupportedOSPlatformVersion) value '{1}' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed). + ///Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist).. + /// + public static string XA1036 { + get { + return ResourceManager.GetString("XA1036", resourceCulture); + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index cdda2379449..768256ab6e6 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -497,6 +497,13 @@ Please change the value to an assembly-qualifed type name which inherits from '{ The 'BundleAssemblies' property is deprecated and it has no effect on the application build. Equivalent functionality is implemented by the 'AndroidUseAssemblyStore' and 'AndroidEnableAssemblyCompression' properties. The following are literal names and should not be translated: 'BundleAssemblies', 'AndroidUseAssemblyStore', 'AndroidEnableAssemblyCompression' + + AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{0}' does not match the $(SupportedOSPlatformVersion) value '{1}' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed). +Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist). + The following are literal names and should not be translated: AndroidManifest.xml, //uses-sdk/@android:minSdkVersion, $(SupportedOSPlatformVersion) +{0} - The minimum SDK version number +{1} - The SupportedOSPlatformVersion property value + Use of AppDomain.CreateDomain() detected in assembly: {0}. .NET 6 and higher will only support a single AppDomain, so this API will no longer be available in Xamarin.Android once .NET 6 is released. The following are literal names and should not be translated: AppDomain.CreateDomain(), AppDomain @@ -607,12 +614,18 @@ Please change the value to an assembly-qualifed type name which inherits from '{ The two spaces indentation at the beginning of this message should be preserved in the translations where possible. {0} - The generated Java type name {1} - The managed type name + + + The deployment target '{0}' is not supported (the minimum is '{1}'). Please increase the $(SupportedOSPlatformVersion) property value in your project file. + The following are literal names and should not be translated: Microsoft.Android, $(SupportedOSPlatformVersion) +{0} - The value of $(SupportedOSPlatformVersion) +{1} - The minimum $(SupportedOSPlatformVersion) supported by this version of the SDK - AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{0}' is less than API-{1}, this configuration is not supported. - The following are literal names and should not be translated: AndroidManifest.xml, //uses-sdk/@android:minSdkVersion, API-{1} -{0} - The minimum SDK version number -{1} - The API version number + The deployment target '{0}' is not supported (the minimum is '{1}'). Please increase (or remove) the //uses-sdk/@android:minSdkVersion value in your AndroidManifest.xml. + The following are literal names and should not be translated: Microsoft.Android, //uses-sdk/@android:minSdkVersion, AndroidManifest.xml +{0} - The value of //uses-sdk/@android:minSdkVersion +{1} - The minimum $(SupportedOSPlatformVersion) supported by this version of the SDK AndroidManifest.xml //uses-sdk/@android:targetSdkVersion '{0}' is less than API-{1}, this configuration is not supported. diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index e1c36a308d8..69584ae7893 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -329,12 +329,6 @@ void Run (DirectoryAssemblyResolver res, bool useMarshalMethods) Log.LogCodedError ("XA4215", Properties.Resources.XA4215_Details, kvp.Key, typeName); } - // NOTE: $(SupportedOSPlatformVersion) will potentially be 21.0 - string minSdkVersion = null; - if (Version.TryParse (SupportedOSPlatformVersion, out var version)) { - minSdkVersion = version.Major.ToString (); - } - // Step 3 - Merge [Activity] and friends into AndroidManifest.xml var manifest = new ManifestDocument (ManifestTemplate) { PackageName = PackageName, @@ -344,7 +338,7 @@ void Run (DirectoryAssemblyResolver res, bool useMarshalMethods) Resolver = res, SdkDir = AndroidSdkDir, TargetSdkVersion = AndroidSdkPlatform, - MinSdkVersion = minSdkVersion, + MinSdkVersion = MonoAndroidHelper.ConvertSupportedOSPlatformVersionToApiLevel (SupportedOSPlatformVersion).ToString (), Debug = Debug, MultiDex = MultiDex, NeedsInternet = NeedsInternet, diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetJavaPlatformJar.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetJavaPlatformJar.cs index 2757d54b138..171913f78e3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetJavaPlatformJar.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetJavaPlatformJar.cs @@ -25,6 +25,8 @@ public class GetJavaPlatformJar : AndroidTask public bool DesignTimeBuild { get; set; } + public string SupportedOSPlatformVersion { get; set; } + [Output] public string JavaPlatformJarPath { get; set; } @@ -37,6 +39,11 @@ public override bool RunTask () XAttribute target_sdk = null; + int supportedOsPlatformVersionAsInt = MonoAndroidHelper.ConvertSupportedOSPlatformVersionToApiLevel (SupportedOSPlatformVersion); + if (supportedOsPlatformVersionAsInt < XABuildConfig.AndroidMinimumDotNetApiLevel) { + Log.LogCodedError ("XA4216", Properties.Resources.XA4216_SupportedOSPlatformVersion, supportedOsPlatformVersionAsInt, XABuildConfig.AndroidMinimumDotNetApiLevel); + } + // Look for targetSdkVersion in the user's AndroidManifest.xml if (!string.IsNullOrWhiteSpace (AndroidManifest)) { if (!File.Exists (AndroidManifest)) { @@ -58,22 +65,24 @@ public override bool RunTask () platform = target_sdk.Value; var min_sdk = uses_sdk.Attribute (androidNs + "minSdkVersion"); - if (min_sdk != null && (!int.TryParse (min_sdk.Value, out int minSdkVersion) || minSdkVersion < XABuildConfig.NDKMinimumApiAvailable)) { - Log.LogWarningForXmlNode ( - code: "XA4216", - file: AndroidManifest, - node: min_sdk, - message: Properties.Resources.XA4216_MinSdkVersion, - messageArgs: new object [] { min_sdk?.Value, XABuildConfig.NDKMinimumApiAvailable } - ); + if (min_sdk != null) { + var failedToParseMinSdk = !int.TryParse (min_sdk.Value, out int minSdkVersion); + + if (failedToParseMinSdk || minSdkVersion < XABuildConfig.AndroidMinimumDotNetApiLevel) { + Log.LogCodedError ("XA4216", Properties.Resources.XA4216_MinSdkVersion, min_sdk?.Value, XABuildConfig.AndroidMinimumDotNetApiLevel); + } + + if (failedToParseMinSdk || minSdkVersion != supportedOsPlatformVersionAsInt) { + Log.LogCodedError ("XA1036", Properties.Resources.XA1036, min_sdk?.Value, SupportedOSPlatformVersion); + } } - if (target_sdk != null && (!int.TryParse (target_sdk.Value, out int targetSdkVersion) || targetSdkVersion < XABuildConfig.NDKMinimumApiAvailable)) { + if (target_sdk != null && (!int.TryParse (target_sdk.Value, out int targetSdkVersion) || targetSdkVersion < XABuildConfig.AndroidMinimumDotNetApiLevel)) { Log.LogWarningForXmlNode ( code: "XA4216", file: AndroidManifest, node: target_sdk, message: Properties.Resources.XA4216_TargetSdkVersion, - messageArgs: new object [] { target_sdk?.Value, XABuildConfig.NDKMinimumApiAvailable } + messageArgs: new object [] { target_sdk?.Value, XABuildConfig.AndroidMinimumDotNetApiLevel } ); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs index 48ea5d3d6f4..db875ef9278 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs @@ -154,7 +154,7 @@ public void BuildAotApplicationWithNdkAndBundleAndÜmläüts (string supportedAb // Set //uses-sdk/@android:minSdkVersion so that LLVM uses the right libc.so proj.AndroidManifest = $@" - + "; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 0502d83076d..d87c9421df4 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1747,27 +1747,6 @@ public void BuildOutsideVisualStudio () } } - [Test] - public void WarningForMinSdkVersion () - { - int minSdkVersion = XABuildConfig.NDKMinimumApiAvailable; - int tooLowSdkVersion = minSdkVersion - 1; - var proj = new XamarinAndroidApplicationProject { - MinSdkVersion = tooLowSdkVersion.ToString (), - TargetSdkVersion = null, - }; - using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) { - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - Assert.IsTrue ( - StringAssertEx.ContainsText ( - b.LastBuildOutput, - $"warning XA4216: AndroidManifest.xml //uses-sdk/@android:minSdkVersion '{tooLowSdkVersion}' is less than API-{minSdkVersion}, this configuration is not supported." - ), - $"Should receive a warning when //uses-sdk/@android:minSdkVersion=\"{tooLowSdkVersion}\"" - ); - } - } - [Test] public void RemoveOldMonoPackageManager () { @@ -2313,7 +2292,7 @@ public class MainActivity : Activity if (int.TryParse (apiLevel, out int a) && a < maxApiLevel) disabledIssues += ",OldTargetApi"; proj.SetProperty ("AndroidLintDisabledIssues", disabledIssues); - proj.MinSdkVersion = "24"; + proj.SupportedOSPlatformVersion = "24"; proj.TargetSdkVersion = apiLevel; Assert.IsTrue (b.Build (proj), "Build should have succeeded."); StringAssertEx.DoesNotContain ("XA0102", b.LastBuildOutput, "Output should not contain any XA0102 warnings"); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index 06858a6e456..1569d1bdcb0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -1171,17 +1171,6 @@ XamarinAndroidApplicationProject CreateMultiDexRequiredApplication (string debug return proj; } - [Test] - [Category ("Minor")] - public void BuildApplicationOver65536Methods () - { - var proj = CreateMultiDexRequiredApplication (); - using (var b = CreateApkBuilder ()) { - b.ThrowOnBuildFailure = false; - Assert.IsFalse (b.Build (proj), "Without MultiDex option, build should fail"); - } - } - [Test] public void CreateMultiDexWithSpacesInConfig () { @@ -1213,11 +1202,12 @@ public void BuildMultiDexApplication () Assert.IsTrue (b.Build (proj), "Build should have succeeded."); Assert.IsTrue (File.Exists (Path.Combine (Root, b.ProjectDirectory, intermediateDir, "android/bin/classes.dex")), "multidex-ed classes.zip exists"); - var multidexKeepPath = Path.Combine (Root, b.ProjectDirectory, intermediateDir, "multidex.keep"); - Assert.IsTrue (File.Exists (multidexKeepPath), "multidex.keep exists"); - Assert.IsTrue (File.ReadAllLines (multidexKeepPath).Length > 1, "multidex.keep must contain more than one line."); - if (!Builder.UseDotNet) + if (!Builder.UseDotNet) { + var multidexKeepPath = Path.Combine (Root, b.ProjectDirectory, intermediateDir, "multidex.keep"); + Assert.IsTrue (File.Exists (multidexKeepPath), "multidex.keep exists"); + Assert.IsTrue (File.ReadAllLines (multidexKeepPath).Length > 1, "multidex.keep must contain more than one line."); Assert.IsTrue (b.LastBuildOutput.ContainsText (Path.Combine (proj.TargetFrameworkVersion, "mono.android.jar")), proj.TargetFrameworkVersion + "/mono.android.jar should be used."); + } Assert.IsFalse (b.LastBuildOutput.ContainsText ("Duplicate zip entry"), "Should not get warning about [META-INF/MANIFEST.MF]"); } } @@ -1236,13 +1226,11 @@ public void BuildAfterMultiDexIsNotRequired () Assert.IsTrue (b.Build (proj), "Build should have succeeded."); FileAssert.Exists (Path.Combine (androidBinDir, "classes.dex")); FileAssert.Exists (Path.Combine (androidBinDir, "classes2.dex")); - FileAssert.Exists (Path.Combine (androidBinDir, "classes3.dex")); using (var zip = ZipHelper.OpenZip (apkPath)) { var entries = zip.Select (e => e.FullName).ToList (); Assert.IsTrue (entries.Contains ("classes.dex"), "APK must contain `classes.dex`."); Assert.IsTrue (entries.Contains ("classes2.dex"), "APK must contain `classes2.dex`."); - Assert.IsTrue (entries.Contains ("classes3.dex"), "APK must contain `classes3.dex`."); } //Now build project again after it no longer requires multidex, remove the *HUGE* AndroidJavaSource build items @@ -1259,44 +1247,6 @@ public void BuildAfterMultiDexIsNotRequired () var entries = zip.Select (e => e.FullName).ToList (); Assert.IsTrue (entries.Contains ("classes.dex"), "APK must contain `classes.dex`."); Assert.IsFalse (entries.Contains ("classes2.dex"), "APK must *not* contain `classes2.dex`."); - Assert.IsFalse (entries.Contains ("classes3.dex"), "APK must *not* contain `classes3.dex`."); - } - } - } - - [Test] - public void MultiDexCustomMainDexFileList ([Values ("19", "21")] string minSdkVersion) - { - var expected = new [] { - "android/support/multidex/ZipUtil$CentralDirectory.class", - "android/support/multidex/MultiDexApplication.class", - "android/support/multidex/MultiDex$V19.class", - "android/support/multidex/ZipUtil.class", - "android/support/multidex/MultiDexExtractor$1.class", - "android/support/multidex/MultiDexExtractor.class", - "android/support/multidex/MultiDex.class", - "MyTest.class", - }; - var proj = CreateMultiDexRequiredApplication (); - proj.MinSdkVersion = minSdkVersion; - proj.TargetSdkVersion = null; - proj.SetProperty ("AndroidEnableMultiDex", "True"); - proj.OtherBuildItems.Add (new BuildItem ("MultiDexMainDexList", "mymultidex.keep") { TextContent = () => "MyTest.class", Encoding = Encoding.ASCII }); - proj.OtherBuildItems.Add (new BuildItem ("AndroidJavaSource", "MyTest.java") { TextContent = () => "public class MyTest {}", Encoding = Encoding.ASCII }); - using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) { - Assert.IsTrue (b.Build (proj), "build should succeed. Run will fail."); - string androidBinDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin"); - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - FileAssert.Exists (Path.Combine (androidBinDir, "classes.dex")); - FileAssert.Exists (Path.Combine (androidBinDir, "classes2.dex")); - if (minSdkVersion == "21") { - //NOTE: d8/r8 does not support custom dex list files in this case - return; - } - //NOTE: d8 has the list in a different order, so we should do an unordered comparison - var actual = File.ReadAllLines (Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "multidex.keep")); - foreach (var item in expected) { - Assert.IsTrue (actual.Contains (item), $"multidex.keep did not contain `{item}`"); } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs index 4559fada920..7642eeabe6b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs @@ -8,6 +8,8 @@ using System.Xml.XPath; using Xamarin.Tools.Zip; using System.Collections.Generic; +using Xamarin.Android.Tasks; +using Xamarin.Android.Tools; namespace Xamarin.Android.Build.Tests { @@ -439,7 +441,7 @@ public void DirectBootAwareAttribute () /* pattern */ "{abi}{minSDK:00}{versionCode:000}", /* props */ null, /* shouldBuild */ true, - /* expected */ "219012;319012", + /* expected */ "221012;321012", }, new object[] { /* seperateApk */ true, @@ -449,7 +451,7 @@ public void DirectBootAwareAttribute () /* pattern */ "{abi}{minSDK:00}{screen}{versionCode:000}", /* props */ "screen=24", /* shouldBuild */ true, - /* expected */ "21924012;31924012", + /* expected */ "22124012;32124012", }, new object[] { /* seperateApk */ true, @@ -459,7 +461,7 @@ public void DirectBootAwareAttribute () /* pattern */ "{abi}{minSDK:00}{screen}{foo:0}{versionCode:000}", /* props */ "screen=24;foo=$(Foo)", /* shouldBuild */ true, - /* expected */ "219241012;319241012", + /* expected */ "221241012;321241012", }, new object[] { /* seperateApk */ true, @@ -469,7 +471,7 @@ public void DirectBootAwareAttribute () /* pattern */ "{abi}{minSDK:00}{screen}{foo:00}{versionCode:000}", /* props */ "screen=24;foo=$(Foo)", /* shouldBuild */ false, - /* expected */ "2192401012;3192401012", + /* expected */ "2212401012;3212401012", }, }; @@ -479,7 +481,8 @@ public void VersionCodeTests (bool seperateApk, string abis, string versionCode, { var proj = new XamarinAndroidApplicationProject () { IsRelease = true, - MinSdkVersion = null, + MinSdkVersion = "21", + SupportedOSPlatformVersion = "21.0", }; proj.SetProperty ("Foo", "1"); proj.SetProperty ("GenerateApplicationManifest", "false"); // Disable $(AndroidVersionCode) support @@ -964,5 +967,149 @@ public void ExportedErrorMessage () Assert.IsTrue (b.LastBuildOutput.ContainsText ($"AndroidManifest.xml(12,5): java{extension} error AMM0000:"), "Should recieve AMM0000 error"); Assert.IsTrue (b.LastBuildOutput.ContainsText ("Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported`"), "Should recieve AMM0000 error"); } + + static object [] SupportedOSTestSources = new object [] { + new object[] { + /* minSdkVersion */ "", + /* removeUsesSdk */ true, + }, + new object[] { + /* minSdkVersion */ "", + /* removeUsesSdk */ false, + }, + new object[] { + /* minSdkVersion */ "21.0", + /* removeUsesSdk */ true, + }, + new object[] { + /* minSdkVersion */ "31", + /* removeUsesSdk */ false, + }, + new object[] { + /* minSdkVersion */ $"{XABuildConfig.AndroidDefaultTargetDotnetApiLevel}.0", + /* removeUsesSdk */ false, + }, + }; + [Test] + [TestCaseSource(nameof (SupportedOSTestSources))] + public void SupportedOSPlatformVersion (string minSdkVersion, bool removeUsesSdkElement) + { + var proj = new XamarinAndroidApplicationProject { + EnableDefaultItems = true, + SupportedOSPlatformVersion = minSdkVersion, + }; + + // An empty SupportedOSPlatformVersion property will default to AndroidMinimumDotNetApiLevel + if (string.IsNullOrEmpty (minSdkVersion)) { + minSdkVersion = XABuildConfig.AndroidMinimumDotNetApiLevel.ToString (); + } + + if (removeUsesSdkElement) { + proj.AndroidManifest = $@" + + + +"; + } + + // Call AccessibilityTraversalAfter from API level 22 + // https://developer.android.com/reference/android/view/View#getAccessibilityTraversalAfter() + proj.MainActivity = proj.DefaultMainActivity.Replace ("button!.Click", "button!.AccessibilityTraversalAfter.ToString ();\nbutton!.Click"); + + var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Build (proj), "`dotnet build` should succeed"); + + var minSdkVersionInt = MonoAndroidHelper.ConvertSupportedOSPlatformVersionToApiLevel (minSdkVersion); + if (minSdkVersionInt < 22) { + StringAssertEx.Contains ("warning CA1416", builder.LastBuildOutput, "Should get warning about Android 22 API"); + } else { + builder.AssertHasNoWarnings (); + } + + var manifestPath = Path.Combine (Root, builder.ProjectDirectory, proj.IntermediateOutputPath, "android", "AndroidManifest.xml"); + FileAssert.Exists (manifestPath); + var manifest = XDocument.Load (manifestPath); + XNamespace ns = "http://schemas.android.com/apk/res/android"; + Assert.AreEqual (minSdkVersionInt.ToString (), manifest.Root.Element ("uses-sdk").Attribute (ns + "minSdkVersion").Value); + } + + static object [] SupportedOSErrorsTestSources = new object [] { + new object[] { + /* minSdkVersion */ "", + /* supportedOSPlatVers */ "", + }, + new object[] { + /* minSdkVersion */ "19", + /* supportedOSPlatVers */ "", + }, + new object[] { + /* minSdkVersion */ $"{XABuildConfig.AndroidDefaultTargetDotnetApiLevel}", + /* supportedOSPlatVers */ "", + }, + new object[] { + /* minSdkVersion */ "", + /* supportedOSPlatVers */ "19.0", + }, + new object[] { + /* minSdkVersion */ "19", + /* supportedOSPlatVers */ "19", + }, + new object[] { + /* minSdkVersion */ "29", + /* supportedOSPlatVers */ $"{XABuildConfig.AndroidDefaultTargetDotnetApiLevel}.0", + }, + }; + [Test] + [TestCaseSource(nameof (SupportedOSErrorsTestSources))] + public void SupportedOSPlatformVersionErrors (string minSdkVersion, string supportedOSPlatVers) + { + var proj = new XamarinAndroidApplicationProject { + EnableDefaultItems = true, + MinSdkVersion = minSdkVersion, + SupportedOSPlatformVersion = supportedOSPlatVers, + }; + + // Mismatch error can only occur when minSdkVersion is set in the manifest + bool wasMinSdkVersionEmpty = false; + + // Empty values will default to AndroidMinimumDotNetApiLevel + int minDotnetApiLevel = XABuildConfig.AndroidMinimumDotNetApiLevel; + if (string.IsNullOrEmpty (minSdkVersion)) { + wasMinSdkVersionEmpty = true; + minSdkVersion = minDotnetApiLevel.ToString (); + } + if (string.IsNullOrEmpty (supportedOSPlatVers)) { + supportedOSPlatVers = minDotnetApiLevel.ToString (); + } + var minSdkVersionInt = MonoAndroidHelper.ConvertSupportedOSPlatformVersionToApiLevel (minSdkVersion); + var supportedOSPlatVersInt = MonoAndroidHelper.ConvertSupportedOSPlatformVersionToApiLevel (supportedOSPlatVers); + var builder = CreateApkBuilder (); + builder.ThrowOnBuildFailure = false; + var buildResult = builder.Build (proj); + + if (supportedOSPlatVersInt < minDotnetApiLevel) { + Assert.IsFalse (buildResult, "SupportedOSPlatformVersion version too low, build should fail."); + StringAssertEx.Contains ("error XA4216", builder.LastBuildOutput, "Should get error XA4216."); + StringAssertEx.Contains ("Please increase the $(SupportedOSPlatformVersion) property value in your project file", + builder.LastBuildOutput, "Should get error about SupportedOSPlatformVersion being too low."); + } + + if (minSdkVersionInt < minDotnetApiLevel ) { + Assert.IsFalse (buildResult, "minSdkVersion too low, build should fail."); + StringAssertEx.Contains ("error XA4216", builder.LastBuildOutput, "Should get error XA4216."); + StringAssertEx.Contains ("Please increase (or remove) the //uses-sdk/@android:minSdkVersion value in your AndroidManifest.xml", + builder.LastBuildOutput, "Should get error about minSdkVersion being too low."); + } + + if (minSdkVersionInt != supportedOSPlatVersInt && !wasMinSdkVersionEmpty) { + Assert.IsFalse (buildResult, $"Min version mismatch {minSdkVersionInt} != {supportedOSPlatVersInt}, build should fail."); + StringAssertEx.Contains ("error XA1036", builder.LastBuildOutput, "Should get error about min version mismatch."); + } + + if (minSdkVersionInt == supportedOSPlatVersInt && minSdkVersionInt >= minDotnetApiLevel && supportedOSPlatVersInt >= minDotnetApiLevel) { + Assert.IsTrue (buildResult, "compatible min versions, build should succeed"); + } + } + } } 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 e4f349982f7..cd353e5001d 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 @@ -1007,7 +1007,7 @@ public void ExtractNativeLibsTrue () { var proj = new XamarinAndroidApplicationProject { // This combination produces android:extractNativeLibs="false" by default - MinSdkVersion = "23", + SupportedOSPlatformVersion = "23", ManifestMerger = "manifestmerger.jar", }; using (var b = CreateApkBuilder ()) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetDependenciesTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetDependenciesTests.cs index 707e8b9bc0b..c9454b6564c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetDependenciesTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetDependenciesTests.cs @@ -103,7 +103,7 @@ public void ManifestFileExists () var manifestFile = Path.Combine (path, "AndroidManifest.xml"); File.WriteAllText (manifestFile, @" - + "); task.PlatformToolsVersion = "26.0.3"; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index 2840c248451..9ba397c83ae 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -808,36 +808,6 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot, bo } } - - // TODO: - // Causes warning: D8 : warning : An API level of 32 is not supported by this compiler. Please use an API level of 31 or earlier - // Add a 32 parameter here when we get a newer version of r8. - [Test] - public void SupportedOSPlatformVersion ([Values (21, 31)] int minSdkVersion) - { - var proj = new XASdkProject { - SupportedOSPlatformVersion = minSdkVersion.ToString (), - }; - // Call AccessibilityTraversalAfter from API level 22 - // https://developer.android.com/reference/android/view/View#getAccessibilityTraversalAfter() - proj.MainActivity = proj.DefaultMainActivity.Replace ("button!.Click", "button!.AccessibilityTraversalAfter.ToString ();\nbutton!.Click"); - - var dotnet = CreateDotNetBuilder (proj); - Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); - - if (minSdkVersion < 22) { - StringAssertEx.Contains ("warning CA1416", dotnet.LastBuildOutput, "Should get warning about Android 22 API"); - } else { - dotnet.AssertHasNoWarnings (); - } - - var manifestPath = Path.Combine (FullProjectDirectory, proj.IntermediateOutputPath, "android", "AndroidManifest.xml"); - FileAssert.Exists (manifestPath); - var manifest = XDocument.Load (manifestPath); - XNamespace ns = "http://schemas.android.com/apk/res/android"; - Assert.AreEqual (minSdkVersion.ToString (), manifest.Root.Element ("uses-sdk").Attribute (ns + "minSdkVersion").Value); - } - [Test] public void DotNetBuildXamarinForms ([Values (true, false)] bool useInterpreter) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XASdkProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XASdkProject.cs index ec5b58011bf..6bdab872fd3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XASdkProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XASdkProject.cs @@ -79,8 +79,6 @@ public XASdkProject (string outputType = "Exe", [CallerMemberName] string packag Sources.Add (new BuildItem.Source ($"Resources\\Resource.designer{Language.DefaultExtension}") { TextContent = () => string.Empty }); } - protected override bool UseDotNet => true; - public string OutputPath => Path.Combine ("bin", Configuration, TargetFramework.ToLowerInvariant ()); public string IntermediateOutputPath => Path.Combine ("obj", Configuration, TargetFramework.ToLowerInvariant ()); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs index 1feaf5e2d8e..63ecc5231f1 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/XamarinAndroidApplicationProject.cs @@ -46,6 +46,7 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug" SetProperty (KnownProperties.ImplicitUsings, "enable"); SetProperty ("XamarinAndroidSupportSkipVerifyVersions", "True"); SetProperty ("_FastDeploymentDiagnosticLogging", "True"); + SupportedOSPlatformVersion = "21.0"; // Workaround for AndroidX, see: https://github.com/xamarin/AndroidSupportComponents/pull/239 Imports.Add (new Import (() => "Directory.Build.targets") { @@ -66,8 +67,8 @@ public XamarinAndroidApplicationProject (string debugConfigurationName = "Debug" SetProperty (ReleaseProperties, "AndroidLinkMode", "SdkOnly"); SetProperty (DebugProperties, KnownProperties.EmbedAssembliesIntoApk, "False", "'$(EmbedAssembliesIntoApk)' == ''"); SetProperty (ReleaseProperties, KnownProperties.EmbedAssembliesIntoApk, "True", "'$(EmbedAssembliesIntoApk)' == ''"); + MinSdkVersion = "19"; } - AndroidManifest = default_android_manifest; if (Builder.UseDotNet) { TargetSdkVersion = XABuildConfig.AndroidDefaultTargetDotnetApiLevel.ToString (); @@ -99,9 +100,17 @@ public virtual string DefaultMainActivity { public string TargetSdkVersion { get; set; } /// - /// Defaults to API 19 + /// Set this to add the `android:minSdkVersion` attribute to the AndroidManifest.xml file /// - public string MinSdkVersion { get; set; } = "19"; + public string MinSdkVersion { get; set; } + + /// + /// Defaults to 21.0 + /// + public string SupportedOSPlatformVersion { + get { return GetProperty (KnownProperties.SupportedOSPlatformVersion); } + set { SetProperty (KnownProperties.SupportedOSPlatformVersion, value); } + } string AotAssembliesPropertyName => Builder.UseDotNet ? KnownProperties.RunAOTCompilation : KnownProperties.AotAssemblies; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetStandard.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetStandard.cs index 7426e1d273d..e45acde512a 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetStandard.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetStandard.cs @@ -30,10 +30,6 @@ public string PackageTargetFallback { get { return GetProperty ("PackageTargetFallback"); } set { SetProperty ("PackageTargetFallback", value); } } - public string TargetFramework { - get { return GetProperty ("TargetFramework"); } - set { SetProperty ("TargetFramework", value); } - } /// /// Projects targeting net7.0 require ref/runtime packs on NuGet.org or dotnet6/dotnet7 diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetXamarinProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetXamarinProject.cs index a6ef3a10b97..acf8296b71e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetXamarinProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetXamarinProject.cs @@ -26,9 +26,9 @@ protected DotNetXamarinProject (string debugConfigurationName = "Debug", string SetProperty ("AssemblyName", () => AssemblyName ?? ProjectName); if (Builder.UseDotNet) { - SetProperty ("TargetFramework", "net8.0-android"); - SetProperty ("EnableDefaultItems", "false"); - SetProperty ("AppendTargetFrameworkToOutputPath", "false"); + TargetFramework = "net8.0-android"; + EnableDefaultItems = false; + AppendTargetFrameworkToOutputPath = false; } else { AddReferences ("System"); // default SetProperty ("Platform", "AnyCPU", "'$(Platform)' == ''"); @@ -77,7 +77,15 @@ public string IntermediateOutputPath { public string Sdk { get; set; } = "Microsoft.NET.Sdk"; - public bool EnableDefaultItems => false; + public bool EnableDefaultItems { + get { return string.Equals (GetProperty ("EnableDefaultItems"), "true", StringComparison.OrdinalIgnoreCase); } + set { SetProperty ("EnableDefaultItems", value.ToString ()); } + } + + public bool AppendTargetFrameworkToOutputPath { + get { return string.Equals (GetProperty ("AppendTargetFrameworkToOutputPath"), "true", StringComparison.OrdinalIgnoreCase); } + set { SetProperty ("AppendTargetFrameworkToOutputPath", value.ToString ()); } + } public void AddReferences (params string [] references) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs index 14b6a7d7783..e274ef9f638 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/XamarinProject.cs @@ -88,7 +88,7 @@ public XamarinProject (string debugConfigurationName = "Debug", string releaseCo Packages = new List (); Imports = new List (); - if (UseDotNet) { + if (Builder.UseDotNet) { //NOTE: for SDK-style projects, we need $(Configuration) set before Microsoft.NET.Sdk.targets Imports.Add (new Import ("Directory.Build.props") { TextContent = () => @@ -114,7 +114,10 @@ public void AddReference (XamarinProject other, string include = null) References.Add (new BuildItem.ProjectReference (include, other.ProjectName, other.ProjectGuid)); } - protected virtual bool UseDotNet => Builder.UseDotNet; + public string TargetFramework { + get { return GetProperty ("TargetFramework"); } + set { SetProperty ("TargetFramework", value); } + } public string GetProperty (string name) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index 3f648c5cfcb..05de2bd0c86 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -117,14 +117,19 @@ public string VersionCode { public bool HasVersionCode => doc.Root.Attribute (versionCodeAttributeName) != null; + // If MinSdkVersionName can't be parsed, set it to XABuildConfig.AndroidMinimumDotNetApiLevel + string TryParseMinSdkVersionName () + { + int minSdkVersion; + if (!int.TryParse (MinSdkVersionName, out minSdkVersion)) + minSdkVersion = XABuildConfig.AndroidMinimumDotNetApiLevel; + return minSdkVersion.ToString (); + } + public string GetMinimumSdk () { - int defaultMinSdkVersion = MonoAndroidHelper.SupportedVersions.MinStableVersion.ApiLevel; var minAttr = doc.Root.Element ("uses-sdk")?.Attribute (androidNs + "minSdkVersion"); if (minAttr == null) { - int minSdkVersion; - if (!int.TryParse (MinSdkVersionName, out minSdkVersion)) - minSdkVersion = defaultMinSdkVersion; - return Math.Min (minSdkVersion, defaultMinSdkVersion).ToString (); + return TryParseMinSdkVersionName (); } return minAttr.Value; } @@ -295,24 +300,20 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li app.Add (new XElement ("meta-data", new XAttribute (androidNs + "name", "com.google.android.wearable.beta.app"), new XAttribute (androidNs + "resource", "@xml/wearable_app_desc"))); } - // If no is specified, add it with both minSdkVersion and - // targetSdkVersion set to TargetFrameworkVersion + string minSdkVersionString = TryParseMinSdkVersionName (); + + // If no is specified, add it with both minSdkVersion and targetSdkVersion if (!manifest.Elements ("uses-sdk").Any ()) { manifest.AddFirst ( new XElement ("uses-sdk", - new XAttribute (androidNs + "minSdkVersion", MinSdkVersionName), + new XAttribute (androidNs + "minSdkVersion", minSdkVersionString), new XAttribute (androidNs + "targetSdkVersion", TargetSdkVersionName))); } - // If no minSdkVersion is specified, set it to TargetFrameworkVersion + // If no minSdkVersion is specified, set it var uses = manifest.Element ("uses-sdk"); - if (uses.Attribute (androidNs + "minSdkVersion") == null) { - int minSdkVersion; - if (!int.TryParse (MinSdkVersionName, out minSdkVersion)) - minSdkVersion = XABuildConfig.NDKMinimumApiAvailable; - minSdkVersion = Math.Min (minSdkVersion, XABuildConfig.NDKMinimumApiAvailable); - uses.SetAttributeValue (androidNs + "minSdkVersion", minSdkVersion.ToString ()); + uses.SetAttributeValue (androidNs + "minSdkVersion", minSdkVersionString); } string targetSdkVersion; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index bbea9a58667..8579f37a8c0 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -544,5 +544,22 @@ public static string GetRelativePathForAndroidAsset (string assetsDirectory, ITa return Convert.ToString (obj, CultureInfo.InvariantCulture); } + + /// + /// Converts $(SupportedOSPlatformVersion) to an API level, as it can be a version (21.0), or an int (21). + /// + /// The version to parse + /// The API level that corresponds to $(SupportedOSPlatformVersion), or 0 if parsing fails. + public static int ConvertSupportedOSPlatformVersionToApiLevel (string version) + { + int apiLevel = 0; + if (version.IndexOf ('.') == -1) { + version += ".0"; + } + if (Version.TryParse (version, out var parsedVersion)) { + apiLevel = parsedVersion.Major; + } + return apiLevel; + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets index 73af03e487f..ff86b19b484 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets @@ -101,6 +101,7 @@ projects. AndroidSdkPlatform="$(_AndroidApiLevel)" AndroidManifest="$(_AndroidManifestAbs)" DesignTimeBuild="$(DesignTimeBuild)" + SupportedOSPlatformVersion="$(SupportedOSPlatformVersion)" > diff --git a/tests/CodeBehind/BuildTests/Properties/AndroidManifest.xml b/tests/CodeBehind/BuildTests/Properties/AndroidManifest.xml index 62875447de2..a0230c27bfe 100644 --- a/tests/CodeBehind/BuildTests/Properties/AndroidManifest.xml +++ b/tests/CodeBehind/BuildTests/Properties/AndroidManifest.xml @@ -1,6 +1,6 @@  - + diff --git a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs index dafe5d5a031..cffcf4e3279 100755 --- a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs @@ -25,9 +25,10 @@ public void ClearDebugProperties () void SetTargetFrameworkAndManifest(XamarinAndroidApplicationProject proj, Builder builder) { builder.LatestTargetFrameworkVersion (out string apiLevel); + proj.SupportedOSPlatformVersion = "24"; proj.AndroidManifest = $@" - + "; diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 8504f47d421..8e8c6b1b360 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -79,7 +79,7 @@ public void GlobalLayoutEvent_ShouldRegisterAndFire_OnActivityLaunch ([Values (f proj = new XamarinAndroidApplicationProject () { IsRelease = isRelease, - MinSdkVersion = "23", + SupportedOSPlatformVersion = "23", TargetSdkVersion = null, }; if (isRelease || !CommercialBuildAvailable) { @@ -959,10 +959,10 @@ public void CheckResouceIsOverridden ([Values (true, false)] bool useAapt2) b.ThrowOnBuildFailure = false; string apiLevel; app.TargetFrameworkVersion = b.LatestTargetFrameworkVersion (out apiLevel); - + app.SupportedOSPlatformVersion = "24"; app.AndroidManifest = $@" - + "; @@ -996,10 +996,10 @@ public void CheckResouceIsOverridden ([Values (true, false)] bool useAapt2) app.AndroidUseAapt2 = useAapt2; app.LayoutMain = app.LayoutMain.Replace ("@string/hello", "@string/hello_me"); app.TargetFrameworkVersion = b.LatestTargetFrameworkVersion (out apiLevel); - + app.SupportedOSPlatformVersion = "24"; app.AndroidManifest = $@" - + "; diff --git a/tests/MSBuildDeviceIntegration/Tests/MonoAndroidExportTest.cs b/tests/MSBuildDeviceIntegration/Tests/MonoAndroidExportTest.cs index 69af71239fd..64f74a973ea 100644 --- a/tests/MSBuildDeviceIntegration/Tests/MonoAndroidExportTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/MonoAndroidExportTest.cs @@ -110,10 +110,10 @@ protected override void OnCreate (Bundle bundle) using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) { string apiLevel; proj.TargetFrameworkVersion = b.LatestTargetFrameworkVersion (out apiLevel); - + proj.SupportedOSPlatformVersion = "24.0"; proj.AndroidManifest = $@" - + ";