-
Notifications
You must be signed in to change notification settings - Fork 516
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[release/7.0.2xx-xcode14.3] [dotnet] Fix linker path on Windows (#18298)
When building from Windows, we need to pass the path to the illink assembly located on the Mac to the linker task. The educated guess we've been using is a bit fragile and has been getting us problems almost on each new .NET major release. On top of that, from .NET 8 the linker is in a separate NuGet package, so the assembly is no longer located in the SDK directory on the Mac. The fix is to follow the same approach we use to find the AOT compiler on the Mac by running a target that finds that information on the remote Mac, and brings it back to Windows, where it is cached and use across build. Created a new XamarinBuildTask class to share most of the code needed for this approach. Backport of #18285. Co-authored-by: Emanuel Fernandez Dell'Oca <[email protected]>
- Loading branch information
1 parent
edd0594
commit 6d9ed79
Showing
5 changed files
with
217 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 13 additions & 126 deletions
139
msbuild/Xamarin.MacDev.Tasks/Tasks/FindAotCompilerTaskBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 == 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) | ||
{ | ||
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; | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 (); | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 (); | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as outdated.
Sorry, something went wrong.