Skip to content

Commit 4faeba1

Browse files
committed
add support for:
-I dir -iquote dir -isystem dir -idirafter dir Define <macro> to <value> (or 1 if <value> omitted)
1 parent c01abe4 commit 4faeba1

File tree

4 files changed

+191
-110
lines changed

4 files changed

+191
-110
lines changed

cxx-sensors/src/main/java/org/sonar/cxx/sensors/utils/JsonCompilationDatabase.java

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public class JsonCompilationDatabase {
4141

4242
private static final Logger LOG = Loggers.get(JsonCompilationDatabase.class);
4343

44+
enum ArgNext {
45+
NONE, DEFINE, INCLUDE, IQUOTE, ISYSTEM, IDIRAFTER;
46+
}
47+
4448
private JsonCompilationDatabase() {
4549
/* utility class is not meant to be instantiated */
4650
}
@@ -61,99 +65,111 @@ public static void parse(CxxConfiguration config, File compileCommandsFile) thro
6165
mapper.enable(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY);
6266
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
6367

64-
JsonCompilationDatabaseCommandObject[] commandObjects = mapper.readValue(compileCommandsFile,
65-
JsonCompilationDatabaseCommandObject[].class);
68+
JsonCompilationDatabaseCommandObject[] commandObjects
69+
= mapper.readValue(compileCommandsFile, JsonCompilationDatabaseCommandObject[].class);
70+
Path cwd;
6671

6772
for (JsonCompilationDatabaseCommandObject commandObject : commandObjects) {
68-
69-
Path cwd = Paths.get(".");
70-
7173
if (commandObject.getDirectory() != null) {
7274
cwd = Paths.get(commandObject.getDirectory());
75+
} else {
76+
cwd = Paths.get(".");
7377
}
7478

7579
Path absPath = cwd.resolve(commandObject.getFile());
80+
CxxCompilationUnitSettings settings = new CxxCompilationUnitSettings();
81+
parseCommandObject(settings, cwd, commandObject);
7682

7783
if ("__global__".equals(commandObject.getFile())) {
78-
CxxCompilationUnitSettings globalSettings = new CxxCompilationUnitSettings();
79-
80-
parseCommandObject(globalSettings, cwd, commandObject);
81-
82-
config.setGlobalCompilationUnitSettings(globalSettings);
84+
config.setGlobalCompilationUnitSettings(settings);
8385
} else {
84-
CxxCompilationUnitSettings settings = new CxxCompilationUnitSettings();
85-
86-
parseCommandObject(settings, cwd, commandObject);
87-
8886
config.addCompilationUnitSettings(absPath.toAbsolutePath().normalize().toString(), settings);
8987
}
9088
}
9189
}
9290

9391
private static void parseCommandObject(CxxCompilationUnitSettings settings,
94-
Path cwd,
95-
JsonCompilationDatabaseCommandObject commandObject) {
92+
Path cwd, JsonCompilationDatabaseCommandObject commandObject) {
93+
9694
settings.setDefines(commandObject.getDefines());
9795
settings.setIncludes(commandObject.getIncludes());
9896

9997
// No need to parse command lines as we have needed information
100-
if (!commandObject.getDefines().isEmpty() || !commandObject.getIncludes().isEmpty()) {
98+
if (commandObject.hasDefines() || commandObject.hasIncludes()) {
10199
return;
102100
}
103101

104102
String cmdLine;
105103

106-
if (!commandObject.getArguments().isEmpty()) {
104+
if (commandObject.hasArguments()) {
107105
cmdLine = commandObject.getArguments().stream().collect(Collectors.joining(" "));
108-
} else if (!commandObject.getCommand().isEmpty()) {
106+
} else if (commandObject.hasCommand()) {
109107
cmdLine = commandObject.getCommand();
110108
} else {
111109
return;
112110
}
113111

114112
String[] args = tokenizeCommandLine(cmdLine);
115-
boolean nextInclude = false;
116-
boolean nextDefine = false;
117-
List<Path> includes = new ArrayList<>();
113+
ArgNext next = ArgNext.NONE;
114+
118115
HashMap<String, String> defines = new HashMap<>();
116+
List<Path> includes = new ArrayList<>();
117+
List<Path> iSystem = new ArrayList<>();
118+
List<Path> iDirAfter = new ArrayList<>();
119119

120-
// Capture defines and includes from command line
121120
for (String arg : args) {
122-
if (nextInclude) {
123-
nextInclude = false;
124-
includes.add(makeRelativeToCwd(cwd, arg));
125-
} else if (nextDefine) {
126-
nextDefine = false;
127-
String[] define = arg.split("=", 2);
128-
if (define.length == 1) {
129-
defines.put(define[0], "");
130-
} else {
131-
defines.put(define[0], define[1]);
132-
}
133-
} else if ("-I".equals(arg)) {
134-
nextInclude = true;
135-
} else if ("-isystem".equals(arg)) {
136-
nextInclude = true;
121+
if (arg.startsWith("-D")) {
122+
arg = arg.substring(2);
123+
next = ArgNext.DEFINE;
137124
} else if (arg.startsWith("-I")) {
138-
includes.add(makeRelativeToCwd(cwd, arg.substring(2)));
125+
arg = arg.substring(2);
126+
next = ArgNext.INCLUDE;
127+
} else if (arg.startsWith("-iquote")) {
128+
arg = arg.substring(7);
129+
next = ArgNext.INCLUDE;
139130
} else if (arg.startsWith("-isystem")) {
140-
includes.add(makeRelativeToCwd(cwd, arg.substring(8)));
141-
} else if ("-D".equals(arg)) {
142-
nextDefine = true;
143-
} else if (arg.startsWith("-D")) {
144-
String[] define = arg.substring(2).split("=", 2);
145-
if (define.length == 1) {
146-
defines.put(define[0], "");
147-
} else {
148-
defines.put(define[0], define[1]);
131+
arg = arg.substring(8);
132+
next = ArgNext.ISYSTEM;
133+
} else if (arg.startsWith("-idirafter")) {
134+
arg = arg.substring(10);
135+
next = ArgNext.IDIRAFTER;
136+
}
137+
138+
if ((next != ArgNext.NONE) && !arg.isEmpty()) {
139+
switch (next) {
140+
case DEFINE:
141+
addMacro(arg, defines);
142+
break;
143+
case INCLUDE:
144+
case IQUOTE:
145+
includes.add(makeRelativeToCwd(cwd, arg));
146+
break;
147+
case ISYSTEM:
148+
iSystem.add(makeRelativeToCwd(cwd, arg));
149+
break;
150+
case IDIRAFTER:
151+
iDirAfter.add(makeRelativeToCwd(cwd, arg));
152+
break;
149153
}
154+
next = ArgNext.NONE;
150155
}
151156
}
152157

153158
settings.setDefines(defines);
159+
includes.addAll(iSystem);
160+
includes.addAll(iDirAfter);
154161
settings.setIncludes(includes);
155162
}
156163

164+
private static void addMacro(String keyValue, HashMap<String, String> defines) {
165+
String[] strings = keyValue.split("=", 2);
166+
if (strings.length == 1) {
167+
defines.put(strings[0], "1");
168+
} else {
169+
defines.put(strings[0], strings[1]);
170+
}
171+
}
172+
157173
private static Path makeRelativeToCwd(Path cwd, String include) {
158174
return cwd.resolve(include).normalize();
159175
}

cxx-sensors/src/main/java/org/sonar/cxx/sensors/utils/JsonCompilationDatabaseCommandObject.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,25 +96,33 @@ public void setFile(String file) {
9696
* escaping of quotes, with ‘"‘ and ‘\‘ being the only special characters.
9797
* Shell expansion is not supported.
9898
*/
99-
public String getCommand() {
100-
return command;
99+
public boolean hasCommand() {
100+
return !command.isEmpty();
101101
}
102-
102+
103103
public void setCommand(String command) {
104104
this.command = command;
105105
}
106+
107+
public String getCommand() {
108+
return command;
109+
}
106110

107111
/**
108112
* The compile command executed as list of strings. Either arguments or
109113
* command is required.
110114
*/
111-
public List<String> getArguments() {
112-
return arguments;
115+
public boolean hasArguments() {
116+
return !arguments.isEmpty();
113117
}
114-
118+
115119
public void setArguments(List<String> arguments) {
116120
this.arguments = arguments;
117121
}
122+
123+
public List<String> getArguments() {
124+
return arguments;
125+
}
118126

119127
/**
120128
* The name of the output created by this compilation step. This field is
@@ -132,23 +140,31 @@ public void setOutput(String output) {
132140
/**
133141
* Extension to define defines
134142
*/
135-
public Map<String, String> getDefines() {
136-
return defines;
143+
public boolean hasDefines() {
144+
return !defines.isEmpty();
137145
}
138146

139147
public void setDefines(Map<String, String> defines) {
140148
this.defines = new HashMap<>(defines);
141149
}
142150

151+
public Map<String, String> getDefines() {
152+
return defines;
153+
}
154+
143155
/**
144156
* Extension to define include directories
145157
*/
146-
public List<Path> getIncludes() {
147-
return Collections.unmodifiableList(includes);
158+
public boolean hasIncludes() {
159+
return !includes.isEmpty();
148160
}
149161

150162
public void setIncludes(List<Path> includes) {
151163
this.includes = new ArrayList<>(includes);
152164
}
153165

166+
public List<Path> getIncludes() {
167+
return Collections.unmodifiableList(includes);
168+
}
169+
154170
}

cxx-sensors/src/test/java/org/sonar/cxx/sensors/utils/JsonCompilationDatabaseTest.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,46 @@ public void testCommandSettings() throws Exception {
8686
assertThat(cus.getDefines().containsKey("COMMAND_SPACE_DEFINE")).isTrue();
8787
assertThat(cus.getDefines().get("COMMAND_SPACE_DEFINE")).isEqualTo("\" foo 'bar' zoo \"");
8888
assertThat(cus.getDefines().containsKey("SIMPLE")).isTrue();
89-
assertThat(cus.getDefines().get("SIMPLE")).isEqualTo("");
89+
assertThat(cus.getDefines().get("SIMPLE")).isEqualTo("1");
9090
assertThat(cus.getDefines().containsKey("GLOBAL_DEFINE")).isFalse();
9191
assertThat(cus.getIncludes().contains(Paths.get("/usr/local/include"))).isTrue();
9292
assertThat(cus.getIncludes().contains(Paths.get("/another/include/dir"))).isTrue();
9393
assertThat(cus.getIncludes().contains(Paths.get("/usr/include"))).isFalse();
9494
}
9595

96+
@Test
97+
public void testArgumentParser() throws Exception {
98+
CxxConfiguration conf = new CxxConfiguration();
99+
100+
File file = new File("src/test/resources/org/sonar/cxx/sensors/json-compilation-database-project/compile_commands.json");
101+
102+
JsonCompilationDatabase.parse(conf, file);
103+
104+
Path cwd = Paths.get(".");
105+
Path absPath = cwd.resolve("test-argument-parser.cpp");
106+
String filename = absPath.toAbsolutePath().normalize().toString();
107+
108+
CxxCompilationUnitSettings cus = conf.getCompilationUnitSettings(filename);
109+
110+
assertThat(cus).isNotNull();
111+
112+
assertThat(cus.getDefines().get("MACRO1")).isEqualTo("1");
113+
assertThat(cus.getDefines().get("MACRO2")).isEqualTo("2");
114+
assertThat(cus.getDefines().get("MACRO3")).isEqualTo("1");
115+
assertThat(cus.getDefines().get("MACRO4")).isEqualTo("4");
116+
assertThat(cus.getDefines().get("MACRO5")).isEqualTo("\" a 'b' c \"");
117+
assertThat(cus.getDefines().get("MACRO6")).isEqualTo("\"With spaces, quotes and \\-es.\"");
118+
119+
assertThat(cus.getIncludes().contains(Paths.get("/aaa/bbb"))).isTrue();
120+
assertThat(cus.getIncludes().contains(Paths.get("/ccc/ddd"))).isTrue();
121+
assertThat(cus.getIncludes().contains(Paths.get("/eee/fff"))).isTrue();
122+
assertThat(cus.getIncludes().contains(Paths.get("/ggg/hhh"))).isTrue();
123+
assertThat(cus.getIncludes().contains(Paths.get("/iii/jjj"))).isTrue();
124+
assertThat(cus.getIncludes().contains(Paths.get("/kkk/lll"))).isTrue();
125+
assertThat(cus.getIncludes().contains(Paths.get("/mmm/nnn"))).isTrue();
126+
assertThat(cus.getIncludes().contains(Paths.get("/ooo/ppp"))).isTrue();
127+
}
128+
96129
@Test
97130
public void testArgumentSettings() throws Exception {
98131
CxxConfiguration conf = new CxxConfiguration();
@@ -112,7 +145,7 @@ public void testArgumentSettings() throws Exception {
112145
assertThat(cus.getDefines().containsKey("ARG_SPACE_DEFINE")).isTrue();
113146
assertThat(cus.getDefines().get("ARG_SPACE_DEFINE")).isEqualTo("\" foo 'bar' zoo \"");
114147
assertThat(cus.getDefines().containsKey("SIMPLE")).isTrue();
115-
assertThat(cus.getDefines().get("SIMPLE")).isEqualTo("");
148+
assertThat(cus.getDefines().get("SIMPLE")).isEqualTo("1");
116149
assertThat(cus.getDefines().containsKey("GLOBAL_DEFINE")).isFalse();
117150
assertThat(cus.getIncludes().contains(Paths.get("/usr/local/include"))).isTrue();
118151
assertThat(cus.getIncludes().contains(Paths.get("/another/include/dir"))).isTrue();
@@ -159,7 +192,7 @@ public void testArgumentAsListSettings() throws Exception {
159192
assertThat(cus.getDefines().containsKey("ARG_SPACE_DEFINE")).isTrue();
160193
assertThat(cus.getDefines().get("ARG_SPACE_DEFINE")).isEqualTo("\" foo 'bar' zoo \"");
161194
assertThat(cus.getDefines().containsKey("SIMPLE")).isTrue();
162-
assertThat(cus.getDefines().get("SIMPLE")).isEqualTo("");
195+
assertThat(cus.getDefines().get("SIMPLE")).isEqualTo("1");
163196
assertThat(cus.getDefines().containsKey("GLOBAL_DEFINE")).isFalse();
164197
assertThat(cus.getIncludes().contains(Paths.get("/usr/local/include"))).isTrue();
165198
assertThat(cus.getIncludes().contains(Paths.get("/another/include/dir"))).isTrue();

0 commit comments

Comments
 (0)