diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CalculateProjectDependencies.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CalculateProjectDependencies.cs new file mode 100644 index 00000000000..2fad92a8a01 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CalculateProjectDependencies.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Collections.Generic; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Xamarin.Android.Tools; + +namespace Xamarin.Android.Tasks +{ + public class CalculateProjectDependencies : Task + { + const int DefaultMinSDKVersion = 11; + + [Required] + public string TargetFrameworkVersion { get; set; } + + [Required] + public ITaskItem ManifestFile { get; set; } + + [Required] + public string BuildToolsVersion { get; set; } + + public string PlatformToolsVersion { get; set; } + + public string ToolsVersion { get; set; } + + public string NdkVersion { get; set; } + + [Output] + public ITaskItem [] Dependencies { get; set; } + + ITaskItem CreateAndroidDependency (string include, string version) + { + if (string.IsNullOrEmpty (version)) + return new TaskItem (include); + + return new TaskItem (include, new Dictionary { + { "Version", version } + }); + } + + public override bool Execute () + { + var dependencies = new List (); + var targetApiLevel = MonoAndroidHelper.SupportedVersions.GetApiLevelFromFrameworkVersion (TargetFrameworkVersion); + var manifestApiLevel = DefaultMinSDKVersion; + if (File.Exists (ManifestFile.ItemSpec)) { + var manifest = AndroidAppManifest.Load (ManifestFile.ItemSpec, MonoAndroidHelper.SupportedVersions); + manifestApiLevel = manifest.TargetSdkVersion ?? manifest.MinSdkVersion ?? DefaultMinSDKVersion; + } + var sdkVersion = Math.Max (targetApiLevel.Value, manifestApiLevel); + dependencies.Add (CreateAndroidDependency ($"platforms;android-{sdkVersion}", $"")); + dependencies.Add (CreateAndroidDependency ($"build-tools;{BuildToolsVersion}", BuildToolsVersion)); + if (!string.IsNullOrEmpty (PlatformToolsVersion)) { + dependencies.Add (CreateAndroidDependency ("platform-tools", PlatformToolsVersion)); + } + if (!string.IsNullOrEmpty (ToolsVersion)) { + dependencies.Add (CreateAndroidDependency ("tools", ToolsVersion)); + } + if (!string.IsNullOrEmpty (NdkVersion)) { + dependencies.Add (CreateAndroidDependency ("ndk-bundle", NdkVersion)); + } + Dependencies = dependencies.ToArray (); + return !Log.HasLoggedErrors; + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/GetDependenciesTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/GetDependenciesTests.cs new file mode 100644 index 00000000000..4157009cab2 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/GetDependenciesTests.cs @@ -0,0 +1,95 @@ +using System; +using NUnit.Framework; +using Xamarin.ProjectTools; +using System.IO; +using System.Linq; +using Microsoft.Build.Framework; +using System.Text; +using Xamarin.Android.Tasks; +using Microsoft.Build.Utilities; + +namespace Xamarin.Android.Build.Tests { + + [TestFixture] + [Parallelizable (ParallelScope.Children)] + public class GetDependenciesTest : BaseTest { + + [Test] + public void ManifestFileDoesNotExist () + { + var path = Path.Combine ("temp", TestName); + var referencePath = CreateFauxReferencesDirectory (Path.Combine (path, "references"), new ApiInfo[] { + new ApiInfo () { Id = 26, Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true }, + } ); + MonoAndroidHelper.RefreshSupportedVersions (new string [] { referencePath }); + IBuildEngine engine = new MockBuildEngine (TestContext.Out); + var task = new CalculateProjectDependencies { + BuildEngine = engine + }; + + task.PlatformToolsVersion = "26.0.3"; + task.ToolsVersion = "26.0.1"; + task.NdkVersion = "12.1"; + task.BuildToolsVersion = "26.0.1"; + task.TargetFrameworkVersion = "v8.0"; + task.ManifestFile = new TaskItem (Path.Combine (path, "AndroidManifest.xml")); + Assert.IsTrue (task.Execute ()); + Assert.IsNotNull (task.Dependencies); + Assert.AreEqual (5, task.Dependencies.Length); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "build-tools;26.0.1" && x.GetMetadata ("Version") == "26.0.1"), + "Dependencies should contains a build-tools version 26.0.1"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "tools" && x.GetMetadata ("Version") == "26.0.1"), + "Dependencies should contains a tools version 26.0.1"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "platforms;android-26" && x.GetMetadata ("Version") == ""), + "Dependencies should contains a platform version android-26"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "platform-tools" && x.GetMetadata ("Version") == "26.0.3"), + "Dependencies should contains a platform-tools version 26.0.3"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "ndk-bundle" && x.GetMetadata ("Version") == "12.1"), + "Dependencies should contains a ndk-bundle version 12.1"); + } + + [Test] + public void ManifestFileExists () + { + var path = Path.Combine (Root, "temp", TestName); + var referencePath = CreateFauxReferencesDirectory (Path.Combine (path, "references"), new ApiInfo[] { + new ApiInfo () { Id = 26, Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true }, + } ); + MonoAndroidHelper.RefreshSupportedVersions (new string [] { referencePath }); + IBuildEngine engine = new MockBuildEngine (TestContext.Out); + var task = new CalculateProjectDependencies { + BuildEngine = engine + }; + + + Directory.CreateDirectory (path); + var manifestFile = Path.Combine (path, "AndroidManifest.xml"); + File.WriteAllText (manifestFile, @" + + +"); + + task.PlatformToolsVersion = "26.0.3"; + task.ToolsVersion = "26.0.1"; + task.NdkVersion = "12.1"; + task.BuildToolsVersion = "26.0.1"; + task.TargetFrameworkVersion = "v8.0"; + task.ManifestFile = new TaskItem (manifestFile); + Assert.IsTrue(task.Execute ()); + Assert.IsNotNull (task.Dependencies); + Assert.AreEqual (5, task.Dependencies.Length); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "build-tools;26.0.1" && x.GetMetadata ("Version") == "26.0.1"), + "Dependencies should contains a build-tools version 26.0.1"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "tools" && x.GetMetadata ("Version") == "26.0.1"), + "Dependencies should contains a tools version 26.0.1"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "platforms;android-26" && x.GetMetadata ("Version") == ""), + "Dependencies should contains a platform version android-26"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "platform-tools" && x.GetMetadata ("Version") == "26.0.3"), + "Dependencies should contains a platform-tools version 26.0.3"); + Assert.IsNotNull (task.Dependencies.FirstOrDefault (x => x.ItemSpec == "ndk-bundle" && x.GetMetadata ("Version") == "12.1"), + "Dependencies should contains a ndk-bundle version 12.1"); + + Directory.Delete (path, recursive: true); + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs index d7160c07e37..b951d241601 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs @@ -131,14 +131,29 @@ protected string CreateFauxAndroidSdkDirectory (string path, string buildToolsVe return androidSdkDirectory; } - protected string CreateFauxReferencesDirectory (string path, string[] versions) + public struct ApiInfo { + public int Id; + public int Level; + public string Name; + public string FrameworkVersion; + public bool Stable; + } + + protected string CreateFauxReferencesDirectory (string path, ApiInfo [] versions) { + string referencesDirectory = Path.Combine (Root, path); Directory.CreateDirectory (referencesDirectory); - Directory.CreateDirectory (Path.Combine (referencesDirectory, "v1.0")); - File.WriteAllText (Path.Combine (referencesDirectory, "v1.0", "mscorlib.dll"), ""); - foreach (var v in versions){ - Directory.CreateDirectory (Path.Combine (referencesDirectory, v)); + Directory.CreateDirectory (Path.Combine (referencesDirectory, "MonoAndroid", "v1.0")); + File.WriteAllText (Path.Combine (referencesDirectory, "MonoAndroid", "v1.0", "mscorlib.dll"), ""); + foreach (var v in versions) { + Directory.CreateDirectory (Path.Combine (referencesDirectory, "MonoAndroid", v.FrameworkVersion)); + Directory.CreateDirectory (Path.Combine (referencesDirectory, "MonoAndroid", v.FrameworkVersion, "RedistList")); + File.WriteAllText (Path.Combine (referencesDirectory, "MonoAndroid", v.FrameworkVersion, "MonoAndroid.dll"), ""); + File.WriteAllText (Path.Combine (referencesDirectory, "MonoAndroid", v.FrameworkVersion, "AndroidApiInfo.xml"), + $"\n{v.Id}\n{v.Level}\n{v.Name}\n{v.FrameworkVersion}\n{v.Stable}\n"); + File.WriteAllText (Path.Combine (referencesDirectory, "MonoAndroid", v.FrameworkVersion, "RedistList", "FrameworkList.xml"), + $""); } return referencesDirectory; } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/MockBuildEngine.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/MockBuildEngine.cs new file mode 100644 index 00000000000..38f36c65d7e --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/MockBuildEngine.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Build.Framework; + +namespace Xamarin.Android.Build.Tests { + public class MockBuildEngine : IBuildEngine, IBuildEngine2, IBuildEngine3, IBuildEngine4 { + public MockBuildEngine (TextWriter output) + { + this.Output = output; + } + + private TextWriter Output { get; } + + int IBuildEngine.ColumnNumberOfTaskNode => -1; + + bool IBuildEngine.ContinueOnError => false; + + int IBuildEngine.LineNumberOfTaskNode => -1; + + string IBuildEngine.ProjectFileOfTaskNode => "this.xml"; + + bool IBuildEngine2.IsRunningMultipleNodes => false; + + bool IBuildEngine.BuildProjectFile (string projectFileName, string [] targetNames, IDictionary globalProperties, IDictionary targetOutputs) => true; + + void IBuildEngine.LogCustomEvent (CustomBuildEventArgs e) + { + this.Output.WriteLine ($"Custom: {e.Message}"); + } + + void IBuildEngine.LogErrorEvent (BuildErrorEventArgs e) + { + this.Output.WriteLine ($"Error: {e.Message}"); + } + + void IBuildEngine.LogMessageEvent (BuildMessageEventArgs e) + { + this.Output.WriteLine ($"Message: {e.Message}"); + } + + void IBuildEngine.LogWarningEvent (BuildWarningEventArgs e) + { + this.Output.WriteLine ($"Warning: {e.Message}"); + } + + private Dictionary Tasks = new Dictionary (); + + void IBuildEngine4.RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection) + { + Tasks.Add (key, obj); + } + + object IBuildEngine4.GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime) + { + return null; + } + + object IBuildEngine4.UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime) + { + var obj = Tasks [key]; + Tasks.Remove (key); + return obj; + } + + BuildEngineResult IBuildEngine3.BuildProjectFilesInParallel (string [] projectFileNames, string [] targetNames, IDictionary [] globalProperties, IList [] removeGlobalProperties, string [] toolsVersion, bool returnTargetOutputs) + { + throw new NotImplementedException (); + } + + void IBuildEngine3.Yield () { } + + void IBuildEngine3.Reacquire () { } + + bool IBuildEngine2.BuildProjectFile (string projectFileName, string [] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion) => true; + + bool IBuildEngine2.BuildProjectFilesInParallel (string [] projectFileNames, string [] targetNames, IDictionary [] globalProperties, IDictionary [] targetOutputsPerProject, string [] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion) => true; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems index fb84fb55378..4caedf0eed4 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.Shared.projitems @@ -19,8 +19,9 @@ + - \ No newline at end of file + diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj index 9a04c0c8228..45222dd1511 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj @@ -72,5 +72,6 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index df83aad5b2a..6c60361d00f 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -187,6 +187,7 @@ + pdb2mdb\BitAccess.cs diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index f000033f5b6..2fdca8ab239 100755 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -32,6 +32,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -237,7 +238,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. 27.0.3 27.0.1 26.1.1 - r14b + 16.1 None @@ -2767,6 +2768,25 @@ because xbuild doesn't support framework reference assemblies. DependsOnTargets="$(InstallDependsOnTargets)"> + + + + + <_ProjectAndroidManifest>$(ProjectDir)$(AndroidManifest) + + + + + + +