diff --git a/Documentation/guides/BuildProcess.md b/Documentation/guides/BuildProcess.md
index f8f32090fa0..4fae4caa383 100644
--- a/Documentation/guides/BuildProcess.md
+++ b/Documentation/guides/BuildProcess.md
@@ -624,7 +624,7 @@ when packaing Release applications.
allows the developer to define custom items to use with the
`AndroidVersionCodePattern`. They are in the form of a `key=value`
pair. All items in the `value` should be integer values. For
- example: `screen=23;target=$(_SupportedApiLevel)`. As you can see
+ example: `screen=23;target=$(_AndroidApiLevel)`. As you can see
you can make use of existing or custom MSBuild properties in the
string.
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveAndroidTooling.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveAndroidTooling.cs
index 85481b95e24..a6b7620425d 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveAndroidTooling.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveAndroidTooling.cs
@@ -40,9 +40,6 @@ public class ResolveAndroidTooling : Task
[Output]
public string AndroidApiLevelName { get; set; }
- [Output]
- public string SupportedApiLevel { get; set; }
-
[Output]
public string AndroidSdkBuildToolsPath { get; set; }
@@ -208,7 +205,6 @@ public override bool Execute ()
Log.LogDebugMessage ($" {nameof (TargetFrameworkVersion)}: {TargetFrameworkVersion}");
Log.LogDebugMessage ($" {nameof (AndroidApiLevel)}: {AndroidApiLevel}");
Log.LogDebugMessage ($" {nameof (AndroidApiLevelName)}: {AndroidApiLevelName}");
- Log.LogDebugMessage ($" {nameof (SupportedApiLevel)}: {SupportedApiLevel}");
Log.LogDebugMessage ($" {nameof (AndroidSdkBuildToolsPath)}: {AndroidSdkBuildToolsPath}");
Log.LogDebugMessage ($" {nameof (AndroidSdkBuildToolsBinPath)}: {AndroidSdkBuildToolsBinPath}");
Log.LogDebugMessage ($" {nameof (ZipAlignPath)}: {ZipAlignPath}");
@@ -272,24 +268,36 @@ bool ValidateApiLevels ()
(string.IsNullOrWhiteSpace (AndroidApiLevel) && string.IsNullOrWhiteSpace (TargetFrameworkVersion));
if (UseLatestAndroidPlatformSdk) {
- AndroidApiLevel = GetMaxInstalledApiLevel ().ToString ();
- SupportedApiLevel = GetMaxStableApiLevel ().ToString ();
- int maxInstalled, maxSupported = 0;
- if (int.TryParse (AndroidApiLevel, out maxInstalled) && int.TryParse (SupportedApiLevel, out maxSupported) && maxInstalled > maxSupported) {
- Log.LogDebugMessage ($"API Level {AndroidApiLevel} is greater than the maximum supported API level of {SupportedApiLevel}. " +
+ int maxInstalled = GetMaxInstalledApiLevel ();
+ int maxSupported = GetMaxStableApiLevel ();
+ AndroidApiLevel = maxInstalled.ToString ();
+ if (maxInstalled > maxSupported) {
+ Log.LogDebugMessage ($"API Level {maxInstalled} is greater than the maximum supported API level of {maxSupported}. " +
"Support for this API will be added in a future release.");
- AndroidApiLevel = SupportedApiLevel;
}
if (!string.IsNullOrWhiteSpace (TargetFrameworkVersion)) {
var userSelected = MonoAndroidHelper.SupportedVersions.GetApiLevelFromFrameworkVersion (TargetFrameworkVersion);
// overwrite using user version only if it is
// above the maxStableApi and a valid apiLevel.
if (userSelected != null && userSelected > maxSupported && userSelected <= maxInstalled) {
+ maxInstalled =
+ maxSupported = userSelected.Value;
AndroidApiLevel = userSelected.ToString ();
- SupportedApiLevel = userSelected.ToString ();
}
}
- TargetFrameworkVersion = GetTargetFrameworkVersionFromApiLevel ();
+
+ for (int apiLevel = maxSupported; apiLevel >= MonoAndroidHelper.SupportedVersions.MinStableVersion.ApiLevel; apiLevel--) {
+ var id = MonoAndroidHelper.SupportedVersions.GetIdFromApiLevel (apiLevel);
+ var apiPlatformDir = MonoAndroidHelper.AndroidSdk.TryGetPlatformDirectoryFromApiLevel (id, MonoAndroidHelper.SupportedVersions);
+ if (apiPlatformDir != null && Directory.Exists (apiPlatformDir)) {
+ var targetFramework = MonoAndroidHelper.SupportedVersions.GetFrameworkVersionFromId (id);
+ if (targetFramework != null && MonoAndroidHelper.SupportedVersions.InstalledBindingVersions.Any (b => b.FrameworkVersion == targetFramework)) {
+ AndroidApiLevel = apiLevel.ToString ();
+ TargetFrameworkVersion = targetFramework;
+ break;
+ }
+ }
+ }
return TargetFrameworkVersion != null;
}
@@ -303,13 +311,11 @@ bool ValidateApiLevels ()
return false;
}
AndroidApiLevel = MonoAndroidHelper.SupportedVersions.GetApiLevelFromId (id).ToString ();
- SupportedApiLevel = AndroidApiLevel;
return true;
}
if (!string.IsNullOrWhiteSpace (AndroidApiLevel)) {
AndroidApiLevel = AndroidApiLevel.Trim ();
- SupportedApiLevel = GetMaxSupportedApiLevel (AndroidApiLevel);
TargetFrameworkVersion = GetTargetFrameworkVersionFromApiLevel ();
return TargetFrameworkVersion != null;
}
@@ -369,8 +375,7 @@ string GetMaxSupportedApiLevel (string apiLevel)
string GetTargetFrameworkVersionFromApiLevel ()
{
- string targetFramework = MonoAndroidHelper.SupportedVersions.GetFrameworkVersionFromId (SupportedApiLevel) ??
- MonoAndroidHelper.SupportedVersions.GetFrameworkVersionFromId (AndroidApiLevel);
+ string targetFramework = MonoAndroidHelper.SupportedVersions.GetFrameworkVersionFromId (AndroidApiLevel);
if (targetFramework != null)
return targetFramework;
Log.LogCodedError ("XA0000",
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ResolveSdksTaskTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ResolveSdksTaskTests.cs
index 4e6ebf9decf..8f1fcda546f 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ResolveSdksTaskTests.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ResolveSdksTaskTests.cs
@@ -190,7 +190,7 @@ public void UseLatestAndroidSdk (string buildtools, string jdk, ApiInfo[] apis,
SequencePointsMode = "None",
};
Assert.AreEqual (expectedTaskResult, resolveSdks.Execute () && validateJavaVersion.Execute () && androidTooling.Execute (), $"Tasks should have {(expectedTaskResult ? "succeeded" : "failed" )}.");
- Assert.AreEqual (expectedTargetFramework, androidTooling.TargetFrameworkVersion, $"TargetFrameworkVersion should be {expectedTargetFramework} but was {targetFrameworkVersion}");
+ Assert.AreEqual (expectedTargetFramework, androidTooling.TargetFrameworkVersion, $"TargetFrameworkVersion should be {expectedTargetFramework} but was {androidTooling.TargetFrameworkVersion}");
if (!string.IsNullOrWhiteSpace (expectedError)) {
Assert.AreEqual (1, errors.Count (), "An error should have been raised.");
Assert.AreEqual (expectedError, errors [0].Code, $"Expected error code {expectedError} but found {errors [0].Code}");
@@ -240,7 +240,7 @@ public void ResolveSdkTiming ()
UseLatestAndroidPlatformSdk = false,
AotAssemblies = false,
SequencePointsMode = "None",
- };;
+ };
var start = DateTime.UtcNow;
Assert.IsTrue (resolveSdks.Execute (), "ResolveSdks should succeed!");
Assert.IsTrue (validateJavaVersion.Execute (), "ValidateJavaVersion should succeed!");
@@ -250,7 +250,6 @@ public void ResolveSdkTiming ()
Assert.AreEqual (androidTooling.AndroidApiLevel, "26", "AndroidApiLevel should be 26");
Assert.AreEqual (androidTooling.TargetFrameworkVersion, "v8.0", "TargetFrameworkVersion should be v8.0");
Assert.AreEqual (androidTooling.AndroidApiLevelName, "26", "AndroidApiLevelName should be 26");
- Assert.AreEqual (androidTooling.SupportedApiLevel, "26", "SupportedApiLevel should be 26");
Assert.NotNull (resolveSdks.ReferenceAssemblyPaths, "ReferenceAssemblyPaths should not be null.");
Assert.AreEqual (resolveSdks.ReferenceAssemblyPaths.Length, 1, "ReferenceAssemblyPaths should have 1 entry.");
Assert.AreEqual (resolveSdks.ReferenceAssemblyPaths[0], Path.Combine (referencePath, "MonoAndroid"), $"ReferenceAssemblyPaths should be {Path.Combine (referencePath, "MonoAndroid")}.");
@@ -278,5 +277,135 @@ public void ResolveSdkTiming ()
Assert.AreEqual (validateJavaVersion.MinimumRequiredJdkVersion, "1.8", "MinimumRequiredJdkVersion should be 1.8");
Directory.Delete (Path.Combine (Root, path), recursive: true);
}
+
+ static object [] TargetFrameworkPairingParameters = new [] {
+ //We support 28, but only 27 is installed
+ new object [] {
+ "Older API Installed", //description
+ // androidSdks
+ new [] {
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ },
+ // targetFrameworks
+ new ApiInfo [] {
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = true },
+ },
+ null, //userSelected
+ "27", //androidApiLevel
+ "27", //androidApiLevelName
+ "v8.1", //targetFrameworkVersion
+ },
+ //28 is installed but we only support 27
+ new object [] {
+ "Newer API Installed", //description
+ // androidSdks
+ new [] {
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = true },
+ },
+ // targetFrameworks
+ new ApiInfo [] {
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ },
+ null, //userSelected
+ "27", //androidApiLevel
+ "27", //androidApiLevelName
+ "v8.1", //targetFrameworkVersion
+ },
+ //A paired downgrade to API 26
+ new object [] {
+ "Paired Downgrade", //description
+ // androidSdks
+ new [] {
+ new ApiInfo () { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true },
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ },
+ // targetFrameworks
+ new ApiInfo [] {
+ new ApiInfo () { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = true },
+ },
+ null, //userSelected
+ "26", //androidApiLevel
+ "26", //androidApiLevelName
+ "v8.0", //targetFrameworkVersion
+ },
+ //A new API level 28 is not stable yet
+ new object [] {
+ "New Unstable API", //description
+ // androidSdks
+ new [] {
+ new ApiInfo () { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true },
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = false },
+ },
+ // targetFrameworks
+ new ApiInfo [] {
+ new ApiInfo () { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true },
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = false },
+ },
+ null, //userSelected
+ "27", //androidApiLevel
+ "27", //androidApiLevelName
+ "v8.1", //targetFrameworkVersion
+ },
+ //User selected a new API level 28 is not stable yet
+ new object [] {
+ "User Selected Unstable API", //description
+ // androidSdks
+ new [] {
+ new ApiInfo () { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true },
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = false },
+ },
+ // targetFrameworks
+ new ApiInfo [] {
+ new ApiInfo () { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true },
+ new ApiInfo () { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true },
+ new ApiInfo () { Id = "28", Level = 28, Name = "P", FrameworkVersion = "v9.0", Stable = false },
+ },
+ "v9.0", //userSelected
+ "28", //androidApiLevel
+ "28", //androidApiLevelName
+ "v9.0", //targetFrameworkVersion
+ },
+ };
+
+ [Test]
+ [TestCaseSource (nameof (TargetFrameworkPairingParameters))]
+ public void TargetFrameworkPairing (string description, ApiInfo[] androidSdk, ApiInfo[] targetFrameworks, string userSelected, string androidApiLevel, string androidApiLevelName, string targetFrameworkVersion)
+ {
+ var path = Path.Combine ("temp", $"{nameof (TargetFrameworkPairing)}_{description}");
+ var androidSdkPath = CreateFauxAndroidSdkDirectory (Path.Combine (path, "android-sdk"), "26.0.3", androidSdk);
+ string javaExe = string.Empty;
+ string javacExe;
+ var javaPath = CreateFauxJavaSdkDirectory (Path.Combine (path, "jdk"), "1.8.0", out javaExe, out javacExe);
+ var referencePath = CreateFauxReferencesDirectory (Path.Combine (path, "references"), targetFrameworks);
+ IBuildEngine engine = new MockBuildEngine (TestContext.Out);
+ var resolveSdks = new ResolveSdks {
+ BuildEngine = engine,
+ AndroidSdkPath = androidSdkPath,
+ AndroidNdkPath = androidSdkPath,
+ JavaSdkPath = javaPath,
+ ReferenceAssemblyPaths = new [] {
+ Path.Combine (referencePath, "MonoAndroid"),
+ },
+ };
+ var androidTooling = new ResolveAndroidTooling {
+ BuildEngine = engine,
+ AndroidSdkPath = androidSdkPath,
+ AndroidNdkPath = androidSdkPath,
+ UseLatestAndroidPlatformSdk = true,
+ TargetFrameworkVersion = userSelected,
+ };
+ Assert.IsTrue (resolveSdks.Execute (), "ResolveSdks should succeed!");
+ Assert.IsTrue (androidTooling.Execute (), "ResolveAndroidTooling should succeed!");
+ Assert.AreEqual (androidApiLevel, androidTooling.AndroidApiLevel, $"AndroidApiLevel should be {androidApiLevel}");
+ Assert.AreEqual (androidApiLevelName, androidTooling.AndroidApiLevelName, $"AndroidApiLevelName should be {androidApiLevelName}");
+ Assert.AreEqual (targetFrameworkVersion, androidTooling.TargetFrameworkVersion, $"TargetFrameworkVersion should be {targetFrameworkVersion}");
+ Directory.Delete (Path.Combine (Root, path), recursive: true);
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index 62709fb6e58..da364b9e28f 100755
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -726,7 +726,6 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
-
@@ -1008,7 +1007,7 @@ because xbuild doesn't support framework reference assemblies.
-
+