2828import java .io .Writer ;
2929import java .nio .file .Paths ;
3030import java .util .ArrayList ;
31- import java .util .Arrays ;
31+ import java .util .Collections ;
32+ import java .util .HashMap ;
3233import java .util .HashSet ;
33- import java .util .Iterator ;
3434import java .util .List ;
3535import java .util .Map ;
3636import java .util .Set ;
@@ -68,26 +68,16 @@ public class CSharpCompiler extends AbstractCompiler {
6868
6969 private static final String [] DEFAULT_INCLUDES = {"**/**" };
7070
71- private Map <String , String > compilerArguments ;
72-
73- // ----------------------------------------------------------------------
74- //
75- // ----------------------------------------------------------------------
76-
7771 public CSharpCompiler () {
7872 super (CompilerOutputStyle .ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES , ".cs" , null , null );
7973 }
8074
81- // ----------------------------------------------------------------------
82- // Compiler Implementation
83- // ----------------------------------------------------------------------
84-
8575 @ Override
8676 public String getCompilerId () {
8777 return "csharp" ;
8878 }
8979
90- public boolean canUpdateTarget (CompilerConfiguration configuration ) throws CompilerException {
80+ public boolean canUpdateTarget (CompilerConfiguration configuration ) {
9181 return false ;
9282 }
9383
@@ -130,37 +120,39 @@ public String[] createCommandLine(CompilerConfiguration config) throws CompilerE
130120 return buildCompilerArguments (config , CSharpCompiler .getSourceFiles (config ));
131121 }
132122
133- // ----------------------------------------------------------------------
134- //
135- // ----------------------------------------------------------------------
136-
123+ /**
124+ * Parse compiler arguments and normalize legacy colon-separated format.
125+ * Converts arguments like "-main:MyClass" (stored as key with null value)
126+ * into proper key-value pairs: "-main" -> "MyClass".
127+ *
128+ * @param config the compiler configuration
129+ * @return normalized map of compiler arguments
130+ */
137131 private Map <String , String > getCompilerArguments (CompilerConfiguration config ) {
138- if (compilerArguments != null ) {
139- return compilerArguments ;
140- }
132+ Map <String , String > customArgs = config .getCustomCompilerArgumentsAsMap ();
133+ Map <String , String > normalizedArgs = new HashMap <>();
141134
142- compilerArguments = config .getCustomCompilerArgumentsAsMap ();
135+ for (Map .Entry <String , String > entry : customArgs .entrySet ()) {
136+ String key = entry .getKey ();
137+ String value = entry .getValue ();
143138
144- Iterator <String > i = compilerArguments .keySet ().iterator ();
139+ // Handle legacy format: "-main:MyClass" stored as key with null value
140+ if (value == null && key .contains (":" )) {
141+ int colonIndex = key .indexOf (':' );
142+ String actualKey = key .substring (0 , colonIndex );
143+ String actualValue = key .substring (colonIndex + 1 );
144+ normalizedArgs .put (actualKey , actualValue );
145145
146- while (i .hasNext ()) {
147- String orig = i .next ();
148- String v = compilerArguments .get (orig );
149- if (orig .contains (":" ) && v == null ) {
150- String [] arr = orig .split (":" );
151- i .remove ();
152- String k = arr [0 ];
153- v = arr [1 ];
154- compilerArguments .put (k , v );
155146 if (config .isDebug ()) {
156- System .out .println ("transforming argument from " + orig + " to " + k + " = [" + v + "]" );
147+ System .out .println ("Normalized argument '" + key + "' to key='" + actualKey + "', value='"
148+ + actualValue + "'" );
157149 }
150+ } else {
151+ normalizedArgs .put (key , value );
158152 }
159153 }
160154
161- config .setCustomCompilerArgumentsAsMap (compilerArguments );
162-
163- return compilerArguments ;
155+ return normalizedArgs ;
164156 }
165157
166158 private String findExecutable (CompilerConfiguration config ) {
@@ -179,45 +171,53 @@ private String findExecutable(CompilerConfiguration config) {
179171
180172 /*
181173 $ mcs --help
182- Mono C# compiler, (C) 2001 - 2003 Ximian , Inc.
174+ Turbo C# compiler, Copyright 2001-2011 Novell , Inc., 2011-2016 Xamarin, Inc, 2016-2017 Microsoft Corp
183175 mcs [options] source-files
184- --about About the Mono C# compiler
185- -addmodule:MODULE Adds the module to the generated assembly
186- -checked[+|-] Set default context to checked
187- -codepage:ID Sets code page to the one in ID (number, utf8, reset)
188- -clscheck[+|-] Disables CLS Compliance verifications
189- -define:S1[;S2] Defines one or more symbols (short: /d:)
190- -debug[+|-], -g Generate debugging information
191- -delaysign[+|-] Only insert the public key into the assembly (no signing)
192- -doc:FILE XML Documentation file to generate
193- -keycontainer:NAME The key pair container used to strongname the assembly
194- -keyfile:FILE The strongname key file used to strongname the assembly
195- -langversion:TEXT Specifies language version modes: ISO-1 or Default
196- -lib:PATH1,PATH2 Adds the paths to the assembly link path
197- -main:class Specified the class that contains the entry point
198- -noconfig[+|-] Disables implicit references to assemblies
199- -nostdlib[+|-] Does not load core libraries
200- -nowarn:W1[,W2] Disables one or more warnings
201- -optimize[+|-] Enables code optimalizations
202- -out:FNAME Specifies output file
203- -pkg:P1[,Pn] References packages P1..Pn
204- -recurse:SPEC Recursively compiles the files in SPEC ([dir]/file)
205- -reference:ASS References the specified assembly (-r:ASS)
206- -target:KIND Specifies the target (KIND is one of: exe, winexe,
207- library, module), (short: /t:)
208- -unsafe[+|-] Allows unsafe code
209- -warnaserror[+|-] Treat warnings as errors
210- -warn:LEVEL Sets warning level (the highest is 4, the default is 2)
211- -help2 Show other help flags
176+ --about About the Mono C# compiler
177+ -addmodule:M1[,Mn] Adds the module to the generated assembly
178+ -checked[+|-] Sets default aritmetic overflow context
179+ -clscheck[+|-] Disables CLS Compliance verifications
180+ -codepage:ID Sets code page to the one in ID (number, utf8, reset)
181+ -define:S1[;S2] Defines one or more conditional symbols (short: -d)
182+ -debug[+|-], -g Generate debugging information
183+ -delaysign[+|-] Only insert the public key into the assembly (no signing)
184+ -doc:FILE Process documentation comments to XML file
185+ -fullpaths Any issued error or warning uses absolute file path
186+ -help Lists all compiler options (short: -?)
187+ -keycontainer:NAME The key pair container used to sign the output assembly
188+ -keyfile:FILE The key file used to strongname the ouput assembly
189+ -langversion:TEXT Specifies language version: ISO-1, ISO-2, 3, 4, 5, 6, Default or Experimental
190+ -lib:PATH1[,PATHn] Specifies the location of referenced assemblies
191+ -main:CLASS Specifies the class with the Main method (short: -m)
192+ -noconfig Disables implicitly referenced assemblies
193+ -nostdlib[+|-] Does not reference mscorlib.dll library
194+ -nowarn:W1[,Wn] Suppress one or more compiler warnings
195+ -optimize[+|-] Enables advanced compiler optimizations (short: -o)
196+ -out:FILE Specifies output assembly name
197+ -pathmap:K=V[,Kn=Vn] Sets a mapping for source path names used in generated output
198+ -pkg:P1[,Pn] References packages P1..Pn
199+ -platform:ARCH Specifies the target platform of the output assembly
200+ ARCH can be one of: anycpu, anycpu32bitpreferred, arm,
201+ x86, x64 or itanium. The default is anycpu.
202+ -recurse:SPEC Recursively compiles files according to SPEC pattern
203+ -reference:A1[,An] Imports metadata from the specified assembly (short: -r)
204+ -reference:ALIAS=A Imports metadata using specified extern alias (short: -r)
205+ -sdk:VERSION Specifies SDK version of referenced assemblies
206+ VERSION can be one of: 2, 4, 4.5 (default) or a custom value
207+ -target:KIND Specifies the format of the output assembly (short: -t)
208+ KIND can be one of: exe, winexe, library, module
209+ -unsafe[+|-] Allows to compile code which uses unsafe keyword
210+ -warnaserror[+|-] Treats all warnings as errors
211+ -warnaserror[+|-]:W1[,Wn] Treats one or more compiler warnings as errors
212+ -warn:0-4 Sets warning level, the default is 4 (short -w:)
213+ -helpinternal Shows internal and advanced compiler options
212214
213215 Resources:
214- -linkresource:FILE[,ID] Links FILE as a resource
215- -resource:FILE[,ID] Embed FILE as a resource
216+ -linkresource:FILE[,ID] Links FILE as a resource (short: -linkres)
217+ -resource:FILE[,ID] Embed FILE as a resource (short: -res)
216218 -win32res:FILE Specifies Win32 resource file (.res)
217219 -win32icon:FILE Use this icon for the output
218220 @file Read response file for more options
219-
220- Options can be of the form -option or /option
221221 */
222222
223223 /*
@@ -389,21 +389,11 @@ private String[] buildCompilerArguments(CompilerConfiguration config, String[] s
389389 throws CompilerException {
390390 List <String > args = new ArrayList <>();
391391
392- if (config .isDebug ()) {
393- args .add ("/debug+" );
394- } else {
395- args .add ("/debug-" );
396- }
397-
398392 // config.isShowWarnings()
399393 // config.getSourceVersion()
400394 // config.getTargetVersion()
401395 // config.getSourceEncoding()
402396
403- // ----------------------------------------------------------------------
404- //
405- // ----------------------------------------------------------------------
406-
407397 for (String element : config .getClasspathEntries ()) {
408398 File f = new File (element );
409399
@@ -433,122 +423,91 @@ private String[] buildCompilerArguments(CompilerConfiguration config, String[] s
433423 }
434424 }
435425
436- // ----------------------------------------------------------------------
437- // Main class
438- // ----------------------------------------------------------------------
439-
426+ // TODO: include all user compiler arguments and not only some!
440427 Map <String , String > compilerArguments = getCompilerArguments (config );
441428
442429 String mainClass = compilerArguments .get ("-main" );
443-
444430 if (!StringUtils .isEmpty (mainClass )) {
445431 args .add ("/main:" + mainClass );
446432 }
447433
448- // ----------------------------------------------------------------------
449434 // Xml Doc output
450- // ----------------------------------------------------------------------
451-
452435 String doc = compilerArguments .get ("-doc" );
453-
454436 if (!StringUtils .isEmpty (doc )) {
455437 args .add ("/doc:"
456438 + new File (config .getOutputLocation (), config .getOutputFileName () + ".xml" ).getAbsolutePath ());
457439 }
458440
459- // ----------------------------------------------------------------------
460- // Nowarn option
461- // ----------------------------------------------------------------------
441+ // Debug option (full, pdbonly...)
442+ String debug = compilerArguments .get ("-debug" );
443+ if (!StringUtils .isEmpty (debug )) {
444+ args .add ("/debug:" + debug );
445+ }
462446
447+ // Nowarn option (w#1,w#2...)
463448 String nowarn = compilerArguments .get ("-nowarn" );
464-
465449 if (!StringUtils .isEmpty (nowarn )) {
466450 args .add ("/nowarn:" + nowarn );
467451 }
468452
469- // ----------------------------------------------------------------------
470453 // Out - Override output name, this is required for generating the unit test dll
471- // ----------------------------------------------------------------------
472-
473454 String out = compilerArguments .get ("-out" );
474-
475455 if (!StringUtils .isEmpty (out )) {
476456 args .add ("/out:" + new File (config .getOutputLocation (), out ).getAbsolutePath ());
477457 } else {
478458 args .add ("/out:" + new File (config .getOutputLocation (), getOutputFile (config )).getAbsolutePath ());
479459 }
480460
481- // ----------------------------------------------------------------------
482461 // Resource File - compile in a resource file into the assembly being created
483- // ----------------------------------------------------------------------
484462 String resourcefile = compilerArguments .get ("-resourcefile" );
485-
486463 if (!StringUtils .isEmpty (resourcefile )) {
487464 String resourceTarget = compilerArguments .get ("-resourcetarget" );
488465 args .add ("/res:" + new File (resourcefile ).getAbsolutePath () + "," + resourceTarget );
489466 }
490467
491- // ----------------------------------------------------------------------
492- // Target - type of assembly to produce, lib,exe,winexe etc...
493- // ----------------------------------------------------------------------
494-
468+ // Target - type of assembly to produce: library,exe,winexe...
495469 String target = compilerArguments .get ("-target" );
496-
497470 if (StringUtils .isEmpty (target )) {
498471 args .add ("/target:library" );
499472 } else {
500473 args .add ("/target:" + target );
501474 }
502475
503- // ----------------------------------------------------------------------
504476 // remove MS logo from output (not applicable for mono)
505- // ----------------------------------------------------------------------
506477 String nologo = compilerArguments .get ("-nologo" );
507-
508- if (!StringUtils .isEmpty (nologo )) {
478+ if (!StringUtils .isEmpty (nologo ) && !"false" .equalsIgnoreCase (nologo )) {
509479 args .add ("/nologo" );
510480 }
511481
512- // ----------------------------------------------------------------------
513482 // Unsafe option
514- // ----------------------------------------------------------------------
515483 String unsafe = compilerArguments .get ("-unsafe" );
516-
517- if (!StringUtils .isEmpty (unsafe ) && unsafe .equals ("true" )) {
484+ if (!StringUtils .isEmpty (unsafe ) && "true" .equalsIgnoreCase (unsafe )) {
518485 args .add ("/unsafe" );
519486 }
520487
521- // ----------------------------------------------------------------------
522488 // PreferredUILang option
523- // ----------------------------------------------------------------------
524489 String preferreduilang = compilerArguments .get ("-preferreduilang" );
525-
526490 if (!StringUtils .isEmpty (preferreduilang )) {
527491 args .add ("/preferreduilang:" + preferreduilang );
528492 }
529493
530- // ----------------------------------------------------------------------
531494 // Utf8Output option
532- // ----------------------------------------------------------------------
533495 String utf8output = compilerArguments .get ("-utf8output" );
534-
535- if (!StringUtils .isEmpty (utf8output )) {
536- args .add ("/utf8output:" );
496+ if (!StringUtils .isEmpty (utf8output ) && !"false" .equals (utf8output )) {
497+ args .add ("/utf8output" );
537498 }
538499
539- // ----------------------------------------------------------------------
540500 // add any resource files
541- // ----------------------------------------------------------------------
542501 this .addResourceArgs (config , args );
543502
544- // ----------------------------------------------------------------------
545503 // add source files
546- // ----------------------------------------------------------------------
547- for (String sourceFile : sourceFiles ) {
548- args .add (sourceFile );
504+ Collections .addAll (args , sourceFiles );
505+
506+ if (config .isDebug ()) {
507+ System .out .println ("built compiler arguments:" + args );
549508 }
550509
551- return args .toArray (new String [args . size () ]);
510+ return args .toArray (new String [0 ]);
552511 }
553512
554513 private void addResourceArgs (CompilerConfiguration config , List <String > args ) {
@@ -560,7 +519,7 @@ private void addResourceArgs(CompilerConfiguration config, List<String> args) {
560519 scanner .addDefaultExcludes ();
561520 scanner .scan ();
562521
563- List < String > includedFiles = Arrays . asList ( scanner .getIncludedFiles () );
522+ String [] includedFiles = scanner .getIncludedFiles ();
564523 for (String name : includedFiles ) {
565524 File filteredResource = new File (filteredResourceDir , name );
566525 String assemblyResourceName = this .convertNameToAssemblyResourceName (name );
@@ -585,7 +544,7 @@ private File findResourceDir(CompilerConfiguration config) {
585544 if (tempResourcesDirAsString != null ) {
586545 filteredResourceDir = new File (tempResourcesDirAsString );
587546 if (config .isDebug ()) {
588- System .out .println ("Found resourceDir at: " + filteredResourceDir . toString () );
547+ System .out .println ("Found resourceDir at: " + filteredResourceDir );
589548 }
590549 } else {
591550 if (config .isDebug ()) {
0 commit comments