@@ -50,6 +50,20 @@ internal sealed partial class CSharpCompilerCommand
50
50
public required string ArtifactsPath { get ; init ; }
51
51
public required bool CanReuseAuxiliaryFiles { get ; init ; }
52
52
53
+ public string BaseDirectory => field ??= Path . GetDirectoryName ( EntryPointFileFullPath ) ! ;
54
+
55
+ /// <summary>
56
+ /// Compiler command line arguments to use. If empty, default arguments are used.
57
+ /// These should be already properly escaped.
58
+ /// </summary>
59
+ public required ImmutableArray < string > CscArguments { get ; init ; }
60
+
61
+ /// <summary>
62
+ /// Path to the <c>bin/Program.dll</c> file. If specified,
63
+ /// the compiled output (<c>obj/Program.dll</c>) will be copied to this location.
64
+ /// </summary>
65
+ public required string ? BuildResultFile { get ; init ; }
66
+
53
67
/// <param name="fallbackToNormalBuild">
54
68
/// Whether the returned error code should not cause the build to fail but instead fallback to full MSBuild.
55
69
/// </param>
@@ -64,7 +78,7 @@ public int Execute(out bool fallbackToNormalBuild)
64
78
requestId : EntryPointFileFullPath ,
65
79
language : RequestLanguage . CSharpCompile ,
66
80
arguments : [ "/noconfig" , "/nologo" , $ "@{ EscapeSingleArg ( rspPath ) } "] ,
67
- workingDirectory : Environment . CurrentDirectory ,
81
+ workingDirectory : BaseDirectory ,
68
82
tempDirectory : Path . GetTempPath ( ) ,
69
83
keepAlive : null ,
70
84
libDirectory : null ,
@@ -87,7 +101,18 @@ public int Execute(out bool fallbackToNormalBuild)
87
101
cancellationToken : default ) ;
88
102
89
103
// Process the response.
90
- return ProcessBuildResponse ( responseTask . Result , out fallbackToNormalBuild ) ;
104
+ var exitCode = ProcessBuildResponse ( responseTask . Result , out fallbackToNormalBuild ) ;
105
+
106
+ // Copy from obj to bin.
107
+ if ( BuildResultFile != null &&
108
+ CSharpCommandLineParser . Default . Parse ( CscArguments , BaseDirectory , sdkDirectory : null ) is { OutputFileName : { } outputFileName } parsedArgs )
109
+ {
110
+ var objFile = parsedArgs . GetOutputFilePath ( outputFileName ) ;
111
+ Reporter . Verbose . WriteLine ( $ "Copying '{ objFile } ' to '{ BuildResultFile } '.") ;
112
+ File . Copy ( objFile , BuildResultFile , overwrite : true ) ;
113
+ }
114
+
115
+ return exitCode ;
91
116
92
117
static string GetCompilerCommitHash ( )
93
118
{
@@ -129,6 +154,14 @@ static int ProcessBuildResponse(BuildResponse response, out bool fallbackToNorma
129
154
130
155
private void PrepareAuxiliaryFiles ( out string rspPath )
131
156
{
157
+ rspPath = Path . Join ( ArtifactsPath , "csc.rsp" ) ;
158
+
159
+ if ( ! CscArguments . IsDefaultOrEmpty )
160
+ {
161
+ File . WriteAllLines ( rspPath , CscArguments ) ;
162
+ return ;
163
+ }
164
+
132
165
string fileDirectory = Path . GetDirectoryName ( EntryPointFileFullPath ) ?? string . Empty ;
133
166
string fileNameWithoutExtension = Path . GetFileNameWithoutExtension ( EntryPointFileFullPath ) ;
134
167
@@ -283,7 +316,6 @@ private void PrepareAuxiliaryFiles(out string rspPath)
283
316
""" ) ;
284
317
}
285
318
286
- rspPath = Path . Join ( ArtifactsPath , "csc.rsp" ) ;
287
319
if ( ShouldEmit ( rspPath ) )
288
320
{
289
321
IEnumerable < string > args = GetCscArguments (
@@ -315,18 +347,18 @@ private static string EscapeSingleArg(string arg)
315
347
{
316
348
if ( IsPathOption ( arg , out var colonIndex ) )
317
349
{
318
- return arg [ ..( colonIndex + 1 ) ] + EscapeCore ( arg [ ( colonIndex + 1 ) ..] ) ;
350
+ return arg [ ..( colonIndex + 1 ) ] + EscapePathArgument ( arg [ ( colonIndex + 1 ) ..] ) ;
319
351
}
320
352
321
- return EscapeCore ( arg ) ;
353
+ return EscapePathArgument ( arg ) ;
354
+ }
322
355
323
- static string EscapeCore ( string arg )
356
+ internal static string EscapePathArgument ( string arg )
357
+ {
358
+ return ArgumentEscaper . EscapeSingleArg ( arg , additionalShouldSurroundWithQuotes : static ( string arg ) =>
324
359
{
325
- return ArgumentEscaper . EscapeSingleArg ( arg , additionalShouldSurroundWithQuotes : static ( string arg ) =>
326
- {
327
- return arg . ContainsAny ( s_additionalShouldSurroundWithQuotes ) ;
328
- } ) ;
329
- }
360
+ return arg . ContainsAny ( s_additionalShouldSurroundWithQuotes ) ;
361
+ } ) ;
330
362
}
331
363
332
364
public static bool IsPathOption ( string arg , out int colonIndex )
0 commit comments