Skip to content

Commit 722d9bd

Browse files
committed
out-of-proc compiler so it can be net6.0
1 parent 5009ae3 commit 722d9bd

File tree

11 files changed

+279
-67
lines changed

11 files changed

+279
-67
lines changed

src/Peachpie.CodeAnalysis/CommandLine/PhpCompilerDriver.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using Microsoft.CodeAnalysis;
22
using System;
33
using System.Collections.Generic;
4+
using System.Diagnostics;
45
using System.IO;
56
using System.Linq;
7+
using System.Runtime.InteropServices;
68
using System.Text;
79
using System.Threading;
810
using System.Threading.Tasks;
@@ -11,6 +13,74 @@ namespace Pchp.CodeAnalysis.CommandLine
1113
{
1214
public static class PhpCompilerDriver
1315
{
16+
/// <summary>
17+
/// Run by <c>Peachpie.NET.Sdk</c>.<c>BuildTask</c>
18+
/// </summary>
19+
public static int Main(string[] args)
20+
{
21+
Debugger.Launch();
22+
23+
// $"/baseDirectory:{BasePath}",
24+
// $"/sdkDirectory:{NetFrameworkPath}",
25+
// $"/additionalReferenceDirectories:{libs}",
26+
// $"responseFile:{ResponseFilePath}"
27+
28+
string responseFile = null;
29+
string baseDirectory = null;
30+
string sdkDirectory = null;
31+
string additionalReferenceDirectories = null;
32+
33+
// arguments passed to the {CommandLineParser}
34+
var passedArgs = new List<string>(args.Length);
35+
36+
for (int i = 0; i < args.Length; i++)
37+
{
38+
if (PhpCommandLineParser.TryParseOption2(
39+
args[i],
40+
out var name,
41+
out var value
42+
))
43+
{
44+
if (string.Equals(name, nameof(responseFile), StringComparison.OrdinalIgnoreCase))
45+
{
46+
responseFile = value;
47+
continue;
48+
}
49+
if (string.Equals(name, nameof(baseDirectory), StringComparison.OrdinalIgnoreCase))
50+
{
51+
baseDirectory = value;
52+
continue;
53+
}
54+
if (string.Equals(name, nameof(sdkDirectory), StringComparison.OrdinalIgnoreCase))
55+
{
56+
sdkDirectory = value;
57+
continue;
58+
}
59+
if (string.Equals(name, nameof(additionalReferenceDirectories), StringComparison.OrdinalIgnoreCase))
60+
{
61+
additionalReferenceDirectories = value;
62+
continue;
63+
}
64+
}
65+
66+
//
67+
passedArgs.Add(args[i]);
68+
}
69+
70+
return Run(
71+
PhpCommandLineParser.Default,
72+
responseFile: responseFile,
73+
args: passedArgs.ToArray(),
74+
clientDirectory: null,
75+
baseDirectory: baseDirectory,
76+
sdkDirectory: sdkDirectory,
77+
additionalReferenceDirectories: additionalReferenceDirectories,
78+
analyzerLoader: new SimpleAnalyzerAssemblyLoader(),
79+
output: Console.Out,
80+
cancellationToken: default(CancellationToken) // TODO: from input
81+
);
82+
}
83+
1484
public static int Run(CommandLineParser parser, string responseFile, string[] args,
1585
string clientDirectory, string baseDirectory, string sdkDirectory,
1686
string additionalReferenceDirectories,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Pchp.CodeAnalysis
6+
{
7+
class SimpleAnalyzerAssemblyLoader : Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader
8+
{
9+
public void AddDependencyLocation(string fullPath)
10+
{
11+
throw new NotImplementedException();
12+
}
13+
14+
public System.Reflection.Assembly LoadFromPath(string fullPath)
15+
{
16+
throw new NotImplementedException();
17+
}
18+
}
19+
}

src/Peachpie.CodeAnalysis/Peachpie.CodeAnalysis.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net6.0</TargetFrameworks>
5+
<OutputType>exe</OutputType>
56
<NoWarn>$(NoWarn);1591</NoWarn>
67
<AssemblyName>Peachpie.CodeAnalysis</AssemblyName>
78
<PackageId>Peachpie.CodeAnalysis</PackageId>

src/Peachpie.NET.Sdk/BuildTask.cs

Lines changed: 152 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Threading;
88
using Microsoft.Build.Framework;
99
using Microsoft.Build.Utilities;
10-
using Pchp.CodeAnalysis.CommandLine;
1110

1211
namespace Peachpie.NET.Sdk.Tools
1312
{
@@ -16,6 +15,12 @@ namespace Peachpie.NET.Sdk.Tools
1615
/// </summary>
1716
public class BuildTask : Task, ICancelableTask // TODO: ToolTask
1817
{
18+
/// <summary>
19+
/// Path to <c>Peachpie.CodeAnalysis</c> executable.
20+
/// </summary>
21+
[Required]
22+
public string PeachpieCompilerFullPath { get; set; }
23+
1924
/// <summary></summary>
2025
[Required]
2126
public string OutputPath { get; set; }
@@ -28,6 +33,11 @@ public class BuildTask : Task, ICancelableTask // TODO: ToolTask
2833
[Required]
2934
public string TempOutputPath { get; set; }
3035

36+
/// <summary>
37+
/// Optional <c>.rsp</c> file to be created.
38+
/// </summary>
39+
public string ResponseFilePath { get; set; }
40+
3141
/// <summary></summary>
3242
[Required]
3343
public string TargetFramework { get; set; }
@@ -183,14 +193,14 @@ public override bool Execute()
183193
AddNoEmpty(args, "sourcelink", SourceLink);
184194
AddNoEmpty(args, "codepage", CodePage);
185195
AddNoEmpty(args, "subdir", PhpRelativePath);
186-
187-
if (DefineConstants != null)
188-
{
189-
foreach (var d in DefineConstants)
190-
{
191-
args.Add("/d:" + d);
192-
}
193-
}
196+
197+
if (DefineConstants != null)
198+
{
199+
foreach (var d in DefineConstants)
200+
{
201+
args.Add("/d:" + d);
202+
}
203+
}
194204

195205
if (ReferencePath != null && ReferencePath.Length != 0)
196206
{
@@ -224,16 +234,14 @@ public override bool Execute()
224234
);
225235

226236
// /attr:FQN("value1","value2","value3")
227-
args.Add($@"/attr:{attr.ItemSpec}({
228-
string.Join(
237+
args.Add($@"/attr:{attr.ItemSpec}({string.Join(
229238
",",
230239
Enumerable.Range(1, 128)
231240
.Select(n => $"_Parameter{n}")
232241
.TakeWhile(prop => props.Contains(prop))
233242
.Select(prop => attr.GetMetadata(prop))
234243
.Select(value => $"\"{value.Replace("\"", "\\\"")}\"")
235-
)
236-
})");
244+
)})");
237245
}
238246
}
239247

@@ -278,17 +286,26 @@ public override bool Execute()
278286
args.AddRange(Compile.Distinct(StringComparer.InvariantCulture));
279287
}
280288

281-
#if DEBUG
282-
// save the arguments as .rsp file for debugging purposes:
283-
try
289+
if (DebuggerAttach)
284290
{
285-
File.WriteAllText(Path.Combine(TempOutputPath, "dotnet-php.rsp"), string.Join(Environment.NewLine, args));
291+
args.Add("/attach");
292+
Debugger.Launch();
286293
}
287-
catch (Exception ex)
294+
295+
// save the arguments as .rsp file for debugging purposes:
296+
if (!string.IsNullOrEmpty(ResponseFilePath))
288297
{
289-
this.Log.LogWarningFromException(ex);
298+
try
299+
{
300+
File.WriteAllText(
301+
Path.Combine(BasePath, ResponseFilePath), string.Join(Environment.NewLine, args.Select(line => $"\"{line.Replace("\\", "\\\\")}\""))
302+
);
303+
}
304+
catch (Exception ex)
305+
{
306+
this.Log.LogWarningFromException(ex);
307+
}
290308
}
291-
#endif
292309

293310
//
294311
// run the compiler:
@@ -299,28 +316,46 @@ public override bool Execute()
299316
return false;
300317
}
301318

302-
// Debugger.Launch
303-
if (DebuggerAttach)
304-
{
305-
Debugger.Launch();
306-
}
307-
308-
// compile
319+
// compile using out-of-process compiler:
309320
try
310321
{
311-
var resultCode = PhpCompilerDriver.Run(
312-
PhpCommandLineParser.Default,
313-
null,
314-
args: args.ToArray(),
315-
clientDirectory: null,
316-
baseDirectory: BasePath,
317-
sdkDirectory: NetFrameworkPath,
318-
additionalReferenceDirectories: libs,
319-
analyzerLoader: new SimpleAnalyzerAssemblyLoader(),
320-
output: new LogWriter(this.Log),
321-
cancellationToken: _cancellation.Token);
322-
323-
return resultCode == 0;
322+
//var resultCode = PhpCompilerDriver.Run(
323+
// PhpCommandLineParser.Default,
324+
// null,
325+
// args: args.ToArray(),
326+
// clientDirectory: null,
327+
// baseDirectory: BasePath,
328+
// sdkDirectory: NetFrameworkPath,
329+
// additionalReferenceDirectories: libs,
330+
// analyzerLoader: new SimpleAnalyzerAssemblyLoader(),
331+
// output: new LogWriter(this.Log),
332+
// cancellationToken: _cancellation.Token);
333+
334+
var compilerArgs = new List<string>()
335+
{
336+
$"/baseDirectory:{BasePath}",
337+
$"/sdkDirectory:{NetFrameworkPath}",
338+
$"/additionalReferenceDirectories:{libs}",
339+
};
340+
341+
if (ResponseFilePath != null)
342+
{
343+
compilerArgs.Add(
344+
$"/responseFile:{ResponseFilePath}"
345+
);
346+
}
347+
else
348+
{
349+
// pass all arguments instead of a single .rsp file
350+
compilerArgs.AddRange(
351+
args
352+
);
353+
}
354+
355+
return RunCompiler(
356+
compilerArgs.ToArray(),
357+
_cancellation.Token
358+
);
324359
}
325360
catch (Exception ex)
326361
{
@@ -329,6 +364,51 @@ public override bool Execute()
329364
}
330365
}
331366

367+
bool RunCompiler(string[] args, CancellationToken cancellation = default(CancellationToken))
368+
{
369+
cancellation.ThrowIfCancellationRequested();
370+
371+
Debug.Assert(File.Exists(PeachpieCompilerFullPath));
372+
373+
//
374+
var pi = new ProcessStartInfo(PeachpieCompilerFullPath)
375+
{
376+
Arguments = FlatternArgs(args),
377+
CreateNoWindow = true,
378+
RedirectStandardError = true,
379+
RedirectStandardOutput = true,
380+
StandardOutputEncoding = Encoding.UTF8,
381+
StandardErrorEncoding = Encoding.UTF8,
382+
UseShellExecute = false,
383+
};
384+
385+
//
386+
var process = Process.Start(pi);
387+
var dataReceived = new DataReceivedEventHandler((o, e) =>
388+
{
389+
this.Log.LogMessageFromText(e.Data, MessageImportance.High);
390+
});
391+
392+
process.OutputDataReceived += dataReceived;
393+
process.ErrorDataReceived += dataReceived;
394+
395+
using (var cancellationHandler = cancellation.Register(() =>
396+
{
397+
if (process.HasExited == false)
398+
{
399+
this.Log.LogMessageFromText("Cancelled by user", MessageImportance.High);
400+
// TODO: send signal first
401+
process.Kill();
402+
}
403+
}))
404+
{
405+
process.WaitForExit();
406+
process.StandardOutput.ReadToEnd();
407+
//
408+
return process.ExitCode == 0;
409+
}
410+
}
411+
332412
void LogException(Exception ex)
333413
{
334414
if (ex is AggregateException aex && aex.InnerExceptions != null)
@@ -344,7 +424,7 @@ void LogException(Exception ex)
344424
}
345425
}
346426

347-
bool AddNoEmpty(List<string> args, string optionName, string optionValue)
427+
static bool AddNoEmpty(List<string> args, string optionName, string optionValue)
348428
{
349429
if (string.IsNullOrEmpty(optionValue))
350430
{
@@ -355,7 +435,37 @@ bool AddNoEmpty(List<string> args, string optionName, string optionValue)
355435
return true;
356436
}
357437

358-
private string FormatArgFromItem(ITaskItem item, string switchName, params string[] metadataNames)
438+
static string FlatternArgs(string[] args)
439+
{
440+
var sb = new StringBuilder(args.Length * 8);
441+
442+
for (int i = 0; i < args.Length; i++)
443+
{
444+
var arg = args[i];
445+
if (string.IsNullOrEmpty(arg))
446+
{
447+
continue;
448+
}
449+
450+
if (sb.Length != 0)
451+
{
452+
sb.Append(' ');
453+
}
454+
455+
// sanitize {arg}
456+
sb.Append('\"');
457+
sb.Append(arg.Trim()
458+
//.Replace("\\", "\\\\")
459+
//.Replace("\"", "\\\"")
460+
);
461+
sb.Append('\"');
462+
}
463+
464+
//
465+
return sb.ToString();
466+
}
467+
468+
static string FormatArgFromItem(ITaskItem item, string switchName, params string[] metadataNames)
359469
{
360470
var arg = new StringBuilder($"/{switchName}:{item.ItemSpec}");
361471

0 commit comments

Comments
 (0)