-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Use a task to launch the linker #17313
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
56 changes: 56 additions & 0 deletions
56
src/Components/Blazor/Build/src/Tasks/BlazorCreateRootDescriptorFile.cs
This file contains hidden or 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,56 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Xml; | ||
using System.Xml.Linq; | ||
using Microsoft.Build.Framework; | ||
using Microsoft.Build.Utilities; | ||
|
||
namespace Microsoft.AspNetCore.Blazor.Build | ||
{ | ||
// Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/CreateRootDescriptorFile.cs | ||
public class BlazorCreateRootDescriptorFile : Task | ||
{ | ||
[Required] | ||
public ITaskItem[] AssemblyNames { get; set; } | ||
|
||
[Required] | ||
public ITaskItem RootDescriptorFilePath { get; set; } | ||
|
||
public override bool Execute() | ||
{ | ||
using var fileStream = File.Create(RootDescriptorFilePath.ItemSpec); | ||
var assemblyNames = AssemblyNames.Select(a => a.ItemSpec); | ||
|
||
WriteRootDescriptor(fileStream, assemblyNames); | ||
return true; | ||
} | ||
|
||
internal static void WriteRootDescriptor(Stream stream, IEnumerable<string> assemblyNames) | ||
{ | ||
var roots = new XElement("linker"); | ||
foreach (var assemblyName in assemblyNames) | ||
{ | ||
roots.Add(new XElement("assembly", | ||
new XAttribute("fullname", assemblyName), | ||
new XElement("type", | ||
new XAttribute("fullname", "*"), | ||
new XAttribute("required", "true")))); | ||
} | ||
|
||
var xmlWriterSettings = new XmlWriterSettings | ||
{ | ||
Indent = true, | ||
OmitXmlDeclaration = true | ||
}; | ||
|
||
using var writer = XmlWriter.Create(stream, xmlWriterSettings); | ||
var xDocument = new XDocument(roots); | ||
|
||
xDocument.Save(writer); | ||
} | ||
} | ||
} |
This file contains hidden or 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,189 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
using Microsoft.Build.Framework; | ||
using Microsoft.Build.Utilities; | ||
|
||
namespace Microsoft.AspNetCore.Blazor.Build.Tasks | ||
{ | ||
// Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/LinkTask.cs | ||
public class BlazorILLink : ToolTask | ||
{ | ||
private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; | ||
|
||
[Required] | ||
public string ILLinkPath { get; set; } | ||
|
||
[Required] | ||
public ITaskItem[] AssemblyPaths { get; set; } | ||
|
||
public ITaskItem[] ReferenceAssemblyPaths { get; set; } | ||
|
||
[Required] | ||
public ITaskItem[] RootAssemblyNames { get; set; } | ||
|
||
[Required] | ||
public ITaskItem OutputDirectory { get; set; } | ||
|
||
public ITaskItem[] RootDescriptorFiles { get; set; } | ||
|
||
public bool ClearInitLocals { get; set; } | ||
|
||
public string ClearInitLocalsAssemblies { get; set; } | ||
|
||
public string ExtraArgs { get; set; } | ||
|
||
public bool DumpDependencies { get; set; } | ||
|
||
private string _dotnetPath; | ||
|
||
private string DotNetPath | ||
{ | ||
get | ||
{ | ||
if (!string.IsNullOrEmpty(_dotnetPath)) | ||
{ | ||
return _dotnetPath; | ||
} | ||
|
||
_dotnetPath = Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName); | ||
if (string.IsNullOrEmpty(_dotnetPath)) | ||
{ | ||
throw new InvalidOperationException($"{DotNetHostPathEnvironmentName} is not set"); | ||
} | ||
|
||
return _dotnetPath; | ||
} | ||
} | ||
|
||
protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High; | ||
|
||
protected override string ToolName => Path.GetFileName(DotNetPath); | ||
|
||
protected override string GenerateFullPathToTool() => DotNetPath; | ||
|
||
protected override string GenerateCommandLineCommands() => ILLinkPath; | ||
|
||
private static string Quote(string path) | ||
{ | ||
return $"\"{path.TrimEnd('\\')}\""; | ||
} | ||
|
||
protected override string GenerateResponseFileCommands() | ||
{ | ||
var args = new StringBuilder(); | ||
|
||
if (RootDescriptorFiles != null) | ||
{ | ||
foreach (var rootFile in RootDescriptorFiles) | ||
{ | ||
args.Append("-x ").AppendLine(Quote(rootFile.ItemSpec)); | ||
} | ||
} | ||
|
||
foreach (var assemblyItem in RootAssemblyNames) | ||
{ | ||
args.Append("-a ").AppendLine(Quote(assemblyItem.ItemSpec)); | ||
} | ||
|
||
var assemblyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
foreach (var assembly in AssemblyPaths) | ||
{ | ||
var assemblyPath = assembly.ItemSpec; | ||
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); | ||
|
||
// If there are multiple paths with the same assembly name, only use the first one. | ||
if (!assemblyNames.Add(assemblyName)) | ||
{ | ||
continue; | ||
} | ||
|
||
args.Append("-reference ") | ||
.AppendLine(Quote(assemblyPath)); | ||
|
||
var action = assembly.GetMetadata("action"); | ||
if ((action != null) && (action.Length > 0)) | ||
{ | ||
args.Append("-p "); | ||
args.Append(action); | ||
args.Append(" ").AppendLine(Quote(assemblyName)); | ||
} | ||
} | ||
|
||
if (ReferenceAssemblyPaths != null) | ||
{ | ||
foreach (var assembly in ReferenceAssemblyPaths) | ||
{ | ||
var assemblyPath = assembly.ItemSpec; | ||
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); | ||
|
||
// Don't process references for which we already have | ||
// implementation assemblies. | ||
if (assemblyNames.Contains(assemblyName)) | ||
{ | ||
continue; | ||
} | ||
|
||
args.Append("-reference ").AppendLine(Quote(assemblyPath)); | ||
|
||
// Treat reference assemblies as "skip". Ideally we | ||
// would not even look at the IL, but only use them to | ||
// resolve surface area. | ||
args.Append("-p skip ").AppendLine(Quote(assemblyName)); | ||
} | ||
} | ||
|
||
if (OutputDirectory != null) | ||
{ | ||
args.Append("-out ").AppendLine(Quote(OutputDirectory.ItemSpec)); | ||
} | ||
|
||
if (ClearInitLocals) | ||
{ | ||
args.AppendLine("--enable-opt clearinitlocals"); | ||
if ((ClearInitLocalsAssemblies != null) && (ClearInitLocalsAssemblies.Length > 0)) | ||
{ | ||
args.Append("-m ClearInitLocalsAssemblies "); | ||
args.AppendLine(ClearInitLocalsAssemblies); | ||
} | ||
} | ||
|
||
if (ExtraArgs != null) | ||
{ | ||
args.AppendLine(ExtraArgs); | ||
} | ||
|
||
if (DumpDependencies) | ||
{ | ||
args.AppendLine("--dump-dependencies"); | ||
} | ||
|
||
return args.ToString(); | ||
} | ||
|
||
protected override bool HandleTaskExecutionErrors() | ||
{ | ||
// Show a slightly better error than the standard ToolTask message that says "dotnet" failed. | ||
Log.LogError($"ILLink failed with exited code {ExitCode}."); | ||
return false; | ||
} | ||
|
||
protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) | ||
{ | ||
if (!string.IsNullOrEmpty(singleLine) && singleLine.StartsWith("Unhandled exception.", StringComparison.Ordinal)) | ||
{ | ||
// The Mono linker currently prints out an entire stack trace when the linker fails. | ||
// We want to show something actionable in the VS Error window. | ||
Log.LogError(singleLine); | ||
} | ||
else | ||
{ | ||
base.LogEventsFromTextOutput(singleLine, messageImportance); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little iffy, but improves the error reporting experience during
dotnet build
and in the IDE: