[xabuild.exe] cross-platform form of tools/scripts/xabuild (#910)

Context: https://github.com/jonathanpeppers/xabuild

What do we want? A *usable*, *parallel*, build tree.

To elaborate: we want to be able to have a "system" Xamarin.Android
install, and a "parallel" xamarin-android install, and be able to
*easily* switch between the two. (For various definitions of "easy";
here, we mean *command-line* use, not IDE use.)

On macOS, this (more or less) Just Works™, and is extremely handy
for testing bug fixes:

	$ xbuild /t:Install Project.csproj
	# Verify that some bug is triggered

	$ xbuild /t:Uninstall Project.csproj
	$ tools/scripts/xabuild /t:Install Project.csproj
	# Verify that some bug is *fixed*

There's One Problem™ with this: MSBuild does not make this easy.
(Related: commit aa1db830.) Apps may rely on files located within
`$(MSBuildExtensionsPath)` or `$(TargetFrameworkRootPath)`, files
such as PCL profile assemblies, or 3rd party frameworks.

Meanwhile, on macOS, `xabuild` is *predicated* upon overriding
`$(TargetFrameworkRootPath)` and `$(MSBuildExtensionsPath)` and
`$(XBUILD_FRAMEWORK_FOLDERS_PATH)`, and creating a bunch of symlinks
to "fake out" `msbuild.exe` so that system-installed files such as
PCL assemblies can be found *through* the parallel environment.

It kinda/sorta works on macOS. It completely falls apart when using
Windows. There is no easy "symlink half the world" solution there.
Overriding `$(MSBuildExtensionsPath)` means that
`Microsoft.Common.targets` can't be found. Overriding
`$(TargetFrameworkRootPath)` means PCL files can't be found.

It's a mess.

Fortunately, more recent versions of MSBuild allow for some of these
properties to contain *multiple* directories instead of a single
directory, which means *there is a way* to support our desired
usable parallel install world order. We "just" need to e.g.
force `$(MSBuildExtensionsPath)` to contain *both* the in-tree
directory *and* the system directory:

```xml
<MSBuildExtensionsPath>In-Tree Directory; System Directory</MSBuildExtensionsPath>
```

Unfortunately, *this isn't easy*. Not all of these properties can be
overridden on the `msbuild.exe` command line. Worse, MSBuild doesn't
allow `;` to be part of a property value, as `;` is a property name
[separator char](https://msdn.microsoft.com/en-us/library/ms164311.aspx)

	msbuild.exe /property:WarningLevel=2;OutDir=bin\Debug

The way to force MSBuild to accept multiple paths in a property value
is by providing a `.exe.config` file with the appropriate values.

~~ Enter `xabuild.exe` ~~

`xabuild.exe` is a nice wrapper around MSBuild for compiling
Xamarin.Android projects using a locally built version of
Xamarin.Android on your system. It seems to work on Windows, macOS, and
Linux and doesn’t require elevation or modifications to your system.

`xabuild.exe` works by doing the following:

 1. Reference `MSBuild.exe` or `MSBuild.dll` depending on the platform
 2. Creates symbolic links to `.NETPortable` and `.NETFramework`
    directories inside the Xamarin.Android build output directory
 3. Overrides MSBuild's `app.config` file to set various properties,
    such as `$(MSBuildExtensionsPath)`.
 4. Run MSBuild’s `Main()` method

~~ Usage ~~

On macOS, `tools/scripts/xabuild` has been updated to use
`xabuild.exe` when `$MSBUILD` is `msbuild:

	$ MSBUILD=msbuild tools/scripts/xabuild /t:SignAndroidPackage samples/HelloWorld/HelloWorld.csproj

When `$MSBUILD` is `xbuild` (the current default), the previous
behavior of overriding `$MSBuildExtensionsPath` and
`$XBUILD_FRAMEWORK_FOLDERS_PATH` is still used.

On Windows, MSBuild 15.3 from Visual Studio 2017 is required.
Simply execute `xabuild.exe`:

	> bin\Debug\bin\xabuild.exe Xamarin.Android-Tests.sln /p:XAIntegratedTests=False

Before `xabuild.exe` existed, `setup-windows.exe` would need to be
executed (as Administrator!) in order for `Xamarin.Android-Tests.sln`
to be built using `msbuild.exe`.
This commit is contained in:
Jonathan Peppers 2017-10-13 16:05:13 -05:00 коммит произвёл Jonathan Pryor
Родитель f91b5d03ef
Коммит f9d15dd203
11 изменённых файлов: 481 добавлений и 92 удалений

Просмотреть файл

@ -116,6 +116,17 @@ to hold file locks on output assemblies containing MSBuild tasks. Until there is
for this, it might be more advisable to use an editor like Visual Studio Code and build via
the command-line.
Windows also requires `xabuild.exe` in place of the `tools/scripts/xabuild` script used
on other platforms.
So a command on macOS such as:
$ tools/scripts/xabuild /t:SignAndroidPackage tests/locales/Xamarin.Android.Locale-Tests/Xamarin.Android.Locale-Tests.csproj
Would be run on Windows as:
> bin\Debug\bin\xabuild.exe /t:SignAndroidPackage tests\locales\Xamarin.Android.Locale-Tests\Xamarin.Android.Locale-Tests.csproj
# Unit Tests
The `xamarin-android` repo contains several unit tests:

Просмотреть файл

@ -105,6 +105,8 @@ Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "netstandard", "src\netstand
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "setup-windows", "tools\setup-windows\setup-windows.csproj", "{73DF9E10-E933-4222-B8E1-F4536FFF9FAD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xabuild", "tools\xabuild\xabuild.csproj", "{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Android", "src\Mono.Android\Mono.Android.csproj", "{66CF299A-CE95-4131-BCD8-DB66E30C4BF7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Android.Export", "src\Mono.Android.Export\Mono.Android.Export.csproj", "{B8105878-D423-4159-A3E7-028298281EC6}"
@ -531,6 +533,18 @@ Global
{1E5501E8-49C1-4659-838D-CC9720C5208F}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
{1E5501E8-49C1-4659-838D-CC9720C5208F}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU
{1E5501E8-49C1-4659-838D-CC9720C5208F}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.Release|AnyCPU.Build.0 = Release|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationDebug|AnyCPU.ActiveCfg = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationDebug|AnyCPU.Build.0 = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationRelease|AnyCPU.ActiveCfg = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationRelease|AnyCPU.Build.0 = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationRelease|Any CPU.ActiveCfg = Debug|Any CPU
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}.XAIntegrationRelease|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8FF78EB6-6FC8-46A7-8A15-EBBA9045C5FA} = {E351F97D-EA4F-4E7F-AAA0-8EBB1F2A4A62}
@ -585,6 +599,7 @@ Global
{B8105878-D423-4159-A3E7-028298281EC6} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{E34BCFA0-CAA4-412C-AA1C-75DB8D67D157} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{1E5501E8-49C1-4659-838D-CC9720C5208F} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}
{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D} = {864062D3-A415-4A6F-9324-5820237BA058}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0

Просмотреть файл

@ -10,11 +10,6 @@ namespace Xamarin.ProjectTools
{
public class Builder : IDisposable
{
const string fixed_osx_xbuild_path = "/Library/Frameworks/Mono.framework/Commands";
const string fixed_linux_xbuild_path = "/usr/bin";
const string xbuildapp = "xbuild";
const string msbuildapp = "msbuild";
public bool IsUnix { get; set; }
public bool RunningMSBuild { get; set; }
public LoggerVerbosity Verbosity { get; set; }
@ -23,18 +18,6 @@ namespace Xamarin.ProjectTools
public string BuildLogFile { get; set; }
public bool ThrowOnBuildFailure { get; set; }
string GetUnixBuildExe ()
{
RunningMSBuild = false;
var tooldir = Directory.Exists (fixed_osx_xbuild_path) ? fixed_osx_xbuild_path : fixed_linux_xbuild_path;
string path = Path.Combine (tooldir, xbuildapp);
if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("USE_MSBUILD"))) {
path = Path.Combine (tooldir, msbuildapp);
RunningMSBuild = true;
}
return File.Exists (path) ? path : msbuildapp;
}
string GetVisualStudio2017Directory ()
{
var editions = new [] {
@ -54,38 +37,20 @@ namespace Xamarin.ProjectTools
return null;
}
string GetWindowsBuildExe ()
{
RunningMSBuild = true;
//First try environment variable
string msbuildExe = Environment.GetEnvironmentVariable ("XA_MSBUILD_EXE");
if (!string.IsNullOrEmpty (msbuildExe) && File.Exists (msbuildExe))
return msbuildExe;
//Next try VS 2017, MSBuild 15.0
var visualStudioDirectory = GetVisualStudio2017Directory ();
if (!string.IsNullOrEmpty(visualStudioDirectory)) {
msbuildExe = Path.Combine (visualStudioDirectory, "MSBuild", "15.0", "Bin", "MSBuild.exe");
if (File.Exists (msbuildExe))
return msbuildExe;
}
//Try older than VS 2017, MSBuild 14.0
msbuildExe = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "MSBuild", "14.0", "Bin", "MSBuild.exe");
if (File.Exists (msbuildExe))
return msbuildExe;
//MSBuild 4.0 last resort
return Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Windows), "Microsoft.NET", "Framework", "v4.0.30319", "MSBuild.exe");
}
public string MSBuildExe {
public string XABuildExe {
get {
return IsUnix
? GetUnixBuildExe ()
: GetWindowsBuildExe ();
if (IsUnix) {
if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("USE_MSBUILD"))) {
RunningMSBuild = true;
}
return Path.GetFullPath (Path.Combine (Root, "..", "..", "tools", "scripts", "xabuild"));
}
#if DEBUG
return Path.GetFullPath (Path.Combine (Root, "..", "..", "bin", "Debug", "bin", "xabuild.exe"));
#else
return Path.GetFullPath (Path.Combine (Root, "..", "..", "bin", "Release", "bin", "xabuild.exe"));
#endif
}
}
@ -200,57 +165,22 @@ namespace Xamarin.ProjectTools
buildLogFullPath, Verbosity.ToString ().ToLower ());
var start = DateTime.UtcNow;
var homeDirectory = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
var androidSdkToolPath = Path.Combine (homeDirectory, "android-toolchain");
var sdkPath = Environment.GetEnvironmentVariable ("ANDROID_SDK_PATH");
if (String.IsNullOrEmpty (sdkPath))
sdkPath = GetPathFromRegistry ("AndroidSdkDirectory");
if (String.IsNullOrEmpty (sdkPath))
sdkPath = Path.GetFullPath (Path.Combine (androidSdkToolPath, "sdk"));
var ndkPath = Environment.GetEnvironmentVariable ("ANDROID_NDK_PATH");
if (String.IsNullOrEmpty (ndkPath))
ndkPath = GetPathFromRegistry ("AndroidNdkDirectory");
if (String.IsNullOrEmpty (ndkPath))
ndkPath = Path.GetFullPath (Path.Combine (androidSdkToolPath, "ndk"));
StringBuilder args = new StringBuilder ();
var psi = new ProcessStartInfo (MSBuildExe);
if (IsUnix) {
if (Directory.Exists (sdkPath)) {
args.AppendFormat ("/p:AndroidSdkDirectory=\"{0}\" ", sdkPath);
}
if (Directory.Exists (ndkPath)) {
args.AppendFormat ("/p:AndroidNdkDirectory=\"{0}\" ", ndkPath);
}
var outdir = Path.GetFullPath (Path.Combine (FrameworkLibDirectory, "..", ".."));
var targetsdir = Path.Combine (FrameworkLibDirectory, "xbuild");
args.AppendFormat (" {0} ", logger);
var args = new StringBuilder ();
var psi = new ProcessStartInfo (XABuildExe);
args.AppendFormat ("{0} /t:{1} {2} /p:UseHostCompilerIfAvailable=false /p:BuildingInsideVisualStudio=true",
QuoteFileName(Path.Combine (Root, projectOrSolution)), target, logger);
if (Directory.Exists (targetsdir)) {
psi.EnvironmentVariables ["TARGETS_DIR"] = targetsdir;
psi.EnvironmentVariables ["MSBuildExtensionsPath"] = targetsdir;
}
if (Directory.Exists (outdir)) {
var frameworksPath = Path.Combine (outdir, "lib", "xamarin.android", "xbuild-frameworks");
psi.EnvironmentVariables ["MONO_ANDROID_PATH"] = outdir;
args.AppendFormat ("/p:MonoDroidInstallDirectory=\"{0}\" ", outdir);
psi.EnvironmentVariables ["XBUILD_FRAMEWORK_FOLDERS_PATH"] = frameworksPath;
if (RunningMSBuild)
args.AppendFormat ($"/p:TargetFrameworkRootPath={frameworksPath} ");
}
args.AppendFormat ("/t:{0} {1} /p:UseHostCompilerIfAvailable=false /p:BuildingInsideVisualStudio=true", target, QuoteFileName (Path.Combine (Root, projectOrSolution)));
}
else {
args.AppendFormat ("{0} /t:{1} {2} /p:UseHostCompilerIfAvailable=false /p:BuildingInsideVisualStudio=true",
QuoteFileName(Path.Combine (Root, projectOrSolution)), target, logger);
}
if (parameters != null) {
foreach (var param in parameters) {
args.AppendFormat (" /p:{0}", param);
}
}
if (RunningMSBuild) {
psi.EnvironmentVariables ["MSBUILD"] = "msbuild";
}
if (environmentVariables != null) {
foreach (var kvp in environmentVariables) {
psi.EnvironmentVariables[kvp.Key] = kvp.Value;
psi.EnvironmentVariables [kvp.Key] = kvp.Value;
}
}
psi.Arguments = args.ToString ();

Просмотреть файл

@ -43,7 +43,7 @@ if [ -z "$MSBUILD" ] ; then
fi
if [ -z "$CONFIGURATION" ]; then
for p in "$*"; do
for p in "$@"; do
case $p in
/property:Configuration=*| \
/p:Configuration=*| \
@ -82,6 +82,11 @@ else
exit 1
fi
if [[ "$MSBUILD" == "msbuild" ]] ; then
exec mono "$prefix/bin/xabuild.exe" "$@"
exit $?
fi
for t in "$TARGETS_DIR" "$prefix/lib/mono/xbuild" "$xa_prefix/xbuild" ; do
if [ -z "$t" -o ! -d "$t" ]; then
continue

26
tools/xabuild/App.config Normal file
Просмотреть файл

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Framework" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Tasks.Core" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Utilities.Core" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Просмотреть файл

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("xabuild")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("Microsoft Corporation")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

Просмотреть файл

@ -0,0 +1,46 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using Mono.Unix;
namespace Xamarin.Android.Build
{
static class SymbolicLink
{
public static bool Create (string source, string target)
{
if (!Directory.Exists (source)) {
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
//NOTE: attempt with and without the AllowUnprivilegedCreate flag, seems to fix Windows Server 2016
if (!CreateSymbolicLink (source, target, SymbolLinkFlag.Directory | SymbolLinkFlag.AllowUnprivilegedCreate) &&
!CreateSymbolicLink (source, target, SymbolLinkFlag.Directory)) {
var error = new Win32Exception ().Message;
Console.Error.WriteLine ($"Unable to create symbolic link from `{source}` to `{target}`: {error}");
return false;
}
} else {
try {
var fileInfo = new UnixFileInfo (target);
fileInfo.CreateSymbolicLink (source);
} catch (Exception exc) {
Console.Error.WriteLine ($"Unable to create symbolic link from `{source}` to `{target}`: {exc.Message}");
return false;
}
}
}
return true;
}
enum SymbolLinkFlag {
File = 0,
Directory = 1,
AllowUnprivilegedCreate = 2,
}
[DllImport ("kernel32.dll")]
[return: MarshalAs (UnmanagedType.I1)]
static extern bool CreateSymbolicLink (string lpSymlinkFileName, string lpTargetFileName, SymbolLinkFlag dwFlags);
}
}

103
tools/xabuild/XABuild.cs Normal file
Просмотреть файл

@ -0,0 +1,103 @@
using Microsoft.Build.CommandLine;
using System;
using System.IO;
using System.Xml;
namespace Xamarin.Android.Build
{
class XABuild
{
[MTAThread]
static int Main ()
{
var paths = new XABuildPaths ();
if (!Directory.Exists (paths.XamarinAndroidBuildOutput)) {
Console.WriteLine ($"Unable to find Xamarin.Android build output at {paths.XamarinAndroidBuildOutput}");
return 1;
}
//Create a custom xabuild.exe.config
CreateConfig (paths);
//Create link to .NETFramework and .NETPortable directory
foreach (var dir in Directory.GetDirectories(paths.SystemProfiles)) {
var name = Path.GetFileName(dir);
if (!SymbolicLink.Create(Path.Combine(paths.FrameworksDirectory, name), dir)) {
return 1;
}
}
return MSBuildApp.Main ();
}
static void CreateConfig (XABuildPaths paths)
{
var xml = new XmlDocument ();
xml.Load (paths.MSBuildConfig);
var toolsets = xml.SelectSingleNode ("configuration/msbuildToolsets/toolset");
SetProperty (toolsets, "VsInstallRoot", paths.VsInstallRoot);
SetProperty (toolsets, "MSBuildToolsPath", paths.MSBuildBin);
SetProperty (toolsets, "MSBuildToolsPath32", paths.MSBuildBin);
SetProperty (toolsets, "MSBuildToolsPath64", paths.MSBuildBin);
SetProperty (toolsets, "MSBuildExtensionsPath", paths.MSBuildExtensionsPath);
SetProperty (toolsets, "MSBuildExtensionsPath32", paths.MSBuildExtensionsPath);
SetProperty (toolsets, "RoslynTargetsPath", Path.Combine (paths.MSBuildBin, "Roslyn"));
SetProperty (toolsets, "AndroidSdkDirectory", paths.AndroidSdkDirectory);
SetProperty (toolsets, "AndroidNdkDirectory", paths.AndroidNdkDirectory);
SetProperty (toolsets, "MonoAndroidToolsDirectory", paths.MonoAndroidToolsDirectory);
SetProperty (toolsets, "TargetFrameworkRootPath", paths.FrameworksDirectory + Path.DirectorySeparatorChar); //NOTE: Must include trailing \
var projectImportSearchPaths = toolsets.SelectSingleNode ("projectImportSearchPaths");
var searchPaths = projectImportSearchPaths.SelectSingleNode ($"searchPaths[@os='{paths.SearchPathsOS}']") as XmlElement;
//NOTE: on Linux, the searchPaths XML element does not exist, so we have to create it
if (searchPaths == null) {
searchPaths = xml.CreateElement ("searchPaths");
searchPaths.SetAttribute ("os", paths.SearchPathsOS);
var property = xml.CreateElement ("property");
property.SetAttribute ("name", "MSBuildExtensionsPath");
property.SetAttribute ("value", "");
searchPaths.AppendChild (property);
property = xml.CreateElement ("property");
property.SetAttribute ("name", "MSBuildExtensionsPath32");
property.SetAttribute ("value", "");
searchPaths.AppendChild (property);
property = xml.CreateElement ("property");
property.SetAttribute ("name", "MSBuildExtensionsPath64");
property.SetAttribute ("value", "");
searchPaths.AppendChild (property);
projectImportSearchPaths.AppendChild (searchPaths);
}
foreach (XmlNode property in searchPaths.SelectNodes ("property[starts-with(@name, 'MSBuildExtensionsPath')]/@value")) {
property.Value = string.Join (";", paths.ProjectImportSearchPaths);
}
xml.Save (paths.XABuildConfig);
}
/// <summary>
/// If the value exists, sets value attribute, else creates the element
/// </summary>
static void SetProperty (XmlNode toolsets, string name, string value)
{
if (string.IsNullOrEmpty (value))
return;
var valueAttribute = toolsets.SelectSingleNode ($"property[@name='{name}']/@value");
if (valueAttribute != null) {
valueAttribute.Value = value;
} else {
var property = toolsets.OwnerDocument.CreateElement ("property");
property.SetAttribute ("name", name);
property.SetAttribute ("value", value);
toolsets.PrependChild (property);
}
}
}
}

Просмотреть файл

@ -0,0 +1,153 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Xamarin.Android.Build
{
/// <summary>
/// Various paths needed by xabuild.exe
/// </summary>
class XABuildPaths
{
public bool IsWindows { get; private set; }
public bool IsMacOS { get; private set; }
public bool IsLinux { get; private set; }
/// <summary>
/// Directory to xabuild.exe
/// </summary>
public string XABuildDirectory { get; private set; }
/// <summary>
/// Path to xabuild.exe.config, on Unix it seems to use MSBuild.dll.config instead
/// </summary>
public string XABuildConfig { get; private set; }
/// <summary>
/// The build output directory of Xamarin.Android, which is a submodule in this repo. Assumes it is already built.
/// </summary>
public string XamarinAndroidBuildOutput { get; private set; }
/// <summary>
/// $(VsInstallRoot), normally C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise
/// </summary>
public string VsInstallRoot { get; private set; }
/// <summary>
/// Path to MSBuild directory
/// </summary>
public string MSBuildPath { get; private set; }
/// <summary>
/// Path to directory of MSBuild.exe
/// </summary>
public string MSBuildBin { get; private set; }
/// <summary>
/// Path to MSBuild's App.config file
/// </summary>
public string MSBuildConfig { get; private set; }
/// <summary>
/// Path to the system directory containing .NETPortable and .NETFramework
/// </summary>
public string SystemProfiles { get; private set; }
/// <summary>
/// Our default $(MSBuildExtensionPath) which should be the "xbuild" directory in the Xamarin.Android build output
/// </summary>
public string MSBuildExtensionsPath { get; private set; }
/// <summary>
/// Array of search paths for MSBuildExtensionsPath
/// </summary>
public string [] ProjectImportSearchPaths { get; private set; }
/// <summary>
/// The xbuild-frameworks directory inside the Xamarin.Android build output
/// </summary>
public string FrameworksDirectory { get; private set; }
/// <summary>
/// Search paths for MSBuildExtensionsPath are specified by an "os" attribute
/// NOTE: Values are "windows", "osx", or "unix"
/// </summary>
public string SearchPathsOS { get; set; }
public string MonoAndroidToolsDirectory { get; private set; }
public string AndroidSdkDirectory { get; private set; }
public string AndroidNdkDirectory { get; private set; }
public XABuildPaths ()
{
IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
IsMacOS = !IsWindows && IsDarwin ();
IsLinux = !IsWindows && !IsMacOS;
XABuildDirectory = Path.GetDirectoryName (GetType ().Assembly.Location);
XamarinAndroidBuildOutput = Path.GetFullPath (Path.Combine (XABuildDirectory, ".."));
string programFiles = Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86);
string userProfile = Environment.GetFolderPath (Environment.SpecialFolder.UserProfile);
string prefix = Path.Combine (XamarinAndroidBuildOutput, "lib", "xamarin.android");
if (IsWindows) {
foreach (var edition in new [] { "Enterprise", "Professional", "Community", "BuildTools" }) {
var vsInstall = Path.Combine (programFiles, "Microsoft Visual Studio", "2017", edition);
if (Directory.Exists (vsInstall)) {
VsInstallRoot = vsInstall;
break;
}
}
if (VsInstallRoot == null)
VsInstallRoot = programFiles;
MSBuildPath = Path.Combine (VsInstallRoot, "MSBuild");
MSBuildBin = Path.Combine (MSBuildPath, "15.0", "Bin");
MSBuildConfig = Path.Combine (MSBuildBin, "MSBuild.exe.config");
ProjectImportSearchPaths = new [] { MSBuildPath, "$(MSBuildProgramFiles32)\\MSBuild" };
SystemProfiles = Path.Combine (programFiles, "Reference Assemblies", "Microsoft", "Framework");
XABuildConfig = Path.Combine (XABuildDirectory, "xabuild.exe.config");
SearchPathsOS = "windows";
} else {
string mono = IsMacOS ? "/Library/Frameworks/Mono.framework/Versions/Current/lib/mono" : "/usr/lib/mono";
MSBuildPath = Path.Combine (mono, "msbuild");
MSBuildBin = Path.Combine (MSBuildPath, "15.0", "bin");
MSBuildConfig = Path.Combine (MSBuildBin, "MSBuild.dll.config");
ProjectImportSearchPaths = new [] { MSBuildPath, Path.Combine (mono, "xbuild") };
SystemProfiles = Path.Combine (mono, "xbuild-frameworks");
XABuildConfig = Path.Combine (XABuildDirectory, "MSBuild.dll.config");
SearchPathsOS = IsMacOS ? "osx" : "unix";
}
FrameworksDirectory = Path.Combine (prefix, "xbuild-frameworks");
MSBuildExtensionsPath = Path.Combine (prefix, "xbuild");
MonoAndroidToolsDirectory = Path.Combine (prefix, "xbuild", "Xamarin", "Android");
AndroidSdkDirectory = Path.Combine (userProfile, "android-toolchain", "sdk");
AndroidNdkDirectory = Path.Combine (userProfile, "android-toolchain", "ndk");
}
[DllImport ("libc")]
static extern int uname (IntPtr buf);
static bool IsDarwin ()
{
IntPtr buf = IntPtr.Zero;
try {
buf = Marshal.AllocHGlobal (8192);
if (uname (buf) == 0) {
string os = Marshal.PtrToStringAnsi (buf);
return os == "Darwin";
}
} catch {
} finally {
if (buf != IntPtr.Zero)
Marshal.FreeHGlobal (buf);
}
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mono.Posix" version="4.0.0.0" targetFramework="net46" />
</packages>

Просмотреть файл

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B7A457E6-9CB6-43F6-BFD6-14D5397FB98D}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Xamarin.Android.Build</RootNamespace>
<AssemblyName>xabuild</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<_MSBuildExtension Condition=" '$(OS)' == 'Windows_NT' ">exe</_MSBuildExtension>
<_MSBuildExtension Condition=" '$(OS)' != 'Windows_NT' ">dll</_MSBuildExtension>
<_MSBuildToolsPath Condition=" !$(MSBuildToolsPath.Contains('xbuild')) ">$(MSBuildToolsPath)</_MSBuildToolsPath>
<_MSBuildToolsPath Condition=" $(MSBuildToolsPath.Contains('xbuild')) ">$([System.IO.Path]::GetFullPath ('$(MSBuildToolsPath)\..\..\..\msbuild\15.0\bin'))</_MSBuildToolsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\bin\Debug\bin</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\bin\Release\bin</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
</Reference>
<Reference Include="MSBuild">
<HintPath>$(_MSBuildToolsPath)\MSBuild.$(_MSBuildExtension)</HintPath>
</Reference>
<Reference Include="Microsoft.Build.Tasks.Core">
<HintPath>$(_MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Build.Utilities.Core">
<HintPath>$(_MSBuildToolsPath)\Microsoft.Build.Utilities.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SymbolicLink.cs" />
<Compile Include="XABuild.cs" />
<Compile Include="XABuildPaths.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">
<Delete Files="$(OutputPath)MSBuild.$(_MSBuildExtension).config" />
</Target>
</Project>