IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
IkvmImporterLauncher.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using System.Runtime.InteropServices;
6using System.Threading;
7using System.Threading.Tasks;
8
9using CliWrap;
10
13
15{
16
21 {
22
23 static readonly string TOOLNAME = "ikvmc";
24 static readonly string TOOLPATH = typeof(IkvmImporterLauncher).Assembly.Location is string s ? Path.GetDirectoryName(s) ?? "" : "";
25
31 public IkvmImporterLauncher(string toolPath, IIkvmToolDiagnosticEventListener listener) :
32 base(TOOLNAME, toolPath, listener)
33 {
34
35 }
36
42 this(TOOLPATH, listener)
43 {
44
45 }
46
51 public IkvmImporterLauncher(string toolPath) :
52 this(toolPath, new IkvmToolNullDiagnosticListener())
53 {
54
55 }
56
63 public async Task<int> ExecuteAsync(IkvmImporterOptions options, CancellationToken cancellationToken = default)
64 {
65 if (options is null)
66 throw new ArgumentNullException(nameof(options));
67
68 using var w = new StringWriter();
69
70 if (options.Output is not null)
71 w.WriteLine($"-out \"{options.Output}\"");
72
73 if (options.Assembly is not null)
74 w.WriteLine($"-assembly \"{options.Assembly}\"");
75
76 if (options.Version is not null)
77 w.WriteLine($"-version {options.Version}");
78
79 if (options.Target is not null)
80 {
81 switch (options.Target)
82 {
83 case IkvmImporterTarget.Library:
84 w.WriteLine($"-target library");
85 break;
86 case IkvmImporterTarget.Exe:
87 w.WriteLine($"-target exe");
88 break;
89 case IkvmImporterTarget.WinExe:
90 w.WriteLine($"-target winexe");
91 break;
92 case IkvmImporterTarget.Module:
93 w.WriteLine($"-target module");
94 break;
95 }
96 }
97
98 if (options.Platform is not null)
99 w.WriteLine($"-platform {options.Platform.ToString().ToLowerInvariant()}");
100
101 if (options.KeyFile is not null)
102 w.WriteLine($"-keyfile \"{options.KeyFile}\"");
103
104 if (options.Key is not null)
105 w.WriteLine($"-key {options.Key}");
106
107 if (options.DelaySign)
108 w.WriteLine("-delay");
109
110 if (options.References is not null)
111 foreach (var reference in options.References)
112 w.WriteLine($"-reference \"{reference}\"");
113
114 if (options.Recurse is not null)
115 foreach (var recurse in options.Recurse)
116 w.WriteLine($"-recurse \"{recurse}\"");
117
118 if (options.Exclude is not null)
119 w.WriteLine($"-exclude \"{options.Exclude}\"");
120
121 if (options.FileVersion is not null)
122 w.WriteLine($"-fileversion {options.FileVersion}");
123
124 if (options.Win32Icon is not null)
125 w.WriteLine($"-win32icon {options.Win32Icon}");
126
127 if (options.Win32Manifest is not null)
128 w.WriteLine($"-win32manifest {options.Win32Manifest}");
129
130 if (options.Resources is not null)
131 foreach (var resource in options.Resources)
132 w.WriteLine($"-resource \"{resource.ResourcePath}={resource.FilePath}\"");
133
134 if (options.ExternalResources is not null)
135 foreach (var resource in options.ExternalResources)
136 w.WriteLine($"-externalresource \"{resource.ResourcePath}={resource.FilePath}\"");
137
138 if (options.CompressResources)
139 w.WriteLine("-compressresources");
140
141 if (options.Debug == IkvmImporterDebugMode.Portable)
142 w.WriteLine("-debug portable");
143 else if (options.Debug == IkvmImporterDebugMode.Embedded)
144 w.WriteLine("-debug embedded");
145
146 if (options.NoAutoSerialization)
147 w.WriteLine("-noautoserialization");
148
149 if (options.NoGlobbing)
150 w.WriteLine("-noglobbing");
151
152 if (options.NoJNI)
153 w.WriteLine("-nojni");
154
155 if (options.Optimize)
156 w.WriteLine("-optimize");
157
158 if (options.OptFields)
159 w.WriteLine("-opt:fields");
160
161 if (options.RemoveAssertions)
162 w.WriteLine("-removeassertions");
163
164 if (options.StrictFinalFieldSemantics)
165 w.WriteLine("-strictfinalfieldsemantics");
166
167 if (options.NoWarn is not null)
168 {
169 if (options.NoWarn.Count == 0)
170 w.WriteLine("-nowarn");
171 else
172 w.WriteLine($"-nowarn {string.Join(",", options.NoWarn)}");
173 }
174
175 if (options.WarningsAsErrors is not null)
176 {
177 if (options.WarningsAsErrors.Count == 0)
178 w.WriteLine("-warnaserror");
179 else
180 w.WriteLine($"-warnaserror {string.Join(",", options.WarningsAsErrors)}");
181 }
182
183 if (options.Main is not null)
184 w.WriteLine($"-main {options.Main}");
185
186 if (options.SrcPath is not null)
187 w.WriteLine($"-srcpath {options.SrcPath}");
188
189 if (options.Apartment is not null)
190 w.WriteLine($"-apartment {options.Apartment}");
191
192 if (options.SetProperties is not null)
193 foreach (var kvp in options.SetProperties)
194 w.WriteLine($"-D \"{kvp.Key}={kvp.Value}\"");
195
196 if (options.NoStackTraceInfo)
197 w.WriteLine("-nostacktraceinfo");
198
199 if (options.PrivatePackages is not null)
200 foreach (var i in options.PrivatePackages)
201 w.WriteLine($"-privatepackage {i}");
202
203 if (options.ClassLoader is not null)
204 w.WriteLine($"-classloader {options.ClassLoader}");
205
206 if (options.SharedClassLoader)
207 w.WriteLine("-sharedclassloader");
208
209 if (options.BaseAddress is not null)
210 w.WriteLine($"-baseaddress {options.BaseAddress}");
211
212 if (options.FileAlign is not null)
213 w.WriteLine($"-filealign {options.FileAlign}");
214
215 if (options.NoPeerCrossReference)
216 w.WriteLine("-nopeercrossreference");
217
218 if (options.NoStdLib)
219 w.WriteLine("-nostdlib");
220
221 if (options.Lib is not null)
222 foreach (var i in options.Lib)
223 w.WriteLine($"-lib \"{i}\"");
224
225 if (options.HighEntropyVA)
226 w.WriteLine("-highentropyva");
227
228 if (options.Static)
229 w.WriteLine("-static");
230
231 if (options.AssemblyAttributes is not null)
232 foreach (var i in options.AssemblyAttributes)
233 w.WriteLine($"-assemblyattributes \"{i}\"");
234
235 if (options.Runtime is not null)
236 w.WriteLine($"-runtime \"{options.Runtime}\"");
237
238 if (options.WarningLevel is not null)
239 w.WriteLine($"-w{options.WarningLevel}");
240
241 if (options.NoParameterReflection)
242 w.WriteLine($"-noparameterreflection");
243
244 if (options.Remap is not null)
245 w.WriteLine($"-remap \"{options.Remap}\"");
246
247 w.WriteLine($"-log json,file=stderr");
248
249 if (options.Input != null)
250 foreach (var i in options.Input)
251 w.Write($"\"{i}\"");
252
253 // prepare path to response file
254 var response = string.IsNullOrWhiteSpace(options.ResponseFile) == false ? Path.GetFullPath(options.ResponseFile) : Path.GetTempFileName();
255
256 // to cancel the executable
257 var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationToken());
258
259 // combine manual cancellation with timeout
260 var ctk = cts.Token;
261 if (options.Timeout != Timeout.Infinite)
262 ctk = CancellationTokenSource.CreateLinkedTokenSource(ctk, new CancellationTokenSource(options.Timeout).Token).Token;
263
264 try
265 {
266 // create response file
267 Directory.CreateDirectory(Path.GetDirectoryName(response) ?? throw new InvalidOperationException());
268 File.WriteAllText(response, w.ToString());
269
270 // locate EXE file
271 string? wrap = null;
272 var exe = GetToolExe();
273 if (exe is null || File.Exists(exe) == false)
274 throw new FileNotFoundException($"Could not locate tool at '{exe}'.");
275
276 // executing on Unix requires some considerations
277 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
278 {
279 // tool executables on Unix need to be invoked through Mono
280 if (exe.EndsWith(".exe"))
281 {
282 wrap = "mono";
283 }
284 else
285 {
286 // else we need to ensure executable bit is set
287
288 try
289 {
290 var psx = Mono.Unix.UnixFileSystemInfo.GetFileSystemEntry(exe);
291 if (psx.FileAccessPermissions.HasFlag(Mono.Unix.FileAccessPermissions.UserExecute) == false)
292 psx.FileAccessPermissions |= Mono.Unix.FileAccessPermissions.UserExecute;
293 }
294 catch (Exception e)
295 {
296 throw new IkvmToolException($"Could not set user executable bit on '{exe}'.", e);
297 }
298 }
299 }
300
301 // assemble list of args
302 var args = new List<string>();
303 args.Add($"@{response}");
304
305 // configure CLI, with wrapper if required
306 Command cli;
307 if (wrap != null)
308 {
309 cli = Cli.Wrap(wrap);
310 args.Insert(0, exe);
311 }
312 else
313 cli = Cli.Wrap(exe);
314
315 // set working directory
316 cli = cli.WithWorkingDirectory(Environment.CurrentDirectory);
317
318 // set configuration of CLI
319 cli = cli.WithArguments(args);
320 cli = cli.WithValidation(CommandResultValidation.None);
321
322 // log the command we're about to run
323 await LogEventAsync(IkvmToolDiagnosticEventLevel.Trace, "Executing {0} {1}", [cli.TargetFilePath, cli.Arguments], ctk);
324
325 // send output to MSBuild (TODO, replace with binary reading)
326 cli = cli.WithStandardErrorPipe(PipeTarget.ToDelegate(l => ParseAndLogEventAsync(l, ctk).AsTask()));
327
328 // execute command
329 using var pid = cli.ExecuteAsync(ctk);
330
331 // windows provides special support for killing subprocesses on termination of parent
332 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
333 {
334 try
335 {
336 if (pid.Task.IsCompleted == false)
337 WindowsChildProcessTracker.AddProcess(Process.GetProcessById(pid.ProcessId));
338 }
339 catch
340 {
341 await LogEventAsync(IkvmToolDiagnosticEventLevel.Error, "Failed to attach child process.", [], ctk);
342 }
343 }
344
345 // wait for the execution to finish
346 var ret = await pid;
347
348 // check that we exited successfully
349 return ret.ExitCode;
350 }
351 finally
352 {
353 // cancel the execution if it is still running
354 if (cts != null)
355 cts.Cancel();
356
357 // clean up response file
358 if (options.ResponseFile == null && response != null && File.Exists(response))
359 {
360 try
361 {
362 File.Delete(response);
363 }
364 catch (IOException)
365 {
366 // did our best
367 }
368 }
369 }
370 }
371
372 }
373
374}
Diagnostic listener that invokes a delegate for each event.
string? GetToolExe()
Gets the path to executable for the given environment.
ValueTask ParseAndLogEventAsync(string line, CancellationToken cancellationToken)
Parses the line and logs it.
ValueTask LogEventAsync(in IkvmToolDiagnosticEvent @event, CancellationToken cancellationToken)
Logs an event if a listener is provided.
Provides methods to launch the IKVM compiler.
IkvmImporterLauncher(string toolPath)
Initializes a new instance.
async Task< int > ExecuteAsync(IkvmImporterOptions options, CancellationToken cancellationToken=default)
Executes the compiler.
IkvmImporterLauncher(string toolPath, IIkvmToolDiagnosticEventListener listener)
Initializes a new instance.
IkvmImporterLauncher(IIkvmToolDiagnosticEventListener listener)
Initializes a new instance.
Options available to the IKVM importer tool.
IkvmImporterTarget? Target
Target to build.
string? Key
Identity of the key to use for signing.
string? Output
Path of the output assembly.
string? KeyFile
Path to the key to use for strong name signing.
IkvmImporterPlatform? Platform
Platform to build.
string? Assembly
Name of the output assembly.
string? Version
Version of the output assembly.
IList< string > References
Set of paths to assemblies to add as references.
bool DelaySign
Whether the assembly should be delay signed.
Allows processes to be automatically killed if this parent process unexpectedly quits....
static void AddProcess(Process process)
Add the process to be tracked. If our current process is killed, the child processes that we are trac...
IkvmToolDiagnosticEventLevel
Describes the level of diagnostic event a tool could emit.