Skip to content

Commit 1dafcda

Browse files
authored
Merge pull request SonarOpenCommunity#2410 from guwirth/json-db
CxxSquidConfiguration
2 parents c910f95 + 51f6124 commit 1dafcda

File tree

4 files changed

+129
-57
lines changed

4 files changed

+129
-57
lines changed

cxx-squid/src/main/java/org/sonar/cxx/config/CxxSquidConfiguration.java

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.nio.charset.Charset;
2727
import java.nio.charset.StandardCharsets;
2828
import java.util.ArrayList;
29+
import java.util.HashMap;
2930
import java.util.LinkedList;
3031
import java.util.List;
3132
import java.util.Locale;
@@ -66,15 +67,45 @@
6667
* With {@code get} and {@code getValues} the information is read out again afterwards. {@code get} returns the first
6768
* found value for key, whereby the search starts on level. {@code getValues} collects all found values over all levels.
6869
* It starts with the given level and further found values are added to the end of the list.
70+
*
71+
* <code>
72+
* CompilationDatabase
73+
* |-- PredefinedMacros
74+
* |-- SonarProjectProperties
75+
* |-- Global
76+
* | |-- Defines
77+
* | | |-- Value
78+
* | | |-- ...
79+
* | |-- IncludeDirectories
80+
* | |-- Value
81+
* | |-- ...
82+
* |-- Units
83+
* |-- File [path=...]
84+
* | |-- Defines
85+
* | | |-- Value
86+
* | | |-- ...
87+
* | |-- IncludeDirectories
88+
* | |-- Value
89+
* | |-- ...
90+
* | -- File [path=...]
91+
* | -- ...
92+
* </code>
6993
*/
7094
public class CxxSquidConfiguration extends SquidConfiguration {
7195

96+
// Root
97+
public static final String ROOT = "CompilationDatabase";
98+
7299
// Levels
73100
public static final String PREDEFINED_MACROS = "PredefinedMacros";
74101
public static final String SONAR_PROJECT_PROPERTIES = "SonarProjectProperties";
75102
public static final String GLOBAL = "Global";
76103
public static final String UNITS = "Units";
77104

105+
// name of 'File' elements
106+
public static final String FILE = "File";
107+
public static final String ATTR_PATH = "path";
108+
78109
// name of 'Value' elements
79110
public static final String VALUE = "Value";
80111

@@ -95,9 +126,13 @@ public class CxxSquidConfiguration extends SquidConfiguration {
95126
private static final Logger LOG = Loggers.get(CxxSquidConfiguration.class);
96127

97128
private final XPathFactory xFactory = XPathFactory.instance();
98-
private final LinkedList<Element> parentList = new LinkedList<>();
99129
private Document document;
130+
private HashMap<String, Element> index = new HashMap<>();
100131

132+
// defines order to search for key/value pairs: Units => Global => SonarProjectProperties => PredefinedMacros
133+
private final LinkedList<Element> parentList = new LinkedList<>();
134+
135+
// base directory to resolve relative paths
101136
private String baseDir = "";
102137

103138
public CxxSquidConfiguration() {
@@ -117,25 +152,29 @@ public CxxSquidConfiguration(String baseDir, Charset encoding) {
117152
super(encoding);
118153
this.baseDir = baseDir;
119154

120-
var root = new Element("CompilationDatabase");
155+
var root = new Element(ROOT);
121156
root.setAttribute(new Attribute("version", "1.0"));
122157
document = new Document(root);
123158

124159
var element = new Element(PREDEFINED_MACROS);
125160
root.addContent(element);
161+
index.put(PREDEFINED_MACROS, element);
126162
parentList.addFirst(element);
127163

128164
element = new Element(SONAR_PROJECT_PROPERTIES);
129165
root.addContent(element);
166+
index.put(SONAR_PROJECT_PROPERTIES, element);
130167
parentList.addFirst(element);
131168

132169
element = new Element(GLOBAL);
133170
root.addContent(element);
171+
index.put(GLOBAL, element);
134172
parentList.addFirst(element);
135173

136174
// UNITS must be first one in the list
137175
element = new Element(UNITS);
138176
root.addContent(element);
177+
index.put(UNITS, element);
139178
parentList.addFirst(element);
140179
}
141180

@@ -516,22 +555,26 @@ private Element getParentElement(@Nullable Element element) {
516555
*/
517556
@CheckForNull
518557
private Element findLevel(String level, @Nullable Element defaultElement) {
558+
Element element = index.getOrDefault(level, null);
559+
if (element != null) {
560+
return element;
561+
}
519562
String xpath;
520563
if (Verifier.checkElementName(level) == null) {
521-
xpath = "/CompilationDatabase/" + level;
564+
xpath = "/" + ROOT + "/" + level;
522565
} else {
523566
// handle special case 'UNITS empty' no need to search in tree
524567
if (isUnitsEmpty()) {
525568
return defaultElement;
526569
}
527-
xpath = "//" + UNITS + "/File[@path='" + unifyPath(level) + "']";
570+
xpath = "/" + ROOT + "/" + UNITS + "/" + FILE + "[@" + ATTR_PATH + "='" + unifyPath(level) + "']";
528571
}
529572
XPathExpression<Element> expr = xFactory.compile(xpath, Filters.element());
530-
List<Element> elements = expr.evaluate(document);
531-
if (!elements.isEmpty()) {
532-
return elements.get(0);
573+
element = expr.evaluateFirst(document);
574+
if (element == null) {
575+
element = defaultElement;
533576
}
534-
return defaultElement;
577+
return element;
535578
}
536579

537580
/**
@@ -548,10 +591,12 @@ private Element getKey(String level, String key) {
548591
eLevel = new Element(level);
549592
document.getRootElement().addContent(eLevel);
550593
} else {
551-
eLevel = new Element("File");
552-
eLevel.setAttribute(new Attribute("path", unifyPath(level)));
594+
eLevel = new Element(FILE);
595+
level = unifyPath(level);
596+
eLevel.setAttribute(new Attribute(ATTR_PATH, level));
553597
parentList.getFirst().addContent(eLevel);
554598
}
599+
index.put(level, eLevel);
555600
}
556601
Element eKey = eLevel.getChild(key);
557602
if (eKey == null) {

cxx-squid/src/main/java/org/sonar/cxx/config/JsonCompilationDatabase.java

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import java.util.HashMap;
3030
import java.util.List;
3131
import java.util.Map;
32-
import java.util.stream.Collectors;
3332
import org.sonar.api.utils.log.Logger;
3433
import org.sonar.api.utils.log.Loggers;
3534

@@ -55,53 +54,43 @@ private static void addMacro(String keyValue, Map<String, String> defines) {
5554
}
5655
}
5756

57+
private static String removeApostophs(String str) {
58+
if (str.charAt(0) == '"' && str.charAt(str.length() - 1) == '"') {
59+
return str.substring(1, str.length() - 1);
60+
}
61+
return str;
62+
}
63+
5864
private static Path makeRelativeToCwd(Path cwd, String include) {
59-
return cwd.resolve(include).normalize();
65+
return cwd.resolve(removeApostophs(include)).normalize();
6066
}
6167

68+
/**
69+
* Tokenize command line with support for escaping
70+
*/
6271
private static String[] tokenizeCommandLine(String cmdLine) {
72+
var arg = new StringBuilder(256);
6373
var args = new ArrayList<String>();
64-
var escape = false;
65-
char stringOpen = 0;
66-
var sb = new StringBuilder(512);
67-
68-
// Tokenize command line with support for escaping
69-
for (var ch : cmdLine.toCharArray()) {
70-
if (escape) {
71-
escape = false;
72-
sb.append(ch);
73-
} else {
74-
if (stringOpen == 0) {
75-
// String not open
76-
if (ch == '\\') {
77-
escape = true;
78-
} else if (ch == '\'') {
79-
stringOpen = '\'';
80-
} else if (ch == '\"') {
81-
stringOpen = '\"';
82-
} else if ((ch == ' ')
83-
&& (sb.length() > 0)) {
84-
args.add(sb.toString());
85-
sb = new StringBuilder(512);
86-
}
87-
if (ch != ' ') {
88-
sb.append(ch);
89-
}
90-
} else {
91-
// String open
92-
if (ch == '\\') {
93-
escape = true;
94-
} else if (ch == stringOpen) {
95-
stringOpen = 0;
96-
}
74+
var insideStr = false;
75+
76+
for (int i = 0; i < cmdLine.length(); i++) {
77+
var ch = cmdLine.charAt(i);
9778

98-
sb.append(ch);
79+
if (ch == ' ' && !insideStr) { // blanks separate arguments (outside of strings)
80+
if (arg.length() > 0) {
81+
args.add(removeApostophs(arg.toString()));
82+
arg.setLength(0);
9983
}
84+
} else if (ch == '"') { // begin or end of string
85+
insideStr = !insideStr;
86+
arg.append(ch);
87+
} else {
88+
arg.append(ch);
10089
}
10190
}
10291

103-
if (sb.length() > 0) {
104-
args.add(sb.toString());
92+
if (arg.length() > 0) {
93+
args.add(removeApostophs(arg.toString()));
10594
}
10695

10796
return args.toArray(new String[0]);
@@ -153,23 +142,24 @@ private void parseCommandObject(JsonCompilationDatabaseCommandObject commandObje
153142

154143
// No need to parse command lines if we have needed information
155144
if (!(commandObject.hasDefines() || commandObject.hasIncludes())) {
156-
String cmdLine;
145+
String[] args;
157146

158147
if (commandObject.hasArguments()) {
159-
cmdLine = commandObject.getArguments().stream().collect(Collectors.joining(" "));
148+
args = commandObject.getArguments().toArray(new String[0]);
149+
if (args.length == 1) {
150+
args = tokenizeCommandLine(args[0]);
151+
}
160152
} else if (commandObject.hasCommand()) {
161-
cmdLine = commandObject.getCommand();
153+
args = tokenizeCommandLine(commandObject.getCommand());
162154
} else {
163155
return;
164156
}
165157

166-
String[] args = tokenizeCommandLine(cmdLine);
167-
var next = ArgNext.NONE;
168-
169158
defines = new HashMap<>();
170159
includes = new ArrayList<>();
171160
var iSystem = new ArrayList<Path>();
172161
var iDirAfter = new ArrayList<Path>();
162+
var next = ArgNext.NONE;
173163

174164
for (var arg : args) {
175165
if (arg.startsWith("-D")) {
@@ -218,13 +208,15 @@ private void parseCommandObject(JsonCompilationDatabaseCommandObject commandObje
218208
}
219209

220210
private void addDefines(String level, Map<String, String> defines) {
221-
defines.forEach((String k, String v) -> squidConfig.add(level, CxxSquidConfiguration.DEFINES, k + " " + v));
211+
defines.forEach((String k, String v) -> {
212+
squidConfig.add(level, CxxSquidConfiguration.DEFINES, k + " " + v);
213+
});
222214
}
223215

224216
private void addIncludes(String level, List<Path> includes) {
225-
for (var include : includes) {
217+
includes.forEach((Path include) -> {
226218
squidConfig.add(level, CxxSquidConfiguration.INCLUDE_DIRECTORIES, include.toString());
227-
}
219+
});
228220
}
229221

230222
private enum ArgNext {

cxx-squid/src/test/java/org/sonar/cxx/config/JsonCompilationDatabaseTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import java.nio.file.Paths;
2626
import java.util.List;
2727
import static org.assertj.core.api.Assertions.*;
28+
import org.junit.jupiter.api.Assumptions;
2829
import org.junit.jupiter.api.Test;
30+
import org.sonar.api.internal.apachecommons.lang.SystemUtils;
2931

3032
class JsonCompilationDatabaseTest {
3133

@@ -104,6 +106,33 @@ void testCommandSettings() throws Exception {
104106
.contains(unifyPath("/usr/include"));
105107
}
106108

109+
@Test
110+
void testVsCommandSettings() throws Exception {
111+
Assumptions.assumeTrue(SystemUtils.IS_OS_WINDOWS);
112+
113+
var squidConfig = new CxxSquidConfiguration();
114+
115+
var file = new File("src/test/resources/jsondb/compile_commands.json");
116+
117+
var jsonDb = new JsonCompilationDatabase(squidConfig);
118+
jsonDb.parse(file);
119+
120+
var filename = "C:/Sample/Project/source.cpp";
121+
122+
List<String> defines = squidConfig.getValues(filename, CxxSquidConfiguration.DEFINES);
123+
List<String> includes = squidConfig.getValues(filename, CxxSquidConfiguration.INCLUDE_DIRECTORIES);
124+
125+
assertThat(defines)
126+
.hasSize(14)
127+
.contains("GLOBAL_DEFINE 1")
128+
.contains("_DLL 1");
129+
130+
assertThat(includes)
131+
.hasSize(24)
132+
.contains(unifyPath("/usr/include"))
133+
.contains(unifyPath("C:\\Access\\Module\\AcquisitionBuffer\\PublicInterface"));
134+
}
135+
107136
@Test
108137
void testArgumentParser() throws Exception {
109138
var squidConfig = new CxxSquidConfiguration();

cxx-squid/src/test/resources/jsondb/compile_commands.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,11 @@
6363
"includes": [
6464
"/usr/local/include"
6565
]
66+
},
67+
{
68+
"_comment_": "Visual Studio sample",
69+
"directory": "C:/Sample/Solutions/",
70+
"command": "\"clang++.exe\" -x c++ \"C:/Sample/Project/source.cpp\" -std=c++14 -Wall -fms-compatibility-version=19.10 -Wmicrosoft -Wno-invalid-token-paste -Wno-unknown-pragmas -Wno-unused-value -fsyntax-only \"-D_MT\" \"-D_DLL\" \"-D_WIN64\" \"-D_WINDOWS\" \"-D_DEBUG\" \"-D_USRDLL\" \"-DSTRICT\" \"-DTGTSVR_PROJECT\" \"-D_WIN32_DCOM\" \"-D_CRT_SECURE_NO_DEPRECATE\" \"-DBOOST_BIND_GLOBAL_PLACEHOLDERS\" \"-DBOOST_THREAD_DYN_LINK\" \"-D_DEBUG_FUNCTIONAL_MACHINERY\" -isystem\"C:/Sample/Project\" -isystem\"C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.29.30133/include\" -isystem\"C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.29.30133/atlmfc/include\" -isystem\"C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/VS/include\" -isystem\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/ucrt\" -isystem\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/um\" -isystem\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/shared\" -isystem\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/winrt\" -isystem\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/cppwinrt\" -I\"C:/Development/svn/Line18/Sample/Tool\" -I\"C:/Development/Sample/Tool/ComComponents/Interfaces\" -I\"C:/Sample/Solutions/PropertySheets/../../../Sample/include\" -I\"C:/Sample/Solutions/PropertySheets/../../../Sample/component/include/IDL\" -I\"C:/Sample/Solutions/PropertySheets/../../../Sample/Tool/ComComponents/Include/IDL\" -I\"C:/Sample/Solutions/PropertySheets/../../../Sample/Tool/ComComponents/Interfaces\" -I\"C:/Sample/Solutions/PropertySheets/../../../Sample/AddOns/Mediators/DeviceConfigX/DeviceConfigX.Com/IDL\" -I\"C:/Sample/Solutions/PropertySheets/../../../..\" -I\"C:/Sample/Solutions/PropertySheets/../../../Access\" -I\"C:/Sample/Solutions/PropertySheets/../../../Access/Framework/Include\" -I\"C:/Sample/Solutions/PropertySheets/../../../Access/Framework/Module/Types\" -I\"C:/Sample/Solutions/PropertySheets/../../../Access/Framework/Module/ModuleUtility\" -I\"C:/Sample/Solutions/PropertySheets/../../../Access/Framework/Project/Time/Time/PublicInterface\" -I\"C:/Sample/Solutions/PropertySheets/../../../Access/Module/AcquisitionBuffer/PublicInterface\"",
71+
"file": "C:/Sample/Project/source.cpp"
6672
}
6773
]

0 commit comments

Comments
 (0)