1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace ILLink.Tasks
{
public class ILLink : ToolTask
{
/// <summary>
/// Paths to the assembly files that should be considered as
/// input to the linker.
/// Each path can also have an "action" metadata,
/// which will set the illink action to take for
/// that assembly.
/// </summary>
[Required]
public ITaskItem [] AssemblyPaths { get; set; }
/// <summary>
/// Paths to assembly files that are reference assemblies,
/// representing the surface area for compilation.
/// </summary>
public ITaskItem [] ReferenceAssemblyPaths { get; set; }
/// <summary>
/// The names of the assemblies to root. This should contain
/// assembly names without an extension, not file names or
/// paths. Exactly which parts of the assemblies get rooted
/// is subject to change. Currently these get passed to
/// illink with "-a", which roots the entry point for
/// executables, and everything for libraries. To control
/// the linker more explicitly, either pass descriptor
/// files, or pass extra arguments for illink.
/// </summary>
[Required]
public ITaskItem [] RootAssemblyNames { get; set; }
/// <summary>
/// The directory in which to place linked assemblies.
/// </summary>
[Required]
public ITaskItem OutputDirectory { get; set; }
/// <summary>
/// A list of XML root descriptor files specifying linker
/// roots at a granular level. See the mono/linker
/// documentation for details about the format.
/// </summary>
public ITaskItem [] RootDescriptorFiles { get; set; }
/// <summary>
/// Boolean specifying whether to clear initlocals flag on methods.
/// </summary>
public bool ClearInitLocals { get; set; }
/// <summary>
/// A comma-separated list of assemblies whose methods
/// should have initlocals flag cleared if ClearInitLocals is true.
/// </summary>
public string ClearInitLocalsAssemblies { get; set; }
/// <summary>
/// Extra arguments to pass to illink, delimited by spaces.
/// </summary>
public string ExtraArgs { get; set; }
/// <summary>
/// Make illink dump dependencies file for linker analyzer tool.
/// </summary>
public bool DumpDependencies { get; set; }
private static string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH";
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;
}
}
/// ToolTask implementation
protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High;
protected override string ToolName => Path.GetFileName (DotNetPath);
protected override string GenerateFullPathToTool () => DotNetPath;
private string _illinkPath = "";
public string ILLinkPath {
get {
if (!String.IsNullOrEmpty (_illinkPath))
return _illinkPath;
var taskDirectory = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location);
// The linker always runs on .NET Core, even when using desktop MSBuild to host ILLink.Tasks.
_illinkPath = Path.Combine (Path.GetDirectoryName (taskDirectory), "netcoreapp3.0", "illink.dll");
return _illinkPath;
}
set => _illinkPath = value;
}
private static string Quote (string path)
{
return $"\"{path.TrimEnd('\\')}\"";
}
protected override string GenerateCommandLineCommands ()
{
var args = new StringBuilder ();
args.Append (Quote (ILLinkPath));
return args.ToString ();
}
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));
HashSet<string> 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));
string 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 ();
}
}
}
|