Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Add GetAndroidDependencies Target (#1290)
Browse files Browse the repository at this point in the history
Fixes: #1269

This commit adds the `GetAndroidDependencies` target to the
`Xamarin.Android.Common.targets`. Its purpose is to examine the
various settings in the project and report which Android SDK
build-tools, platform-tools, etc. are required.

`GetAndroidDependencies` will output an `@(AndroidDependency)` with
`%(AndroidDependency.Version)` metadata.

`@(AndroidDependency)` will contain `%(Identity)` values of:

  * `build-tools;{VERSION}`, where `{VERSION}` and `%(Version)` is
     controlled by the `$(AndroidSdkBuildToolsVersion)` property.
  * `ndk-bundle`: `%(Version)` is controlled by the
    `$(AndroidNdkVersion)` property.
  * `platform-tools`: `%(Version)` is controlled by the
    `$(AndroidSdkPlatformToolsVersion)` property.
  * `platforms;android-{API}`, where `{API}` and `%(Version)` is the
    API level for the `$(TargetFrameworkVersion)` in the `.csproj`.
    For example, `platforms;android-27` for
    `$(TargetFrameworkVersion)`=`v7.1`.
  * `tools`: `%(Version)` is controlled by the
    `$(AndroidSdkToolsVersion)` property.
  • Loading branch information
dellis1972 authored and jonpryor committed Feb 12, 2018
1 parent 0d822e2 commit f9b2c97
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -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<string, string> {
{ "Version", version }
});
}

public override bool Execute ()
{
var dependencies = new List<ITaskItem> ();
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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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, @"<?xml version='1.0' ?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android' android:versionCode='1' android:versionName='1.0' package='Mono.Android_Tests'>
<uses-sdk android:minSdkVersion='10' />
</manifest>");

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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
$"<AndroidApiInfo>\n<Id>{v.Id}</Id>\n<Level>{v.Level}</Level>\n<Name>{v.Name}</Name>\n<Version>{v.FrameworkVersion}</Version>\n<Stable>{v.Stable}</Stable>\n</AndroidApiInfo>");
File.WriteAllText (Path.Combine (referencesDirectory, "MonoAndroid", v.FrameworkVersion, "RedistList", "FrameworkList.xml"),
$"<FileList Redist=\"MonoAndroid\" Name=\"Xamarin.Android {v.FrameworkVersion} Support\" IncludeFramework=\"v1.0\"></FileList>");
}
return referencesDirectory;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<object, object> Tasks = new Dictionary<object, object> ();

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<string> [] 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
<Compile Include="$(MSBuildThisFileDirectory)PackagingTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\BaseTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\BuildHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utilities\MockBuildEngine.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Utilities\" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,6 @@
<Compile Include="BuildTest.OSS.cs" />
<Compile Include="ManifestTest.OSS.cs" />
<Compile Include="AndroidRegExTests.cs" />
<Compile Include="GetDependenciesTests.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
<Compile Include="Tasks\JavaToolTask.cs" />
<Compile Include="Tasks\GenerateLayoutCodeBehind.cs" />
<Compile Include="Tasks\CalculateLayoutCodeBehind.cs" />
<Compile Include="Tasks\CalculateProjectDependencies.cs" />
<Compile Include="$(MonoSourceFullPath)\mcs\tools\pdb2mdb\BitAccess.cs">
<Link>pdb2mdb\BitAccess.cs</Link>
</Compile>
Expand Down
22 changes: 21 additions & 1 deletion src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<UsingTask TaskName="Xamarin.Android.Tasks.BuildApk" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.CalculateAdditionalResourceCacheDirectories" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.CalculateLayoutCodeBehind" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.CalculateProjectDependencies" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.CheckForRemovedItems" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.CheckTargetFrameworks" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.CompileToDalvik" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
Expand Down Expand Up @@ -237,7 +238,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<AndroidSdkBuildToolsVersion Condition="'$(AndroidSdkBuildToolsVersion)' == ''">27.0.3</AndroidSdkBuildToolsVersion>
<AndroidSdkPlatformToolsVersion Condition="'$(AndroidSdkPlatformToolsVersion)' == ''">27.0.1</AndroidSdkPlatformToolsVersion>
<AndroidSdkToolsVersion Condition="'$(AndroidSdkToolsVersion)' == ''">26.1.1</AndroidSdkToolsVersion>
<AndroidNdkVersion Condition="'$(AndroidNdkVersion)' == ''">r14b</AndroidNdkVersion>
<AndroidNdkVersion Condition="'$(AndroidNdkVersion)' == ''">16.1</AndroidNdkVersion>

<!-- Obsolete -->
<AndroidGdbDebugServer>None</AndroidGdbDebugServer>
Expand Down Expand Up @@ -2767,6 +2768,25 @@ because xbuild doesn't support framework reference assemblies.
DependsOnTargets="$(InstallDependsOnTargets)">
</Target>


<!-- SDK Management Targets -->
<Target Name="GetAndroidDependencies" DependsOnTargets="$(GetAndroidDependenciesDependsOn)" Returns="@(AndroidDependency)">
<PropertyGroup>
<_ProjectAndroidManifest>$(ProjectDir)$(AndroidManifest)</_ProjectAndroidManifest>
</PropertyGroup>
<Error Text="AndroidManifest file does not exist" Condition="'$(_ProjectAndroidManifest)'!='' And !Exists ('$(_ProjectAndroidManifest)')"/>
<CalculateProjectDependencies
TargetFrameworkVersion="$(TargetFrameworkVersion)"
ManifestFile="$(_ProjectAndroidManifest)"
BuildToolsVersion="$(AndroidSdkBuildToolsVersion)"
PlatformToolsVersion="$(AndroidSdkPlatformToolsVersion)"
ToolsVersion="$(AndroidSdkToolsVersion)"
NdkVersion="$(AndroidNdkVersion)"
>
<Output TaskParameter="Dependencies" ItemName="AndroidDependency" />
</CalculateProjectDependencies>
</Target>

<Import Project="$(MSBuildThisFileDirectory)Xamarin.Android.Common.Debugging.targets"
Condition="Exists('$(MSBuildThisFileDirectory)Xamarin.Android.Common.Debugging.targets')"/>

Expand Down

0 comments on commit f9b2c97

Please sign in to comment.