/src/cppcheck/lib/importproject.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Cppcheck - A tool for static C/C++ code analysis |
3 | | * Copyright (C) 2007-2023 Cppcheck team. |
4 | | * |
5 | | * This program is free software: you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation, either version 3 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "importproject.h" |
20 | | |
21 | | #include "path.h" |
22 | | #include "settings.h" |
23 | | #include "standards.h" |
24 | | #include "suppressions.h" |
25 | | #include "token.h" |
26 | | #include "tokenize.h" |
27 | | #include "utils.h" |
28 | | |
29 | | #include <algorithm> |
30 | | #include <cstdlib> |
31 | | #include <cstring> |
32 | | #include <fstream> // IWYU pragma: keep |
33 | | #include <iostream> |
34 | | #include <iterator> |
35 | | #include <sstream> // IWYU pragma: keep |
36 | | #include <unordered_set> |
37 | | #include <utility> |
38 | | |
39 | | #include <tinyxml2.h> |
40 | | |
41 | | #include <simplecpp.h> |
42 | | |
43 | | #include "json.h" |
44 | | |
45 | | ImportProject::ImportProject() |
46 | 1.36k | { |
47 | 1.36k | projectType = Type::UNKNOWN; |
48 | 1.36k | } |
49 | | |
50 | | void ImportProject::ignorePaths(const std::vector<std::string> &ipaths) |
51 | 0 | { |
52 | 0 | for (std::list<FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) { |
53 | 0 | bool ignore = false; |
54 | 0 | for (std::string i : ipaths) { |
55 | 0 | if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { |
56 | 0 | ignore = true; |
57 | 0 | break; |
58 | 0 | } |
59 | 0 | if (isValidGlobPattern(i) && matchglob(i, it->filename)) { |
60 | 0 | ignore = true; |
61 | 0 | break; |
62 | 0 | } |
63 | 0 | if (!Path::isAbsolute(i)) { |
64 | 0 | i = mPath + i; |
65 | 0 | if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { |
66 | 0 | ignore = true; |
67 | 0 | break; |
68 | 0 | } |
69 | 0 | } |
70 | 0 | } |
71 | 0 | if (ignore) |
72 | 0 | it = fileSettings.erase(it); |
73 | 0 | else |
74 | 0 | ++it; |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | void ImportProject::ignoreOtherConfigs(const std::string &cfg) |
79 | 0 | { |
80 | 0 | for (std::list<FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) { |
81 | 0 | if (it->cfg != cfg) |
82 | 0 | it = fileSettings.erase(it); |
83 | 0 | else |
84 | 0 | ++it; |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | void ImportProject::FileSettings::setDefines(std::string defs) |
89 | 0 | { |
90 | 0 | while (defs.find(";%(") != std::string::npos) { |
91 | 0 | const std::string::size_type pos1 = defs.find(";%("); |
92 | 0 | const std::string::size_type pos2 = defs.find(';', pos1+1); |
93 | 0 | defs.erase(pos1, pos2 == std::string::npos ? pos2 : (pos2-pos1)); |
94 | 0 | } |
95 | 0 | while (defs.find(";;") != std::string::npos) |
96 | 0 | defs.erase(defs.find(";;"),1); |
97 | 0 | while (!defs.empty() && defs[0] == ';') |
98 | 0 | defs.erase(0, 1); |
99 | 0 | while (!defs.empty() && endsWith(defs,';')) |
100 | 0 | defs.erase(defs.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it |
101 | 0 | bool eq = false; |
102 | 0 | for (std::size_t pos = 0; pos < defs.size(); ++pos) { |
103 | 0 | if (defs[pos] == '(' || defs[pos] == '=') |
104 | 0 | eq = true; |
105 | 0 | else if (defs[pos] == ';') { |
106 | 0 | if (!eq) { |
107 | 0 | defs.insert(pos,"=1"); |
108 | 0 | pos += 3; |
109 | 0 | } |
110 | 0 | if (pos < defs.size()) |
111 | 0 | eq = false; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | if (!eq && !defs.empty()) |
115 | 0 | defs += "=1"; |
116 | 0 | defines.swap(defs); |
117 | 0 | } |
118 | | |
119 | | static bool simplifyPathWithVariables(std::string &s, std::map<std::string, std::string, cppcheck::stricmp> &variables) |
120 | 0 | { |
121 | 0 | std::set<std::string, cppcheck::stricmp> expanded; |
122 | 0 | std::string::size_type start = 0; |
123 | 0 | while ((start = s.find("$(")) != std::string::npos) { |
124 | 0 | const std::string::size_type end = s.find(')',start); |
125 | 0 | if (end == std::string::npos) |
126 | 0 | break; |
127 | 0 | const std::string var = s.substr(start+2,end-start-2); |
128 | 0 | if (expanded.find(var) != expanded.end()) |
129 | 0 | break; |
130 | 0 | expanded.insert(var); |
131 | 0 | std::map<std::string, std::string, cppcheck::stricmp>::const_iterator it1 = variables.find(var); |
132 | | // variable was not found within defined variables |
133 | 0 | if (it1 == variables.end()) { |
134 | 0 | const char *envValue = std::getenv(var.c_str()); |
135 | 0 | if (!envValue) { |
136 | | //! \todo generate a debug/info message about undefined variable |
137 | 0 | break; |
138 | 0 | } |
139 | 0 | variables[var] = std::string(envValue); |
140 | 0 | it1 = variables.find(var); |
141 | 0 | } |
142 | 0 | s.replace(start, end - start + 1, it1->second); |
143 | 0 | } |
144 | 0 | if (s.find("$(") != std::string::npos) |
145 | 0 | return false; |
146 | 0 | s = Path::simplifyPath(Path::fromNativeSeparators(s)); |
147 | 0 | return true; |
148 | 0 | } |
149 | | |
150 | | void ImportProject::FileSettings::setIncludePaths(const std::string &basepath, const std::list<std::string> &in, std::map<std::string, std::string, cppcheck::stricmp> &variables) |
151 | 0 | { |
152 | 0 | std::set<std::string> found; |
153 | | // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) |
154 | 0 | const std::list<std::string> copyIn(in); |
155 | 0 | includePaths.clear(); |
156 | 0 | for (const std::string &ipath : copyIn) { |
157 | 0 | if (ipath.empty()) |
158 | 0 | continue; |
159 | 0 | if (startsWith(ipath,"%(")) |
160 | 0 | continue; |
161 | 0 | std::string s(Path::fromNativeSeparators(ipath)); |
162 | 0 | if (!found.insert(s).second) |
163 | 0 | continue; |
164 | 0 | if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) { |
165 | 0 | if (!endsWith(s,'/')) |
166 | 0 | s += '/'; |
167 | 0 | includePaths.push_back(std::move(s)); |
168 | 0 | continue; |
169 | 0 | } |
170 | | |
171 | 0 | if (endsWith(s,'/')) // this is a temporary hack, simplifyPath can crash if path ends with '/' |
172 | 0 | s.erase(s.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it |
173 | |
|
174 | 0 | if (s.find("$(") == std::string::npos) { |
175 | 0 | s = Path::simplifyPath(basepath + s); |
176 | 0 | } else { |
177 | 0 | if (!simplifyPathWithVariables(s, variables)) |
178 | 0 | continue; |
179 | 0 | } |
180 | 0 | if (s.empty()) |
181 | 0 | continue; |
182 | 0 | includePaths.push_back(s + '/'); |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | ImportProject::Type ImportProject::import(const std::string &filename, Settings *settings) |
187 | 0 | { |
188 | 0 | std::ifstream fin(filename); |
189 | 0 | if (!fin.is_open()) |
190 | 0 | return ImportProject::Type::MISSING; |
191 | | |
192 | 0 | mPath = Path::getPathFromFilename(Path::fromNativeSeparators(filename)); |
193 | 0 | if (!mPath.empty() && !endsWith(mPath,'/')) |
194 | 0 | mPath += '/'; |
195 | |
|
196 | 0 | const std::vector<std::string> fileFilters = |
197 | 0 | settings ? settings->fileFilters : std::vector<std::string>(); |
198 | |
|
199 | 0 | if (endsWith(filename, ".json")) { |
200 | 0 | if (importCompileCommands(fin)) { |
201 | 0 | setRelativePaths(filename); |
202 | 0 | return ImportProject::Type::COMPILE_DB; |
203 | 0 | } |
204 | 0 | } else if (endsWith(filename, ".sln")) { |
205 | 0 | if (importSln(fin, mPath, fileFilters)) { |
206 | 0 | setRelativePaths(filename); |
207 | 0 | return ImportProject::Type::VS_SLN; |
208 | 0 | } |
209 | 0 | } else if (endsWith(filename, ".vcxproj")) { |
210 | 0 | std::map<std::string, std::string, cppcheck::stricmp> variables; |
211 | 0 | if (importVcxproj(filename, variables, emptyString, fileFilters)) { |
212 | 0 | setRelativePaths(filename); |
213 | 0 | return ImportProject::Type::VS_VCXPROJ; |
214 | 0 | } |
215 | 0 | } else if (endsWith(filename, ".bpr")) { |
216 | 0 | if (importBcb6Prj(filename)) { |
217 | 0 | setRelativePaths(filename); |
218 | 0 | return ImportProject::Type::BORLAND; |
219 | 0 | } |
220 | 0 | } else if (settings && endsWith(filename, ".cppcheck")) { |
221 | 0 | if (importCppcheckGuiProject(fin, settings)) { |
222 | 0 | setRelativePaths(filename); |
223 | 0 | return ImportProject::Type::CPPCHECK_GUI; |
224 | 0 | } |
225 | 0 | } else { |
226 | 0 | return ImportProject::Type::UNKNOWN; |
227 | 0 | } |
228 | 0 | return ImportProject::Type::FAILURE; |
229 | 0 | } |
230 | | |
231 | | static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[]) |
232 | 0 | { |
233 | 0 | std::string ret; |
234 | 0 | bool escapedString = false; |
235 | 0 | bool str = false; |
236 | 0 | bool escape = false; |
237 | 0 | for (; *pos < command.size() && (str || !std::strchr(until, command[*pos])); (*pos)++) { |
238 | 0 | if (escape) |
239 | 0 | escape = false; |
240 | 0 | else if (command[*pos] == '\\') { |
241 | 0 | if (str) |
242 | 0 | escape = true; |
243 | 0 | else if (command[*pos + 1] == '"') { |
244 | 0 | if (escapedString) |
245 | 0 | return ret + "\\\""; |
246 | 0 | escapedString = true; |
247 | 0 | ret += "\\\""; |
248 | 0 | (*pos)++; |
249 | 0 | continue; |
250 | 0 | } |
251 | 0 | } else if (command[*pos] == '\"') |
252 | 0 | str = !str; |
253 | 0 | ret += command[*pos]; |
254 | 0 | } |
255 | 0 | return ret; |
256 | 0 | } |
257 | | |
258 | | static std::string unescape(const std::string &in) |
259 | 0 | { |
260 | 0 | std::string out; |
261 | 0 | bool escape = false; |
262 | 0 | for (const char c: in) { |
263 | 0 | if (escape) { |
264 | 0 | escape = false; |
265 | 0 | if (!std::strchr("\\\"\'",c)) |
266 | 0 | out += "\\"; |
267 | 0 | out += c; |
268 | 0 | } else if (c == '\\') |
269 | 0 | escape = true; |
270 | 0 | else |
271 | 0 | out += c; |
272 | 0 | } |
273 | 0 | return out; |
274 | 0 | } |
275 | | |
276 | | void ImportProject::FileSettings::parseCommand(const std::string& command) |
277 | 0 | { |
278 | 0 | std::string defs; |
279 | | |
280 | | // Parse command.. |
281 | 0 | std::string::size_type pos = 0; |
282 | 0 | while (std::string::npos != (pos = command.find(' ',pos))) { |
283 | 0 | while (pos < command.size() && command[pos] == ' ') |
284 | 0 | pos++; |
285 | 0 | if (pos >= command.size()) |
286 | 0 | break; |
287 | 0 | if (command[pos] != '/' && command[pos] != '-') |
288 | 0 | continue; |
289 | 0 | pos++; |
290 | 0 | if (pos >= command.size()) |
291 | 0 | break; |
292 | 0 | const char F = command[pos++]; |
293 | 0 | if (std::strchr("DUI", F)) { |
294 | 0 | while (pos < command.size() && command[pos] == ' ') |
295 | 0 | ++pos; |
296 | 0 | } |
297 | 0 | const std::string fval = readUntil(command, &pos, " ="); |
298 | 0 | if (F=='D') { |
299 | 0 | std::string defval = readUntil(command, &pos, " "); |
300 | 0 | defs += fval; |
301 | 0 | if (defval.size() >= 3 && startsWith(defval,"=\"") && defval.back()=='\"') |
302 | 0 | defval = "=" + unescape(defval.substr(2, defval.size() - 3)); |
303 | 0 | else if (defval.size() >= 5 && startsWith(defval, "=\\\"") && endsWith(defval, "\\\"")) |
304 | 0 | defval = "=\"" + unescape(defval.substr(3, defval.size() - 5)) + "\""; |
305 | 0 | if (!defval.empty()) |
306 | 0 | defs += defval; |
307 | 0 | defs += ';'; |
308 | 0 | } else if (F=='U') |
309 | 0 | undefs.insert(fval); |
310 | 0 | else if (F=='I') { |
311 | 0 | std::string i = fval; |
312 | 0 | if (i.size() > 1 && i[0] == '\"' && i.back() == '\"') |
313 | 0 | i = unescape(i.substr(1, i.size() - 2)); |
314 | 0 | if (std::find(includePaths.cbegin(), includePaths.cend(), i) == includePaths.cend()) |
315 | 0 | includePaths.push_back(std::move(i)); |
316 | 0 | } else if (F=='s' && startsWith(fval,"td")) { |
317 | 0 | ++pos; |
318 | 0 | standard = readUntil(command, &pos, " "); |
319 | 0 | } else if (F == 'i' && fval == "system") { |
320 | 0 | ++pos; |
321 | 0 | std::string isystem = readUntil(command, &pos, " "); |
322 | 0 | systemIncludePaths.push_back(std::move(isystem)); |
323 | 0 | } else if (F=='m') { |
324 | 0 | if (fval == "unicode") { |
325 | 0 | defs += "UNICODE"; |
326 | 0 | defs += ";"; |
327 | 0 | } |
328 | 0 | } else if (F=='f') { |
329 | 0 | if (fval == "pic") { |
330 | 0 | defs += "__pic__"; |
331 | 0 | defs += ";"; |
332 | 0 | } else if (fval == "PIC") { |
333 | 0 | defs += "__PIC__"; |
334 | 0 | defs += ";"; |
335 | 0 | } else if (fval == "pie") { |
336 | 0 | defs += "__pie__"; |
337 | 0 | defs += ";"; |
338 | 0 | } else if (fval == "PIE") { |
339 | 0 | defs += "__PIE__"; |
340 | 0 | defs += ";"; |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | 0 | setDefines(defs); |
345 | 0 | } |
346 | | |
347 | | bool ImportProject::importCompileCommands(std::istream &istr) |
348 | 0 | { |
349 | 0 | picojson::value compileCommands; |
350 | 0 | istr >> compileCommands; |
351 | 0 | if (!compileCommands.is<picojson::array>()) { |
352 | 0 | printError("compilation database is not a JSON array"); |
353 | 0 | return false; |
354 | 0 | } |
355 | | |
356 | 0 | for (const picojson::value &fileInfo : compileCommands.get<picojson::array>()) { |
357 | 0 | picojson::object obj = fileInfo.get<picojson::object>(); |
358 | 0 | std::string dirpath = Path::fromNativeSeparators(obj["directory"].get<std::string>()); |
359 | | |
360 | | /* CMAKE produces the directory without trailing / so add it if not |
361 | | * there - it is needed by setIncludePaths() */ |
362 | 0 | if (!endsWith(dirpath, '/')) |
363 | 0 | dirpath += '/'; |
364 | |
|
365 | 0 | const std::string directory = dirpath; |
366 | |
|
367 | 0 | std::string command; |
368 | 0 | if (obj.count("arguments")) { |
369 | 0 | if (obj["arguments"].is<picojson::array>()) { |
370 | 0 | for (const picojson::value& arg : obj["arguments"].get<picojson::array>()) { |
371 | 0 | if (arg.is<std::string>()) { |
372 | 0 | std::string str = arg.get<std::string>(); |
373 | 0 | if (str.find(' ') != std::string::npos) |
374 | 0 | str = "\"" + str + "\""; |
375 | 0 | command += str + " "; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | } else { |
379 | 0 | printError("'arguments' field in compilation database entry is not a JSON array"); |
380 | 0 | return false; |
381 | 0 | } |
382 | 0 | } else if (obj.count("command")) { |
383 | 0 | if (obj["command"].is<std::string>()) { |
384 | 0 | command = obj["command"].get<std::string>(); |
385 | 0 | } else { |
386 | 0 | printError("'command' field in compilation database entry is not a string"); |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | } else { |
390 | 0 | printError("no 'arguments' or 'command' field found in compilation database entry"); |
391 | 0 | return false; |
392 | 0 | } |
393 | | |
394 | 0 | if (!obj.count("file") || !obj["file"].is<std::string>()) { |
395 | 0 | printError("skip compilation database entry because it does not have a proper 'file' field"); |
396 | 0 | continue; |
397 | 0 | } |
398 | | |
399 | 0 | const std::string file = Path::fromNativeSeparators(obj["file"].get<std::string>()); |
400 | | |
401 | | // Accept file? |
402 | 0 | if (!Path::acceptFile(file)) |
403 | 0 | continue; |
404 | | |
405 | 0 | struct FileSettings fs; |
406 | 0 | if (Path::isAbsolute(file)) |
407 | 0 | fs.filename = Path::simplifyPath(file); |
408 | | #ifdef _WIN32 |
409 | | else if (file[0] == '/' && directory.size() > 2 && std::isalpha(directory[0]) && directory[1] == ':') |
410 | | // directory: C:\foo\bar |
411 | | // file: /xy/z.c |
412 | | // => c:/xy/z.c |
413 | | fs.filename = Path::simplifyPath(directory.substr(0,2) + file); |
414 | | #endif |
415 | 0 | else |
416 | 0 | fs.filename = Path::simplifyPath(directory + file); |
417 | 0 | if (!sourceFileExists(fs.filename)) { |
418 | 0 | printError("'" + fs.filename + "' from compilation database does not exist"); |
419 | 0 | return false; |
420 | 0 | } |
421 | 0 | fs.parseCommand(command); // read settings; -D, -I, -U, -std, -m*, -f* |
422 | 0 | std::map<std::string, std::string, cppcheck::stricmp> variables; |
423 | 0 | fs.setIncludePaths(directory, fs.includePaths, variables); |
424 | 0 | fileSettings.push_back(std::move(fs)); |
425 | 0 | } |
426 | | |
427 | 0 | return true; |
428 | 0 | } |
429 | | |
430 | | bool ImportProject::importSln(std::istream &istr, const std::string &path, const std::vector<std::string> &fileFilters) |
431 | 0 | { |
432 | 0 | std::string line; |
433 | |
|
434 | 0 | if (!std::getline(istr,line)) { |
435 | 0 | printError("Visual Studio solution file is empty"); |
436 | 0 | return false; |
437 | 0 | } |
438 | | |
439 | 0 | if (!startsWith(line, "Microsoft Visual Studio Solution File")) { |
440 | | // Skip BOM |
441 | 0 | if (!std::getline(istr, line) || !startsWith(line, "Microsoft Visual Studio Solution File")) { |
442 | 0 | printError("Visual Studio solution file header not found"); |
443 | 0 | return false; |
444 | 0 | } |
445 | 0 | } |
446 | | |
447 | 0 | std::map<std::string,std::string,cppcheck::stricmp> variables; |
448 | 0 | variables["SolutionDir"] = path; |
449 | |
|
450 | 0 | bool found = false; |
451 | |
|
452 | 0 | while (std::getline(istr,line)) { |
453 | 0 | if (!startsWith(line,"Project(")) |
454 | 0 | continue; |
455 | 0 | const std::string::size_type pos = line.find(".vcxproj"); |
456 | 0 | if (pos == std::string::npos) |
457 | 0 | continue; |
458 | 0 | const std::string::size_type pos1 = line.rfind('\"',pos); |
459 | 0 | if (pos1 == std::string::npos) |
460 | 0 | continue; |
461 | 0 | std::string vcxproj(line.substr(pos1+1, pos-pos1+7)); |
462 | 0 | if (!Path::isAbsolute(vcxproj)) |
463 | 0 | vcxproj = path + vcxproj; |
464 | 0 | if (!importVcxproj(Path::fromNativeSeparators(vcxproj), variables, emptyString, fileFilters)) { |
465 | 0 | printError("failed to load '" + vcxproj + "' from Visual Studio solution"); |
466 | 0 | return false; |
467 | 0 | } |
468 | 0 | found = true; |
469 | 0 | } |
470 | | |
471 | 0 | if (!found) { |
472 | 0 | printError("no projects found in Visual Studio solution file"); |
473 | 0 | return false; |
474 | 0 | } |
475 | | |
476 | 0 | return true; |
477 | 0 | } |
478 | | |
479 | | namespace { |
480 | | struct ProjectConfiguration { |
481 | 0 | explicit ProjectConfiguration(const tinyxml2::XMLElement *cfg) { |
482 | 0 | const char *a = cfg->Attribute("Include"); |
483 | 0 | if (a) |
484 | 0 | name = a; |
485 | 0 | for (const tinyxml2::XMLElement *e = cfg->FirstChildElement(); e; e = e->NextSiblingElement()) { |
486 | 0 | if (!e->GetText()) |
487 | 0 | continue; |
488 | 0 | if (std::strcmp(e->Name(),"Configuration")==0) |
489 | 0 | configuration = e->GetText(); |
490 | 0 | else if (std::strcmp(e->Name(),"Platform")==0) { |
491 | 0 | platformStr = e->GetText(); |
492 | 0 | if (platformStr == "Win32") |
493 | 0 | platform = Win32; |
494 | 0 | else if (platformStr == "x64") |
495 | 0 | platform = x64; |
496 | 0 | else |
497 | 0 | platform = Unknown; |
498 | 0 | } |
499 | 0 | } |
500 | 0 | } |
501 | | std::string name; |
502 | | std::string configuration; |
503 | | enum { Win32, x64, Unknown } platform = Unknown; |
504 | | std::string platformStr; |
505 | | }; |
506 | | |
507 | | struct ItemDefinitionGroup { |
508 | 0 | explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : additionalIncludePaths(std::move(includePaths)) { |
509 | 0 | const char *condAttr = idg->Attribute("Condition"); |
510 | 0 | if (condAttr) |
511 | 0 | condition = condAttr; |
512 | 0 | for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) { |
513 | 0 | if (std::strcmp(e1->Name(), "ClCompile") == 0) { |
514 | 0 | enhancedInstructionSet = "StreamingSIMDExtensions2"; |
515 | 0 | for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { |
516 | 0 | if (e->GetText()) { |
517 | 0 | if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0) |
518 | 0 | preprocessorDefinitions = e->GetText(); |
519 | 0 | else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) { |
520 | 0 | if (!additionalIncludePaths.empty()) |
521 | 0 | additionalIncludePaths += ';'; |
522 | 0 | additionalIncludePaths += e->GetText(); |
523 | 0 | } else if (std::strcmp(e->Name(), "LanguageStandard") == 0) { |
524 | 0 | if (std::strcmp(e->GetText(), "stdcpp14") == 0) |
525 | 0 | cppstd = Standards::CPP14; |
526 | 0 | else if (std::strcmp(e->GetText(), "stdcpp17") == 0) |
527 | 0 | cppstd = Standards::CPP17; |
528 | 0 | else if (std::strcmp(e->GetText(), "stdcpp20") == 0) |
529 | 0 | cppstd = Standards::CPP20; |
530 | 0 | else if (std::strcmp(e->GetText(), "stdcpplatest") == 0) |
531 | 0 | cppstd = Standards::CPPLatest; |
532 | 0 | } else if (std::strcmp(e->Name(), "EnableEnhancedInstructionSet") == 0) { |
533 | 0 | enhancedInstructionSet = e->GetText(); |
534 | 0 | } |
535 | 0 | } |
536 | 0 | } |
537 | 0 | } |
538 | 0 | else if (std::strcmp(e1->Name(), "Link") == 0) { |
539 | 0 | for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { |
540 | 0 | if (!e->GetText()) |
541 | 0 | continue; |
542 | 0 | if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) { |
543 | 0 | entryPointSymbol = e->GetText(); |
544 | 0 | } |
545 | 0 | } |
546 | 0 | } |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | 0 | static void replaceAll(std::string &c, const std::string &from, const std::string &to) { |
551 | 0 | std::string::size_type pos; |
552 | 0 | while ((pos = c.find(from)) != std::string::npos) { |
553 | 0 | c.erase(pos,from.size()); |
554 | 0 | c.insert(pos,to); |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | 0 | bool conditionIsTrue(const ProjectConfiguration &p) const { |
559 | 0 | if (condition.empty()) |
560 | 0 | return true; |
561 | 0 | std::string c = '(' + condition + ");"; |
562 | 0 | replaceAll(c, "$(Configuration)", p.configuration); |
563 | 0 | replaceAll(c, "$(Platform)", p.platformStr); |
564 | | |
565 | | // TODO : Better evaluation |
566 | 0 | Settings s; |
567 | 0 | std::istringstream istr(c); |
568 | 0 | Tokenizer tokenizer(&s); |
569 | 0 | tokenizer.tokenize(istr,"vcxproj"); |
570 | 0 | for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { |
571 | 0 | if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) { |
572 | 0 | if (tok->astOperand1()->expressionString() == "Configuration.Contains") |
573 | 0 | return ('\'' + p.configuration + '\'') == tok->astOperand2()->str(); |
574 | 0 | } |
575 | 0 | if (tok->str() == "==" && tok->astOperand1() && tok->astOperand2() && tok->astOperand1()->str() == tok->astOperand2()->str()) |
576 | 0 | return true; |
577 | 0 | } |
578 | 0 | return false; |
579 | 0 | } |
580 | | std::string condition; |
581 | | std::string enhancedInstructionSet; |
582 | | std::string preprocessorDefinitions; |
583 | | std::string additionalIncludePaths; |
584 | | std::string entryPointSymbol; // TODO: use this |
585 | | Standards::cppstd_t cppstd = Standards::CPPLatest; |
586 | | }; |
587 | | } |
588 | | |
589 | | static std::list<std::string> toStringList(const std::string &s) |
590 | 0 | { |
591 | 0 | std::list<std::string> ret; |
592 | 0 | std::string::size_type pos1 = 0; |
593 | 0 | std::string::size_type pos2; |
594 | 0 | while ((pos2 = s.find(';',pos1)) != std::string::npos) { |
595 | 0 | ret.push_back(s.substr(pos1, pos2-pos1)); |
596 | 0 | pos1 = pos2 + 1; |
597 | 0 | if (pos1 >= s.size()) |
598 | 0 | break; |
599 | 0 | } |
600 | 0 | if (pos1 < s.size()) |
601 | 0 | ret.push_back(s.substr(pos1)); |
602 | 0 | return ret; |
603 | 0 | } |
604 | | |
605 | | static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map<std::string,std::string,cppcheck::stricmp> &variables, std::string &includePath, bool *useOfMfc) |
606 | 0 | { |
607 | 0 | if (useOfMfc) { |
608 | 0 | for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { |
609 | 0 | if (std::strcmp(e->Name(), "UseOfMfc") == 0) { |
610 | 0 | *useOfMfc = true; |
611 | 0 | break; |
612 | 0 | } |
613 | 0 | } |
614 | 0 | } |
615 | |
|
616 | 0 | const char* labelAttribute = node->Attribute("Label"); |
617 | 0 | if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) { |
618 | 0 | for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { |
619 | 0 | const std::string name(propertyGroup->Name()); |
620 | 0 | const char *text = propertyGroup->GetText(); |
621 | 0 | variables[name] = std::string(text ? text : ""); |
622 | 0 | } |
623 | |
|
624 | 0 | } else if (!labelAttribute) { |
625 | 0 | for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { |
626 | 0 | if (std::strcmp(propertyGroup->Name(), "IncludePath") != 0) |
627 | 0 | continue; |
628 | 0 | const char *text = propertyGroup->GetText(); |
629 | 0 | if (!text) |
630 | 0 | continue; |
631 | 0 | std::string path(text); |
632 | 0 | const std::string::size_type pos = path.find("$(IncludePath)"); |
633 | 0 | if (pos != std::string::npos) |
634 | 0 | path.replace(pos, 14U, includePath); |
635 | 0 | includePath = path; |
636 | 0 | } |
637 | 0 | } |
638 | 0 | } |
639 | | |
640 | | static void loadVisualStudioProperties(const std::string &props, std::map<std::string,std::string,cppcheck::stricmp> &variables, std::string &includePath, const std::string &additionalIncludeDirectories, std::list<ItemDefinitionGroup> &itemDefinitionGroupList) |
641 | 0 | { |
642 | 0 | std::string filename(props); |
643 | | // variables can't be resolved |
644 | 0 | if (!simplifyPathWithVariables(filename, variables)) |
645 | 0 | return; |
646 | | |
647 | | // prepend project dir (if it exists) to transform relative paths into absolute ones |
648 | 0 | if (!Path::isAbsolute(filename) && variables.count("ProjectDir") > 0) |
649 | 0 | filename = Path::getAbsoluteFilePath(variables.at("ProjectDir") + filename); |
650 | |
|
651 | 0 | tinyxml2::XMLDocument doc; |
652 | 0 | if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) |
653 | 0 | return; |
654 | 0 | const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); |
655 | 0 | if (rootnode == nullptr) |
656 | 0 | return; |
657 | 0 | for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { |
658 | 0 | if (std::strcmp(node->Name(), "ImportGroup") == 0) { |
659 | 0 | const char *labelAttribute = node->Attribute("Label"); |
660 | 0 | if (labelAttribute == nullptr || std::strcmp(labelAttribute, "PropertySheets") != 0) |
661 | 0 | continue; |
662 | 0 | for (const tinyxml2::XMLElement *importGroup = node->FirstChildElement(); importGroup; importGroup = importGroup->NextSiblingElement()) { |
663 | 0 | if (std::strcmp(importGroup->Name(), "Import") == 0) { |
664 | 0 | const char *projectAttribute = importGroup->Attribute("Project"); |
665 | 0 | if (projectAttribute == nullptr) |
666 | 0 | continue; |
667 | 0 | std::string loadprj(projectAttribute); |
668 | 0 | if (loadprj.find('$') == std::string::npos) { |
669 | 0 | loadprj = Path::getPathFromFilename(filename) + loadprj; |
670 | 0 | } |
671 | 0 | loadVisualStudioProperties(loadprj, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList); |
672 | 0 | } |
673 | 0 | } |
674 | 0 | } else if (std::strcmp(node->Name(),"PropertyGroup")==0) { |
675 | 0 | importPropertyGroup(node, variables, includePath, nullptr); |
676 | 0 | } else if (std::strcmp(node->Name(),"ItemDefinitionGroup")==0) { |
677 | 0 | itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } |
681 | | |
682 | | bool ImportProject::importVcxproj(const std::string &filename, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters) |
683 | 0 | { |
684 | 0 | variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename)); |
685 | |
|
686 | 0 | std::list<ProjectConfiguration> projectConfigurationList; |
687 | 0 | std::list<std::string> compileList; |
688 | 0 | std::list<ItemDefinitionGroup> itemDefinitionGroupList; |
689 | 0 | std::string includePath; |
690 | |
|
691 | 0 | bool useOfMfc = false; |
692 | |
|
693 | 0 | tinyxml2::XMLDocument doc; |
694 | 0 | const tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); |
695 | 0 | if (error != tinyxml2::XML_SUCCESS) { |
696 | 0 | printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); |
697 | 0 | return false; |
698 | 0 | } |
699 | 0 | const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); |
700 | 0 | if (rootnode == nullptr) { |
701 | 0 | printError("Visual Studio project file has no XML root node"); |
702 | 0 | return false; |
703 | 0 | } |
704 | 0 | for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { |
705 | 0 | if (std::strcmp(node->Name(), "ItemGroup") == 0) { |
706 | 0 | const char *labelAttribute = node->Attribute("Label"); |
707 | 0 | if (labelAttribute && std::strcmp(labelAttribute, "ProjectConfigurations") == 0) { |
708 | 0 | for (const tinyxml2::XMLElement *cfg = node->FirstChildElement(); cfg; cfg = cfg->NextSiblingElement()) { |
709 | 0 | if (std::strcmp(cfg->Name(), "ProjectConfiguration") == 0) { |
710 | 0 | const ProjectConfiguration p(cfg); |
711 | 0 | if (p.platform != ProjectConfiguration::Unknown) { |
712 | 0 | projectConfigurationList.emplace_back(cfg); |
713 | 0 | mAllVSConfigs.insert(p.configuration); |
714 | 0 | } |
715 | 0 | } |
716 | 0 | } |
717 | 0 | } else { |
718 | 0 | for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { |
719 | 0 | if (std::strcmp(e->Name(), "ClCompile") == 0) { |
720 | 0 | const char *include = e->Attribute("Include"); |
721 | 0 | if (include && Path::acceptFile(include)) |
722 | 0 | compileList.emplace_back(include); |
723 | 0 | } |
724 | 0 | } |
725 | 0 | } |
726 | 0 | } else if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) { |
727 | 0 | itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); |
728 | 0 | } else if (std::strcmp(node->Name(), "PropertyGroup") == 0) { |
729 | 0 | importPropertyGroup(node, variables, includePath, &useOfMfc); |
730 | 0 | } else if (std::strcmp(node->Name(), "ImportGroup") == 0) { |
731 | 0 | const char *labelAttribute = node->Attribute("Label"); |
732 | 0 | if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) { |
733 | 0 | for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { |
734 | 0 | if (std::strcmp(e->Name(), "Import") == 0) { |
735 | 0 | const char *projectAttribute = e->Attribute("Project"); |
736 | 0 | if (projectAttribute) |
737 | 0 | loadVisualStudioProperties(projectAttribute, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList); |
738 | 0 | } |
739 | 0 | } |
740 | 0 | } |
741 | 0 | } |
742 | 0 | } |
743 | |
|
744 | 0 | for (const std::string &c : compileList) { |
745 | 0 | const std::string cfilename = Path::simplifyPath(Path::isAbsolute(c) ? c : Path::getPathFromFilename(filename) + c); |
746 | 0 | if (!fileFilters.empty() && !matchglobs(fileFilters, cfilename)) |
747 | 0 | continue; |
748 | | |
749 | 0 | for (const ProjectConfiguration &p : projectConfigurationList) { |
750 | |
|
751 | 0 | if (!guiProject.checkVsConfigs.empty()) { |
752 | 0 | const bool doChecking = std::any_of(guiProject.checkVsConfigs.cbegin(), guiProject.checkVsConfigs.cend(), [&](const std::string& c) { |
753 | 0 | return c == p.configuration; |
754 | 0 | }); |
755 | 0 | if (!doChecking) |
756 | 0 | continue; |
757 | 0 | } |
758 | | |
759 | 0 | FileSettings fs; |
760 | 0 | fs.filename = cfilename; |
761 | 0 | fs.cfg = p.name; |
762 | 0 | fs.msc = true; |
763 | 0 | fs.useMfc = useOfMfc; |
764 | 0 | fs.defines = "_WIN32=1"; |
765 | 0 | if (p.platform == ProjectConfiguration::Win32) |
766 | 0 | fs.platformType = cppcheck::Platform::Type::Win32W; |
767 | 0 | else if (p.platform == ProjectConfiguration::x64) { |
768 | 0 | fs.platformType = cppcheck::Platform::Type::Win64; |
769 | 0 | fs.defines += ";_WIN64=1"; |
770 | 0 | } |
771 | 0 | std::string additionalIncludePaths; |
772 | 0 | for (const ItemDefinitionGroup &i : itemDefinitionGroupList) { |
773 | 0 | if (!i.conditionIsTrue(p)) |
774 | 0 | continue; |
775 | 0 | fs.standard = Standards::getCPP(i.cppstd); |
776 | 0 | fs.defines += ';' + i.preprocessorDefinitions; |
777 | 0 | if (i.enhancedInstructionSet == "StreamingSIMDExtensions") |
778 | 0 | fs.defines += ";__SSE__"; |
779 | 0 | else if (i.enhancedInstructionSet == "StreamingSIMDExtensions2") |
780 | 0 | fs.defines += ";__SSE2__"; |
781 | 0 | else if (i.enhancedInstructionSet == "AdvancedVectorExtensions") |
782 | 0 | fs.defines += ";__AVX__"; |
783 | 0 | else if (i.enhancedInstructionSet == "AdvancedVectorExtensions2") |
784 | 0 | fs.defines += ";__AVX2__"; |
785 | 0 | else if (i.enhancedInstructionSet == "AdvancedVectorExtensions512") |
786 | 0 | fs.defines += ";__AVX512__"; |
787 | 0 | additionalIncludePaths += ';' + i.additionalIncludePaths; |
788 | 0 | } |
789 | 0 | fs.setDefines(fs.defines); |
790 | 0 | fs.setIncludePaths(Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables); |
791 | 0 | fileSettings.push_back(std::move(fs)); |
792 | 0 | } |
793 | 0 | } |
794 | |
|
795 | 0 | return true; |
796 | 0 | } |
797 | | |
798 | | bool ImportProject::importBcb6Prj(const std::string &projectFilename) |
799 | 0 | { |
800 | 0 | tinyxml2::XMLDocument doc; |
801 | 0 | const tinyxml2::XMLError error = doc.LoadFile(projectFilename.c_str()); |
802 | 0 | if (error != tinyxml2::XML_SUCCESS) { |
803 | 0 | printError(std::string("Borland project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); |
804 | 0 | return false; |
805 | 0 | } |
806 | 0 | const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); |
807 | 0 | if (rootnode == nullptr) { |
808 | 0 | printError("Borland project file has no XML root node"); |
809 | 0 | return false; |
810 | 0 | } |
811 | | |
812 | 0 | const std::string& projectDir = Path::simplifyPath(Path::getPathFromFilename(projectFilename)); |
813 | |
|
814 | 0 | std::list<std::string> compileList; |
815 | 0 | std::string includePath; |
816 | 0 | std::string userdefines; |
817 | 0 | std::string sysdefines; |
818 | 0 | std::string cflag1; |
819 | |
|
820 | 0 | for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { |
821 | 0 | if (std::strcmp(node->Name(), "FILELIST") == 0) { |
822 | 0 | for (const tinyxml2::XMLElement *f = node->FirstChildElement(); f; f = f->NextSiblingElement()) { |
823 | 0 | if (std::strcmp(f->Name(), "FILE") == 0) { |
824 | 0 | const char *filename = f->Attribute("FILENAME"); |
825 | 0 | if (filename && Path::acceptFile(filename)) |
826 | 0 | compileList.emplace_back(filename); |
827 | 0 | } |
828 | 0 | } |
829 | 0 | } else if (std::strcmp(node->Name(), "MACROS") == 0) { |
830 | 0 | for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { |
831 | 0 | if (std::strcmp(m->Name(), "INCLUDEPATH") == 0) { |
832 | 0 | const char *v = m->Attribute("value"); |
833 | 0 | if (v) |
834 | 0 | includePath = v; |
835 | 0 | } else if (std::strcmp(m->Name(), "USERDEFINES") == 0) { |
836 | 0 | const char *v = m->Attribute("value"); |
837 | 0 | if (v) |
838 | 0 | userdefines = v; |
839 | 0 | } else if (std::strcmp(m->Name(), "SYSDEFINES") == 0) { |
840 | 0 | const char *v = m->Attribute("value"); |
841 | 0 | if (v) |
842 | 0 | sysdefines = v; |
843 | 0 | } |
844 | 0 | } |
845 | 0 | } else if (std::strcmp(node->Name(), "OPTIONS") == 0) { |
846 | 0 | for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { |
847 | 0 | if (std::strcmp(m->Name(), "CFLAG1") == 0) { |
848 | 0 | const char *v = m->Attribute("value"); |
849 | 0 | if (v) |
850 | 0 | cflag1 = v; |
851 | 0 | } |
852 | 0 | } |
853 | 0 | } |
854 | 0 | } |
855 | |
|
856 | 0 | std::set<std::string> cflags; |
857 | | |
858 | | // parse cflag1 and fill the cflags set |
859 | 0 | { |
860 | 0 | std::string arg; |
861 | |
|
862 | 0 | for (const char i : cflag1) { |
863 | 0 | if (i == ' ' && !arg.empty()) { |
864 | 0 | cflags.insert(arg); |
865 | 0 | arg.clear(); |
866 | 0 | continue; |
867 | 0 | } |
868 | 0 | arg += i; |
869 | 0 | } |
870 | |
|
871 | 0 | if (!arg.empty()) { |
872 | 0 | cflags.insert(arg); |
873 | 0 | } |
874 | | |
875 | | // cleanup: -t is "An alternate name for the -Wxxx switches; there is no difference" |
876 | | // -> Remove every known -txxx argument and replace it with its -Wxxx counterpart. |
877 | | // This way, we know what we have to check for later on. |
878 | 0 | static const std::map<std::string, std::string> synonyms = { |
879 | 0 | { "-tC","-WC" }, |
880 | 0 | { "-tCDR","-WCDR" }, |
881 | 0 | { "-tCDV","-WCDV" }, |
882 | 0 | { "-tW","-W" }, |
883 | 0 | { "-tWC","-WC" }, |
884 | 0 | { "-tWCDR","-WCDR" }, |
885 | 0 | { "-tWCDV","-WCDV" }, |
886 | 0 | { "-tWD","-WD" }, |
887 | 0 | { "-tWDR","-WDR" }, |
888 | 0 | { "-tWDV","-WDV" }, |
889 | 0 | { "-tWM","-WM" }, |
890 | 0 | { "-tWP","-WP" }, |
891 | 0 | { "-tWR","-WR" }, |
892 | 0 | { "-tWU","-WU" }, |
893 | 0 | { "-tWV","-WV" } |
894 | 0 | }; |
895 | |
|
896 | 0 | for (std::map<std::string, std::string>::const_iterator i = synonyms.cbegin(); i != synonyms.cend(); ++i) { |
897 | 0 | if (cflags.erase(i->first) > 0) { |
898 | 0 | cflags.insert(i->second); |
899 | 0 | } |
900 | 0 | } |
901 | 0 | } |
902 | |
|
903 | 0 | std::string predefines; |
904 | 0 | std::string cppPredefines; |
905 | | |
906 | | // Collecting predefines. See BCB6 help topic "Predefined macros" |
907 | 0 | { |
908 | 0 | cppPredefines += |
909 | | // Defined if you've selected C++ compilation; will increase in later releases. |
910 | | // value 0x0560 (but 0x0564 for our BCB6 SP4) |
911 | | // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros |
912 | 0 | ";__BCPLUSPLUS__=0x0560" |
913 | | |
914 | | // Defined if in C++ mode; otherwise, undefined. |
915 | 0 | ";__cplusplus=1" |
916 | | |
917 | | // Defined as 1 for C++ files(meaning that templates are supported); otherwise, it is undefined. |
918 | 0 | ";__TEMPLATES__=1" |
919 | | |
920 | | // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. |
921 | 0 | ";_WCHAR_T" |
922 | | |
923 | | // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. |
924 | 0 | ";_WCHAR_T_DEFINED" |
925 | | |
926 | | // Defined in any compiler that has an optimizer. |
927 | 0 | ";__BCOPT__=1" |
928 | | |
929 | | // Version number. |
930 | | // BCB6 is 0x056X (SP4 is 0x0564) |
931 | | // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros |
932 | 0 | ";__BORLANDC__=0x0560" |
933 | 0 | ";__TCPLUSPLUS__=0x0560" |
934 | 0 | ";__TURBOC__=0x0560"; |
935 | | |
936 | | // Defined if Calling Convention is set to cdecl; otherwise undefined. |
937 | 0 | const bool useCdecl = (cflags.find("-p") == cflags.end() |
938 | 0 | && cflags.find("-pm") == cflags.end() |
939 | 0 | && cflags.find("-pr") == cflags.end() |
940 | 0 | && cflags.find("-ps") == cflags.end()); |
941 | 0 | if (useCdecl) |
942 | 0 | predefines += ";__CDECL=1"; |
943 | | |
944 | | // Defined by default indicating that the default char is unsigned char. Use the -K compiler option to undefine this macro. |
945 | 0 | const bool treatCharAsUnsignedChar = (cflags.find("-K") != cflags.end()); |
946 | 0 | if (treatCharAsUnsignedChar) |
947 | 0 | predefines += ";_CHAR_UNSIGNED=1"; |
948 | | |
949 | | // Defined whenever one of the CodeGuard compiler options is used; otherwise it is undefined. |
950 | 0 | const bool codeguardUsed = (cflags.find("-vGd") != cflags.end() |
951 | 0 | || cflags.find("-vGt") != cflags.end() |
952 | 0 | || cflags.find("-vGc") != cflags.end()); |
953 | 0 | if (codeguardUsed) |
954 | 0 | predefines += ";__CODEGUARD__"; |
955 | | |
956 | | // When defined, the macro indicates that the program is a console application. |
957 | 0 | const bool isConsoleApp = (cflags.find("-WC") != cflags.end()); |
958 | 0 | if (isConsoleApp) |
959 | 0 | predefines += ";__CONSOLE__=1"; |
960 | | |
961 | | // Enable stack unwinding. This is true by default; use -xd- to disable. |
962 | 0 | const bool enableStackUnwinding = (cflags.find("-xd-") == cflags.end()); |
963 | 0 | if (enableStackUnwinding) |
964 | 0 | predefines += ";_CPPUNWIND=1"; |
965 | | |
966 | | // Defined whenever the -WD compiler option is used; otherwise it is undefined. |
967 | 0 | const bool isDLL = (cflags.find("-WD") != cflags.end()); |
968 | 0 | if (isDLL) |
969 | 0 | predefines += ";__DLL__=1"; |
970 | | |
971 | | // Defined when compiling in 32-bit flat memory model. |
972 | | // TODO: not sure how to switch to another memory model or how to read configuration from project file |
973 | 0 | predefines += ";__FLAT__=1"; |
974 | | |
975 | | // Always defined. The default value is 300. You can change the value to 400 or 500 by using the /4 or /5 compiler options. |
976 | 0 | if (cflags.find("-6") != cflags.end()) |
977 | 0 | predefines += ";_M_IX86=600"; |
978 | 0 | else if (cflags.find("-5") != cflags.end()) |
979 | 0 | predefines += ";_M_IX86=500"; |
980 | 0 | else if (cflags.find("-4") != cflags.end()) |
981 | 0 | predefines += ";_M_IX86=400"; |
982 | 0 | else |
983 | 0 | predefines += ";_M_IX86=300"; |
984 | | |
985 | | // Defined only if the -WM option is used. It specifies that the multithread library is to be linked. |
986 | 0 | const bool linkMtLib = (cflags.find("-WM") != cflags.end()); |
987 | 0 | if (linkMtLib) |
988 | 0 | predefines += ";__MT__=1"; |
989 | | |
990 | | // Defined if Calling Convention is set to Pascal; otherwise undefined. |
991 | 0 | const bool usePascalCallingConvention = (cflags.find("-p") != cflags.end()); |
992 | 0 | if (usePascalCallingConvention) |
993 | 0 | predefines += ";__PASCAL__=1"; |
994 | | |
995 | | // Defined if you compile with the -A compiler option; otherwise, it is undefined. |
996 | 0 | const bool useAnsiKeywordExtensions = (cflags.find("-A") != cflags.end()); |
997 | 0 | if (useAnsiKeywordExtensions) |
998 | 0 | predefines += ";__STDC__=1"; |
999 | | |
1000 | | // Thread Local Storage. Always true in C++Builder. |
1001 | 0 | predefines += ";__TLC__=1"; |
1002 | | |
1003 | | // Defined for Windows-only code. |
1004 | 0 | const bool isWindowsTarget = (cflags.find("-WC") != cflags.end() |
1005 | 0 | || cflags.find("-WCDR") != cflags.end() |
1006 | 0 | || cflags.find("-WCDV") != cflags.end() |
1007 | 0 | || cflags.find("-WD") != cflags.end() |
1008 | 0 | || cflags.find("-WDR") != cflags.end() |
1009 | 0 | || cflags.find("-WDV") != cflags.end() |
1010 | 0 | || cflags.find("-WM") != cflags.end() |
1011 | 0 | || cflags.find("-WP") != cflags.end() |
1012 | 0 | || cflags.find("-WR") != cflags.end() |
1013 | 0 | || cflags.find("-WU") != cflags.end() |
1014 | 0 | || cflags.find("-WV") != cflags.end()); |
1015 | 0 | if (isWindowsTarget) |
1016 | 0 | predefines += ";_Windows"; |
1017 | | |
1018 | | // Defined for console and GUI applications. |
1019 | | // TODO: I'm not sure about the difference to define "_Windows". |
1020 | | // From description, I would assume __WIN32__ is only defined for |
1021 | | // executables, while _Windows would also be defined for DLLs, etc. |
1022 | | // However, in a newly created DLL project, both __WIN32__ and |
1023 | | // _Windows are defined. -> treating them the same for now. |
1024 | | // Also boost uses __WIN32__ for OS identification. |
1025 | 0 | const bool isConsoleOrGuiApp = isWindowsTarget; |
1026 | 0 | if (isConsoleOrGuiApp) |
1027 | 0 | predefines += ";__WIN32__=1"; |
1028 | 0 | } |
1029 | | |
1030 | | // Include paths may contain variables like "$(BCB)\include" or "$(BCB)\include\vcl". |
1031 | | // Those get resolved by ImportProject::FileSettings::setIncludePaths by |
1032 | | // 1. checking the provided variables map ("BCB" => "C:\\Program Files (x86)\\Borland\\CBuilder6") |
1033 | | // 2. checking env variables as a fallback |
1034 | | // Setting env is always possible. Configuring the variables via cli might be an addition. |
1035 | | // Reading the BCB6 install location from registry in windows environments would also be possible, |
1036 | | // but I didn't see any such functionality around the source. Not in favor of adding it only |
1037 | | // for the BCB6 project loading. |
1038 | 0 | std::map<std::string, std::string, cppcheck::stricmp> variables; |
1039 | 0 | const std::string defines = predefines + ";" + sysdefines + ";" + userdefines; |
1040 | 0 | const std::string cppDefines = cppPredefines + ";" + defines; |
1041 | 0 | const bool forceCppMode = (cflags.find("-P") != cflags.end()); |
1042 | |
|
1043 | 0 | for (const std::string &c : compileList) { |
1044 | | // C++ compilation is selected by file extension by default, so these |
1045 | | // defines have to be configured on a per-file base. |
1046 | | // |
1047 | | // > Files with the .CPP extension compile as C++ files. Files with a .C |
1048 | | // > extension, with no extension, or with extensions other than .CPP, |
1049 | | // > .OBJ, .LIB, or .ASM compile as C files. |
1050 | | // (http://docwiki.embarcadero.com/RADStudio/Tokyo/en/BCC32.EXE,_the_C%2B%2B_32-bit_Command-Line_Compiler) |
1051 | | // |
1052 | | // We can also force C++ compilation for all files using the -P command line switch. |
1053 | 0 | const bool cppMode = forceCppMode || Path::getFilenameExtensionInLowerCase(c) == ".cpp"; |
1054 | 0 | FileSettings fs; |
1055 | 0 | fs.setIncludePaths(projectDir, toStringList(includePath), variables); |
1056 | 0 | fs.setDefines(cppMode ? cppDefines : defines); |
1057 | 0 | fs.filename = Path::simplifyPath(Path::isAbsolute(c) ? c : projectDir + c); |
1058 | 0 | fileSettings.push_back(std::move(fs)); |
1059 | 0 | } |
1060 | |
|
1061 | 0 | return true; |
1062 | 0 | } |
1063 | | |
1064 | | static std::string joinRelativePath(const std::string &path1, const std::string &path2) |
1065 | 0 | { |
1066 | 0 | if (!path1.empty() && !Path::isAbsolute(path2)) |
1067 | 0 | return path1 + path2; |
1068 | 0 | return path2; |
1069 | 0 | } |
1070 | | |
1071 | | static std::list<std::string> readXmlStringList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[]) |
1072 | 0 | { |
1073 | 0 | std::list<std::string> ret; |
1074 | 0 | for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { |
1075 | 0 | if (strcmp(child->Name(), name) != 0) |
1076 | 0 | continue; |
1077 | 0 | const char *attr = attribute ? child->Attribute(attribute) : child->GetText(); |
1078 | 0 | if (attr) |
1079 | 0 | ret.push_back(joinRelativePath(path, attr)); |
1080 | 0 | } |
1081 | 0 | return ret; |
1082 | 0 | } |
1083 | | |
1084 | | static std::string join(const std::list<std::string> &strlist, const char *sep) |
1085 | 0 | { |
1086 | 0 | std::string ret; |
1087 | 0 | for (const std::string &s : strlist) { |
1088 | 0 | ret += (ret.empty() ? "" : sep) + s; |
1089 | 0 | } |
1090 | 0 | return ret; |
1091 | 0 | } |
1092 | | |
1093 | | static std::string istream_to_string(std::istream &istr) |
1094 | 0 | { |
1095 | 0 | std::istreambuf_iterator<char> eos; |
1096 | 0 | return std::string(std::istreambuf_iterator<char>(istr), eos); |
1097 | 0 | } |
1098 | | |
1099 | 0 | static const char * readSafe(const char *s, const char *def) { |
1100 | 0 | return s ? s : def; |
1101 | 0 | } |
1102 | | |
1103 | | bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *settings) |
1104 | 0 | { |
1105 | 0 | tinyxml2::XMLDocument doc; |
1106 | 0 | const std::string xmldata = istream_to_string(istr); |
1107 | 0 | const tinyxml2::XMLError error = doc.Parse(xmldata.data(), xmldata.size()); |
1108 | 0 | if (error != tinyxml2::XML_SUCCESS) { |
1109 | 0 | printError(std::string("Cppcheck GUI project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); |
1110 | 0 | return false; |
1111 | 0 | } |
1112 | 0 | const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); |
1113 | 0 | if (rootnode == nullptr || strcmp(rootnode->Name(), CppcheckXml::ProjectElementName) != 0) { |
1114 | 0 | printError("Cppcheck GUI project file has no XML root node"); |
1115 | 0 | return false; |
1116 | 0 | } |
1117 | | |
1118 | 0 | const std::string &path = mPath; |
1119 | |
|
1120 | 0 | std::list<std::string> paths; |
1121 | 0 | std::list<Suppressions::Suppression> suppressions; |
1122 | 0 | Settings temp; |
1123 | |
|
1124 | 0 | guiProject.analyzeAllVsConfigs.clear(); |
1125 | |
|
1126 | 0 | bool checkLevelExhaustive = false; |
1127 | | |
1128 | | // TODO: this should support all available command-line options |
1129 | 0 | for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { |
1130 | 0 | if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0) { |
1131 | 0 | if (node->Attribute(CppcheckXml::RootPathNameAttrib)) { |
1132 | 0 | temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib))); |
1133 | 0 | temp.relativePaths = true; |
1134 | 0 | } |
1135 | 0 | } else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0) |
1136 | 0 | temp.buildDir = joinRelativePath(path, readSafe(node->GetText(), "")); |
1137 | 0 | else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0) |
1138 | 0 | temp.includePaths = readXmlStringList(node, path, CppcheckXml::DirElementName, CppcheckXml::DirNameAttrib); |
1139 | 0 | else if (strcmp(node->Name(), CppcheckXml::DefinesElementName) == 0) |
1140 | 0 | temp.userDefines = join(readXmlStringList(node, "", CppcheckXml::DefineName, CppcheckXml::DefineNameAttrib), ";"); |
1141 | 0 | else if (strcmp(node->Name(), CppcheckXml::UndefinesElementName) == 0) { |
1142 | 0 | for (const std::string &u : readXmlStringList(node, "", CppcheckXml::UndefineName, nullptr)) |
1143 | 0 | temp.userUndefs.insert(u); |
1144 | 0 | } else if (strcmp(node->Name(), CppcheckXml::ImportProjectElementName) == 0) { |
1145 | 0 | const std::string t_str = readSafe(node->GetText(), ""); |
1146 | 0 | if (!t_str.empty()) |
1147 | 0 | guiProject.projectFile = path + t_str; |
1148 | 0 | } |
1149 | 0 | else if (strcmp(node->Name(), CppcheckXml::PathsElementName) == 0) |
1150 | 0 | paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib); |
1151 | 0 | else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0) |
1152 | 0 | guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); |
1153 | 0 | else if (strcmp(node->Name(), CppcheckXml::FunctionContracts) == 0) |
1154 | 0 | ; |
1155 | 0 | else if (strcmp(node->Name(), CppcheckXml::VariableContractsElementName) == 0) |
1156 | 0 | ; |
1157 | 0 | else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0) |
1158 | 0 | guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); |
1159 | 0 | else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0) |
1160 | 0 | guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); |
1161 | 0 | else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) { |
1162 | 0 | for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { |
1163 | 0 | if (strcmp(child->Name(), CppcheckXml::SuppressionElementName) != 0) |
1164 | 0 | continue; |
1165 | 0 | Suppressions::Suppression s; |
1166 | 0 | s.errorId = readSafe(child->GetText(), ""); |
1167 | 0 | s.fileName = readSafe(child->Attribute("fileName"), ""); |
1168 | 0 | if (!s.fileName.empty()) |
1169 | 0 | s.fileName = joinRelativePath(path, s.fileName); |
1170 | 0 | s.lineNumber = child->IntAttribute("lineNumber", Suppressions::Suppression::NO_LINE); |
1171 | 0 | s.symbolName = readSafe(child->Attribute("symbolName"), ""); |
1172 | 0 | s.hash = strToInt<std::size_t>(readSafe(child->Attribute("hash"), "0")); |
1173 | 0 | suppressions.push_back(std::move(s)); |
1174 | 0 | } |
1175 | 0 | } else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0) |
1176 | 0 | guiProject.checkVsConfigs = readXmlStringList(node, emptyString, CppcheckXml::VSConfigurationName, nullptr); |
1177 | 0 | else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0) |
1178 | 0 | guiProject.platform = readSafe(node->GetText(), ""); |
1179 | 0 | else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0) |
1180 | 0 | guiProject.analyzeAllVsConfigs = readSafe(node->GetText(), ""); |
1181 | 0 | else if (strcmp(node->Name(), CppcheckXml::Parser) == 0) |
1182 | 0 | temp.clang = true; |
1183 | 0 | else if (strcmp(node->Name(), CppcheckXml::AddonsElementName) == 0) { |
1184 | 0 | const auto& addons = readXmlStringList(node, emptyString, CppcheckXml::AddonElementName, nullptr); |
1185 | 0 | temp.addons.insert(addons.cbegin(), addons.cend()); |
1186 | 0 | } |
1187 | 0 | else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0) |
1188 | 0 | node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning |
1189 | 0 | else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) { |
1190 | 0 | const std::list<std::string> toolList = readXmlStringList(node, emptyString, CppcheckXml::ToolElementName, nullptr); |
1191 | 0 | for (const std::string &toolName : toolList) { |
1192 | 0 | if (toolName == CppcheckXml::ClangTidy) |
1193 | 0 | temp.clangTidy = true; |
1194 | 0 | } |
1195 | 0 | } else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) |
1196 | 0 | temp.checkHeaders = (strcmp(readSafe(node->GetText(), ""), "true") == 0); |
1197 | 0 | else if (strcmp(node->Name(), CppcheckXml::CheckLevelExhaustiveElementName) == 0) |
1198 | 0 | checkLevelExhaustive = true; |
1199 | 0 | else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0) |
1200 | 0 | temp.checkUnusedTemplates = (strcmp(readSafe(node->GetText(), ""), "true") == 0); |
1201 | 0 | else if (strcmp(node->Name(), CppcheckXml::MaxCtuDepthElementName) == 0) |
1202 | 0 | temp.maxCtuDepth = strToInt<int>(readSafe(node->GetText(), "2")); // TODO: bail out when missing? |
1203 | 0 | else if (strcmp(node->Name(), CppcheckXml::MaxTemplateRecursionElementName) == 0) |
1204 | 0 | temp.maxTemplateRecursion = strToInt<int>(readSafe(node->GetText(), "100")); // TODO: bail out when missing? |
1205 | 0 | else if (strcmp(node->Name(), CppcheckXml::CheckUnknownFunctionReturn) == 0) |
1206 | 0 | ; // TODO |
1207 | 0 | else if (strcmp(node->Name(), Settings::SafeChecks::XmlRootName) == 0) { |
1208 | 0 | for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { |
1209 | 0 | if (strcmp(child->Name(), Settings::SafeChecks::XmlClasses) == 0) |
1210 | 0 | temp.safeChecks.classes = true; |
1211 | 0 | else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalFunctions) == 0) |
1212 | 0 | temp.safeChecks.externalFunctions = true; |
1213 | 0 | else if (strcmp(child->Name(), Settings::SafeChecks::XmlInternalFunctions) == 0) |
1214 | 0 | temp.safeChecks.internalFunctions = true; |
1215 | 0 | else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalVariables) == 0) |
1216 | 0 | temp.safeChecks.externalVariables = true; |
1217 | 0 | else { |
1218 | 0 | printError("Unknown '" + std::string(Settings::SafeChecks::XmlRootName) + "' element '" + std::string(child->Name()) + "' in Cppcheck project file"); |
1219 | 0 | return false; |
1220 | 0 | } |
1221 | 0 | } |
1222 | 0 | } else if (strcmp(node->Name(), CppcheckXml::TagWarningsElementName) == 0) |
1223 | 0 | ; // TODO |
1224 | | // Cppcheck Premium features |
1225 | 0 | else if (strcmp(node->Name(), CppcheckXml::BughuntingElementName) == 0) |
1226 | 0 | temp.premiumArgs += " --bughunting"; |
1227 | 0 | else if (strcmp(node->Name(), CppcheckXml::CertIntPrecisionElementName) == 0) |
1228 | 0 | temp.premiumArgs += std::string(" --cert-c-int-precision=") + readSafe(node->GetText(), "0"); |
1229 | 0 | else if (strcmp(node->Name(), CppcheckXml::CodingStandardsElementName) == 0) { |
1230 | 0 | for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { |
1231 | 0 | if (strcmp(child->Name(), CppcheckXml::CodingStandardElementName) == 0 && child->GetText()) |
1232 | 0 | temp.premiumArgs += std::string(" --") + child->GetText(); |
1233 | 0 | } |
1234 | 0 | } |
1235 | 0 | else if (strcmp(node->Name(), CppcheckXml::ProjectNameElementName) == 0) |
1236 | 0 | ; // no-op |
1237 | 0 | else { |
1238 | 0 | printError("Unknown element '" + std::string(node->Name()) + "' in Cppcheck project file"); |
1239 | 0 | return false; |
1240 | 0 | } |
1241 | 0 | } |
1242 | 0 | settings->basePaths = temp.basePaths; |
1243 | 0 | settings->relativePaths |= temp.relativePaths; |
1244 | 0 | settings->buildDir = temp.buildDir; |
1245 | 0 | settings->includePaths = temp.includePaths; |
1246 | 0 | settings->userDefines = temp.userDefines; |
1247 | 0 | settings->userUndefs = temp.userUndefs; |
1248 | 0 | settings->addons = temp.addons; |
1249 | 0 | settings->clang = temp.clang; |
1250 | 0 | settings->clangTidy = temp.clangTidy; |
1251 | |
|
1252 | 0 | if (!settings->premiumArgs.empty()) |
1253 | 0 | settings->premiumArgs += temp.premiumArgs; |
1254 | 0 | else if (!temp.premiumArgs.empty()) |
1255 | 0 | settings->premiumArgs = temp.premiumArgs.substr(1); |
1256 | |
|
1257 | 0 | for (const std::string &p : paths) |
1258 | 0 | guiProject.pathNames.push_back(p); |
1259 | 0 | settings->nomsg.addSuppressions(std::move(suppressions)); |
1260 | 0 | settings->checkHeaders = temp.checkHeaders; |
1261 | 0 | settings->checkUnusedTemplates = temp.checkUnusedTemplates; |
1262 | 0 | settings->maxCtuDepth = temp.maxCtuDepth; |
1263 | 0 | settings->maxTemplateRecursion = temp.maxTemplateRecursion; |
1264 | 0 | settings->safeChecks = temp.safeChecks; |
1265 | |
|
1266 | 0 | if (checkLevelExhaustive) |
1267 | 0 | settings->setCheckLevelExhaustive(); |
1268 | 0 | else |
1269 | 0 | settings->setCheckLevelNormal(); |
1270 | |
|
1271 | 0 | return true; |
1272 | 0 | } |
1273 | | |
1274 | | void ImportProject::selectOneVsConfig(cppcheck::Platform::Type platform) |
1275 | 0 | { |
1276 | 0 | std::set<std::string> filenames; |
1277 | 0 | for (std::list<ImportProject::FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) { |
1278 | 0 | if (it->cfg.empty()) { |
1279 | 0 | ++it; |
1280 | 0 | continue; |
1281 | 0 | } |
1282 | 0 | const ImportProject::FileSettings &fs = *it; |
1283 | 0 | bool remove = false; |
1284 | 0 | if (!startsWith(fs.cfg,"Debug")) |
1285 | 0 | remove = true; |
1286 | 0 | if (platform == cppcheck::Platform::Type::Win64 && fs.platformType != platform) |
1287 | 0 | remove = true; |
1288 | 0 | else if ((platform == cppcheck::Platform::Type::Win32A || platform == cppcheck::Platform::Type::Win32W) && fs.platformType == cppcheck::Platform::Type::Win64) |
1289 | 0 | remove = true; |
1290 | 0 | else if (filenames.find(fs.filename) != filenames.end()) |
1291 | 0 | remove = true; |
1292 | 0 | if (remove) { |
1293 | 0 | it = fileSettings.erase(it); |
1294 | 0 | } else { |
1295 | 0 | filenames.insert(fs.filename); |
1296 | 0 | ++it; |
1297 | 0 | } |
1298 | 0 | } |
1299 | 0 | } |
1300 | | |
1301 | | void ImportProject::selectVsConfigurations(cppcheck::Platform::Type platform, const std::vector<std::string> &configurations) |
1302 | 0 | { |
1303 | 0 | for (std::list<ImportProject::FileSettings>::iterator it = fileSettings.begin(); it != fileSettings.end();) { |
1304 | 0 | if (it->cfg.empty()) { |
1305 | 0 | ++it; |
1306 | 0 | continue; |
1307 | 0 | } |
1308 | 0 | const ImportProject::FileSettings &fs = *it; |
1309 | 0 | const auto config = fs.cfg.substr(0, fs.cfg.find('|')); |
1310 | 0 | bool remove = false; |
1311 | 0 | if (std::find(configurations.begin(), configurations.end(), config) == configurations.end()) |
1312 | 0 | remove = true; |
1313 | 0 | if (platform == cppcheck::Platform::Type::Win64 && fs.platformType != platform) |
1314 | 0 | remove = true; |
1315 | 0 | else if ((platform == cppcheck::Platform::Type::Win32A || platform == cppcheck::Platform::Type::Win32W) && fs.platformType == cppcheck::Platform::Type::Win64) |
1316 | 0 | remove = true; |
1317 | 0 | if (remove) { |
1318 | 0 | it = fileSettings.erase(it); |
1319 | 0 | } else { |
1320 | 0 | ++it; |
1321 | 0 | } |
1322 | 0 | } |
1323 | 0 | } |
1324 | | |
1325 | | std::list<std::string> ImportProject::getVSConfigs() |
1326 | 0 | { |
1327 | 0 | return std::list<std::string>(mAllVSConfigs.cbegin(), mAllVSConfigs.cend()); |
1328 | 0 | } |
1329 | | |
1330 | | void ImportProject::setRelativePaths(const std::string &filename) |
1331 | 0 | { |
1332 | 0 | if (Path::isAbsolute(filename)) |
1333 | 0 | return; |
1334 | 0 | const std::vector<std::string> basePaths{Path::fromNativeSeparators(Path::getCurrentPath())}; |
1335 | 0 | for (auto &fs: fileSettings) { |
1336 | 0 | fs.filename = Path::getRelativePath(fs.filename, basePaths); |
1337 | 0 | for (auto &includePath: fs.includePaths) |
1338 | 0 | includePath = Path::getRelativePath(includePath, basePaths); |
1339 | 0 | } |
1340 | 0 | } |
1341 | | |
1342 | | void ImportProject::printError(const std::string &message) |
1343 | 0 | { |
1344 | 0 | std::cout << "cppcheck: error: " << message << std::endl; |
1345 | 0 | } |
1346 | | |
1347 | | bool ImportProject::sourceFileExists(const std::string &file) |
1348 | 0 | { |
1349 | 0 | return Path::isFile(file); |
1350 | 0 | } |