Skip to content

Commit 832b4cc

Browse files
committed
Fixed ConcurrentModificationException on compilerArguments
1 parent a4c009c commit 832b4cc

File tree

2 files changed

+92
-132
lines changed

2 files changed

+92
-132
lines changed

plexus-compilers/plexus-compiler-csharp/src/main/java/org/codehaus/plexus/compiler/csharp/CSharpCompiler.java

Lines changed: 87 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
import java.io.Writer;
2929
import java.nio.file.Paths;
3030
import java.util.ArrayList;
31-
import java.util.Arrays;
31+
import java.util.Collections;
32+
import java.util.HashMap;
3233
import java.util.HashSet;
33-
import java.util.Iterator;
3434
import java.util.List;
3535
import java.util.Map;
3636
import 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

Comments
 (0)