From ec426d12efa32f36187e8cd1118f57dd8e191f2a Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 5 Aug 2016 12:12:30 -0400 Subject: [PATCH] [Xamarin.Android.Build.Tasks] Use correct Mono.Android.dll We're trying to get [`make jenkins`][0] working on Jenkins, and [it's failing][1], as one might expect when a particular repo and associated build system has never been run on Jenkins before: Android.App/ApplicationTest.cs(9,7): error CS0246: The type or namespace name `NUnit' could not be found. Are you missing an assembly reference? This error occurs while building `src/Mono.Android/Test/Mono.Android-Tests.csproj`, and happens because the `Xamarin.Android.NUnitLite.dll` assembly isn't referenced... because it isn't *found*: Microsoft.Common.targets: warning : Reference 'Xamarin.Android.NUnitLite' not resolved This raises a long-standing issue with building `xamarin-android`: The Xamarin.Android SDK is intended to be usable from a system-wide installation environment, e.g. installed into `$(MSBuildExtensionsPath)`, as many MSBuild tasks such as `` are used to resolve assemblies, and those -- by default -- look in system-wide installation locations. "Unfortunately," A `xamarin-android` *build* **should not** be built into the system-wide install location; it should be built in a separate location, for all manner of "hygiene" and "sanity" reasons. Additionally, overriding `$(TargetFrameworkRootPath)` isn't a reliable solution. All of which adds up to either build failures such as the above -- when there is no system-wide Xamarin.Android install -- or the use/resolution of the system-wide Xamarin.Android assemblies when building e.g. `src/Mono.Android/Test`, which is wholly undesirable. A possible "fix"/"workaround": *force* `xbuild` to behave as we want it to by setting the `$MSBuildExtensionsPath` and `$XBUILD_FRAMEWORK_FOLDERS_PATH` environment variables *from within the build process*. Since `xbuild` doesn't cache the value of these environment variables, calling `Environment.SetEnvironmentVariable()` within the build process will cause subsequent ``/etc. invocations to use the updated values, allowing us to control which directories are used for assembly framework resolution purposes. Update the `_GetReferenceAssemblyPaths` target within `Xamarin.Android.Common.targets` and `Xamarin.Android.Bindings.targets` so that they call the new `` task, which updates the `$MSBuildExtensionsPath` and `$XBUILD_FRAMEWORK_FOLDERS_PATH` environment variables. This allows building `src/Mono.Android/Test` with "normal" `xbuild` to use the intended assemblies without requiring the use of `tools/scripts/xabuild` to build `src/Mono.Android/Test`. [0]: https://github.com/xamarin/xamarin-android/commit/a16673d3eb2c4945c3a74f5f8154603d7658fc9a [1]: https://jenkins.mono-project.com/view/Xamarin.Android/job/xamarin-android/20/console --- .../Tasks/SetMSBuildExtensionsPath.cs | 49 +++++++++++++++++++ .../Xamarin.Android.Bindings.targets | 2 + .../Xamarin.Android.Build.Tasks.csproj | 1 + .../Xamarin.Android.Common.targets | 2 + 4 files changed, 54 insertions(+) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/SetMSBuildExtensionsPath.cs diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/SetMSBuildExtensionsPath.cs b/src/Xamarin.Android.Build.Tasks/Tasks/SetMSBuildExtensionsPath.cs new file mode 100644 index 00000000000..1ceaceb5b17 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/SetMSBuildExtensionsPath.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; +using System.Linq; + +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; + +namespace Xamarin.Android.Tasks +{ + public sealed class SetMSBuildExtensionsPath : Task + { + const string MSBuildExtensionsPath = "MSBuildExtensionsPath"; + const string XBUILD_FRAMEWORK_FOLDERS_PATH = "XBUILD_FRAMEWORK_FOLDERS_PATH"; + public override bool Execute () + { + var frameworksPath = Path.GetDirectoryName (typeof (SetMSBuildExtensionsPath).Assembly.Location); + if (Path.DirectorySeparatorChar == '\\') { + // TODO: Default Windows search location + } else { + // e == $prefix/lib/xbuild/Xamarin/Android + // Want: $prefix/lib/xbuild-frameworks + if (!frameworksPath.EndsWith ("xbuild/Xamarin/Android", StringComparison.OrdinalIgnoreCase)) { + throw new NotSupportedException ("Cannot determine path to xbuild-frameworks!"); + } + frameworksPath = Path.GetDirectoryName (Path.GetDirectoryName (Path.GetDirectoryName (frameworksPath))); + frameworksPath = Path.Combine (frameworksPath, "xbuild-frameworks"); + } + + UpdateEnvironmentVariable (MSBuildExtensionsPath, frameworksPath); + UpdateEnvironmentVariable (XBUILD_FRAMEWORK_FOLDERS_PATH, frameworksPath); + + return !Log.HasLoggedErrors; + } + + void UpdateEnvironmentVariable (string environmentVariable, string newPath) + { + var p = (Environment.GetEnvironmentVariable (environmentVariable) ?? "") + .Split (new [] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); + if (p.Any (x => string.Equals (x, newPath, StringComparison.OrdinalIgnoreCase))) { + return; + } + + var newValue = string.Join (Path.PathSeparator.ToString (), new [] { newPath }.Concat (p)); + Log.LogMessage (MessageImportance.Low, $" Setting environment variable `{environmentVariable}`='{newValue}'."); + Environment.SetEnvironmentVariable (environmentVariable, newValue); + } + } +} + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets index 5ca744343e7..88ee69b1611 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Bindings.targets @@ -35,6 +35,7 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved. +