Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Fix linker path on Windows #18285

Merged
merged 6 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1497,4 +1497,11 @@
<value>The "{0}" task was not given a value for the parameter "{1}", which is required when building on this platform.</value>
</data>

<data name="E7115" xml:space="preserve">
<value>The illink assembly doesn't exist: '{0}'.</value>
<comment>
{0}: the path to the linker assembly
</comment>
</data>

</root>
139 changes: 13 additions & 126 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/FindAotCompilerTaskBase.cs
Original file line number Diff line number Diff line change
@@ -1,160 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Xamarin.Localization.MSBuild;
using Threading = System.Threading.Tasks;

namespace Xamarin.MacDev.Tasks {
public abstract class FindAotCompilerTaskBase : XamarinTask {
public abstract class FindAotCompilerTaskBase : XamarinBuildTask {
[Required]
public ITaskItem [] MonoAotCrossCompiler { get; set; }

public bool KeepTemporaryOutput { get; set; }

[Required]
public string RuntimeIdentifier { get; set; }

[Output]
public string AotCompiler { get; set; }

public override bool Execute ()
{
// If we can't find the AOT compiler path in MonoAotCrossCompiler, evaluate a project file that does know how to find it.
// This happens when executing remotely from Windows, because the MonoAotCrossCompiler item group will be empty in that case.
var targetName = "ComputeAotCompilerPath";
var target = $@"<Target Name=""{targetName}"">
<PropertyGroup>
<_XamarinAOTCompiler>@(MonoAotCrossCompiler->WithMetadataValue(""RuntimeIdentifier"", ""$(RuntimeIdentifier)""))</_XamarinAOTCompiler>
</PropertyGroup>
<WriteLinesToFile File=""$(OutputFilePath)"" Lines=""$(_XamarinAOTCompiler)"" />
</Target>";

if (MonoAotCrossCompiler?.Length > 0 && string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("XAMARIN_FORCE_AOT_COMPILER_PATH_COMPUTATION"))) {
var aotCompilerItem = MonoAotCrossCompiler.SingleOrDefault (v => v.GetMetadata ("RuntimeIdentifier") == RuntimeIdentifier);

if (aotCompilerItem is null) {
Log.LogMessage (MessageImportance.Low, "Unable to find the AOT compiler for the RuntimeIdentifier '{0}' in the MonoAotCrossCompiler item group", RuntimeIdentifier);
AotCompiler = ComputeAotCompilerPath ();
AotCompiler = ComputeValueUsingTarget (target, targetName);
} else {
AotCompiler = aotCompilerItem.ItemSpec;
}
} else {
AotCompiler = ComputeAotCompilerPath ();
AotCompiler = ComputeValueUsingTarget (target, targetName);
}

if (!File.Exists (AotCompiler))
Log.LogError (MSBStrings.E7081 /*"The AOT compiler '{0}' does not exist." */, AotCompiler);

return !Log.HasLoggedErrors;
}

// If we can't find the AOT compiler path in MonoAotCrossCompiler, evaluate a project file that does know how to find it.
// This happens when executing remotely from Windows, because the MonoAotCrossCompiler item group will be empty in that case.
string ComputeAotCompilerPath ()
{
var projectPath = Path.GetTempFileName ();

File.Delete (projectPath);
projectPath += ".csproj";

var csproj = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<TargetFramework>net{TargetFramework.Version}-{PlatformName}</TargetFramework>
</PropertyGroup>
<Target Name=""ComputeAotCompilerPath"">
<PropertyGroup>
<_XamarinAOTCompiler>@(MonoAotCrossCompiler->WithMetadataValue(""RuntimeIdentifier"", ""$(RuntimeIdentifier)""))</_XamarinAOTCompiler>
</PropertyGroup>
<WriteLinesToFile File=""$(OutputFilePath)"" Lines=""$(_XamarinAOTCompiler)"" />
</Target>
</Project>
";
File.WriteAllText (projectPath, csproj);

var dotnetPath = Environment.GetEnvironmentVariable ("DOTNET_HOST_PATH");

if (string.IsNullOrEmpty (dotnetPath)) {
dotnetPath = "dotnet";
}

var environment = default (Dictionary<string, string>);
var customHome = Environment.GetEnvironmentVariable ("DOTNET_CUSTOM_HOME");

if (!string.IsNullOrEmpty (customHome)) {
environment = new Dictionary<string, string> { { "HOME", customHome } };
}

try {
ExecuteRestoreAsync (dotnetPath, projectPath, environment).Wait ();

return ExecuteBuildAsync (dotnetPath, projectPath, environment).Result;
} finally {
if (KeepTemporaryOutput) {
Log.LogMessage (MessageImportance.Normal, $"Temporary project for the FindAotCompiler task: {projectPath}");
} else {
File.Delete (projectPath);
}
}
}

async Threading.Task ExecuteRestoreAsync (string dotnetPath, string projectPath, Dictionary<string, string> environment)
rolfbjarne marked this conversation as resolved.
Show resolved Hide resolved
{
var binlog = GetTempBinLog ();
var arguments = new List<string> ();

arguments.Add ("restore");

var dotnetDir = Path.GetDirectoryName (dotnetPath);
var configFile = Path.Combine (dotnetDir, "NuGet.config");

if (File.Exists (configFile)) {
arguments.Add ("/p:RestoreConfigFile=" + configFile);
}

arguments.Add ("/bl:" + binlog);
arguments.Add (projectPath);

try {
await ExecuteAsync (dotnetPath, arguments, environment: environment);
} finally {
if (KeepTemporaryOutput) {
Log.LogMessage (MessageImportance.Normal, $"Temporary restore log for the FindAotCompiler task: {binlog}");
} else {
File.Delete (binlog);
}
}
}

async Threading.Task<string> ExecuteBuildAsync (string dotnetPath, string projectPath, Dictionary<string, string> environment)
{
var outputFile = Path.GetTempFileName ();
var binlog = GetTempBinLog ();
var arguments = new List<string> ();

arguments.Add ("build");
arguments.Add ("/p:OutputFilePath=" + outputFile);
arguments.Add ("/p:RuntimeIdentifier=" + RuntimeIdentifier);
arguments.Add ("/t:ComputeAotCompilerPath");
arguments.Add ("/bl:" + binlog);
arguments.Add (projectPath);

try {
await ExecuteAsync (dotnetPath, arguments, environment: environment);

return File.ReadAllText (outputFile).Trim ();
} finally {
if (KeepTemporaryOutput) {
Log.LogMessage (MessageImportance.Normal, $"Temporary output for the FindAotCompiler task: {outputFile}");
Log.LogMessage (MessageImportance.Normal, $"Temporary build log for the FindAotCompiler task: {binlog}");
} else {
File.Delete (outputFile);
File.Delete (binlog);
}
}
}

string GetTempBinLog ()
{
var binlog = Path.GetTempFileName ();

File.Delete (binlog);
binlog += ".binlog";

return binlog;
}
}
}

48 changes: 48 additions & 0 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/FindILLink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Tasks;
using Xamarin.Localization.MSBuild;
using Xamarin.Messaging.Build.Client;

namespace Xamarin.MacDev.Tasks {
public class FindILLink : XamarinBuildTask, ITaskCallback, ICancelableTask {
[Output]
public string ILLinkPath { get; set; }

public override bool Execute ()
{
if (this.ShouldExecuteRemotely (SessionId))
return new TaskRunner (SessionId, BuildEngine4).RunAsync (this).Result;

var targetName = "ComputeILLinkTaskPath";
var target = $@"<Target Name=""{targetName}"">
<WriteLinesToFile File=""$(OutputFilePath)"" Lines=""$(ILLinkTasksAssembly)"" />
</Target>";

var illinkTaskPath = ComputeValueUsingTarget (target, targetName);

if (!string.IsNullOrEmpty (illinkTaskPath))
ILLinkPath = Path.Combine (Path.GetDirectoryName (illinkTaskPath), "illink.dll");

if (!File.Exists (ILLinkPath))
Log.LogError (MSBStrings.E7115 /*"The illink assembly doesn't exist: '{0}'" */, ILLinkPath);

return !Log.HasLoggedErrors;
}

public IEnumerable<ITaskItem> GetAdditionalItemsToBeCopied () => Enumerable.Empty<ITaskItem> ();

public bool ShouldCopyToBuildServer (ITaskItem item) => false;

public bool ShouldCreateOutputFile (ITaskItem item) => false;

public void Cancel ()
{
if (ShouldExecuteRemotely ())
BuildConnection.CancelAsync (BuildEngine4).Wait ();
}
}
}

113 changes: 113 additions & 0 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/XamarinBuildTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Xamarin.Localization.MSBuild;
using Threading = System.Threading.Tasks;

namespace Xamarin.MacDev.Tasks {
public abstract class XamarinBuildTask : XamarinTask {
public bool KeepTemporaryOutput { get; set; }

[Required]
public string RuntimeIdentifier { get; set; }

/// <summary>
/// Runs the target passed in computeValueTarget and returns its result.
/// The target must write the result into a text file using $(OutputFilePath) as path.
/// </summary>
/// <returns></returns>
protected string ComputeValueUsingTarget (string computeValueTarget, string targetName)
{
var projectDirectory = Path.GetTempFileName ();
File.Delete (projectDirectory);
Directory.CreateDirectory (projectDirectory);
var projectPath = Path.Combine (projectDirectory, targetName + ".csproj");

var csproj = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<TargetFramework>net{TargetFramework.Version}-{PlatformName}</TargetFramework>
</PropertyGroup>
{computeValueTarget}
</Project>
";
File.WriteAllText (projectPath, csproj);

var dotnetPath = Environment.GetEnvironmentVariable ("DOTNET_HOST_PATH");

if (string.IsNullOrEmpty (dotnetPath)) {
dotnetPath = "dotnet";
}

var environment = default (Dictionary<string, string>);
var customHome = Environment.GetEnvironmentVariable ("DOTNET_CUSTOM_HOME");

if (!string.IsNullOrEmpty (customHome)) {
environment = new Dictionary<string, string> { { "HOME", customHome } };
}

try {
ExecuteRestoreAsync (dotnetPath, projectPath, targetName, environment).Wait ();

return ExecuteBuildAsync (dotnetPath, projectPath, targetName, environment).Result;
} finally {
if (KeepTemporaryOutput) {
Log.LogMessage (MessageImportance.Normal, $"Temporary project directory for the {targetName} task: {projectDirectory}");
} else {
Directory.Delete (projectDirectory, true);
}
}
}

async Threading.Task ExecuteRestoreAsync (string dotnetPath, string projectPath, string targetName, Dictionary<string, string> environment)
{
var projectDirectory = Path.GetDirectoryName (projectPath);
var binlog = Path.Combine (projectDirectory, targetName + ".binlog");
var arguments = new List<string> ();

arguments.Add ("restore");

var dotnetDir = Path.GetDirectoryName (dotnetPath);
var configFile = Path.Combine (dotnetDir, "NuGet.config");

if (File.Exists (configFile)) {
arguments.Add ("/p:RestoreConfigFile=" + configFile);
}

arguments.Add ("/bl:" + binlog);
arguments.Add (projectPath);

try {
await ExecuteAsync (dotnetPath, arguments, environment: environment);
} finally {
if (KeepTemporaryOutput) {
Log.LogMessage (MessageImportance.Normal, $"Temporary restore log for the {targetName} task: {binlog}");
} else {
File.Delete (binlog);
}
}
}

async Threading.Task<string> ExecuteBuildAsync (string dotnetPath, string projectPath, string targetName, Dictionary<string, string> environment)
{
var projectDirectory = Path.GetDirectoryName (projectPath);
var outputFile = Path.Combine (projectDirectory, "Output.txt");
var binlog = Path.Combine (projectDirectory, targetName + ".binlog");
var arguments = new List<string> ();

arguments.Add ("build");
arguments.Add ("/p:OutputFilePath=" + outputFile);
arguments.Add ("/p:RuntimeIdentifier=" + RuntimeIdentifier);
arguments.Add ($"/t:{targetName}");
arguments.Add ("/bl:" + binlog);
arguments.Add (projectPath);

await ExecuteAsync (dotnetPath, arguments, environment: environment);

return File.ReadAllText (outputFile).Trim ();
}
}
}

Loading