diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 0e6ca353480..13a84f6fa02 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -816,6 +817,32 @@ namespace { bool useOfMfc = false; bool useUnicode = false; }; + + struct ItemGroupClCompile { + explicit ItemGroupClCompile(std::string file) : filename(std::move(file)) {} + ItemGroupClCompile(const tinyxml2::XMLElement* element, std::string file) : filename(std::move(file)) { + for (const tinyxml2::XMLElement* childElement = element->FirstChildElement(); childElement; childElement = childElement->NextSiblingElement()) { + const char* name = childElement->Name(); + if (!name) + continue; + if (std::strcmp(name, "ExcludedFromBuild") == 0) { + const char* condition = childElement->Attribute("Condition"); + const char* text = childElement->GetText(); + if (!condition || !text) + continue; + const std::string cond(condition); + // Check if the condition is of the form: '$(Configuration)|$(Platform)' == 'Debug|x64' + const std::regex rgx("==\\s*'([^']+)'"); + std::smatch match; + if (std::regex_search(cond, match, rgx) && match.size() > 1 && std::strcmp(text, "true") == 0) + exclude.insert(match[1]); + } + // TODO: ForcedIncludeFiles and PrecompiledHeaderFile + } + } + std::string filename; + std::set exclude; + }; } static std::list toStringList(const std::string &s) @@ -923,7 +950,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename)); std::list projectConfigurationList; - std::list compileList; + std::list compileList; std::list itemDefinitionGroupList; std::vector configurationPropertyGroups; std::string includePath; @@ -954,7 +981,8 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X const char *include = e->Attribute("Include"); if (include && Path::acceptFile(include)) { std::string toInclude = Path::simplifyPath(Path::isAbsolute(include) ? include : Path::getPathFromFilename(filename) + include); - compileList.emplace_back(toInclude); + findAndReplace(toInclude, "$(MSBuildThisFileDirectory)", "./"); + compileList.emplace_back(e, toInclude); } } } @@ -1016,7 +1044,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X for (const auto& sharedProject : sharedItemsProjects) { for (const auto &file : sharedProject.sourceFiles) { std::string pathToFile = Path::simplifyPath(Path::getPathFromFilename(sharedProject.pathToProjectFile) + file); - compileList.emplace_back(std::move(pathToFile)); + compileList.emplace_back(pathToFile); } for (const auto &p : sharedProject.includePaths) { std::string path = Path::simplifyPath(Path::getPathFromFilename(sharedProject.pathToProjectFile) + p); @@ -1026,8 +1054,8 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X // Project files PathMatch filtermatcher(fileFilters, Path::getCurrentPath()); - for (const std::string &cfilename : compileList) { - if (!fileFilters.empty() && !filtermatcher.match(cfilename)) + for (const ItemGroupClCompile &compile : compileList) { + if (!fileFilters.empty() && !filtermatcher.match(compile.filename)) continue; for (const ProjectConfiguration &p : projectConfigurationList) { @@ -1040,7 +1068,11 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X continue; } - FileSettings fs{cfilename, Standards::Language::None, 0}; // file will be identified later on + // check if the file should be excluded for this configuration + if (!compile.exclude.empty() && compile.exclude.find(p.name) != compile.exclude.end()) + continue; + + FileSettings fs{ compile.filename, Standards::Language::None, 0}; // file will be identified later on fs.cfg = p.name; // TODO: detect actual MSC version fs.msc = true; @@ -1053,7 +1085,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X } std::string additionalIncludePaths; for (const ItemDefinitionGroup &i : itemDefinitionGroupList) { - if (!i.conditionIsTrue(p, cfilename, errors)) + if (!i.conditionIsTrue(p, compile.filename, errors)) continue; fs.standard = Standards::getCPP(i.cppstd); fs.defines += ';' + i.preprocessorDefinitions; @@ -1071,7 +1103,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X } bool useUnicode = false; for (const ConfigurationPropertyGroup &c : configurationPropertyGroups) { - if (!c.conditionIsTrue(p, cfilename, errors)) + if (!c.conditionIsTrue(p, compile.filename, errors)) continue; // in msbuild the last definition wins useUnicode = c.useUnicode; @@ -1081,10 +1113,11 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X fs.defines += ";UNICODE=1;_UNICODE=1"; } fsSetDefines(fs, fs.defines); - fsSetIncludePaths(fs, Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables); + fsSetIncludePaths(fs, Path::getPathFromFilename(compile.filename), toStringList(includePath + ';' + additionalIncludePaths), variables); for (const auto &path : sharedItemsIncludePaths) { fs.includePaths.emplace_back(path); } + fileSettings.push_back(std::move(fs)); } } diff --git a/test/cli/exclude/DebugX64.cpp b/test/cli/exclude/DebugX64.cpp new file mode 100644 index 00000000000..cfb1fce687a --- /dev/null +++ b/test/cli/exclude/DebugX64.cpp @@ -0,0 +1,8 @@ +#include + +int foo() +{ + std::cout << "DebugX64\n"; + int x = 3 / 0; (void)x; // ERROR + return 0; +} diff --git a/test/cli/exclude/ReleaseX64.cpp b/test/cli/exclude/ReleaseX64.cpp new file mode 100644 index 00000000000..8fa6e6d0f82 --- /dev/null +++ b/test/cli/exclude/ReleaseX64.cpp @@ -0,0 +1,8 @@ +#include + +int foo() +{ + std::cout << "ReleaseX64\n"; + int x = 3 / 0; (void)x; // ERROR + return 0; +} diff --git a/test/cli/exclude/exclude.cppcheck b/test/cli/exclude/exclude.cppcheck new file mode 100644 index 00000000000..50d7da84486 --- /dev/null +++ b/test/cli/exclude/exclude.cppcheck @@ -0,0 +1,16 @@ + + + exclude-cppcheck-build-dir + exclude.slnx + false + true + true + true + 2 + 100 + + Debug + + + exclude + diff --git a/test/cli/exclude/exclude.slnx b/test/cli/exclude/exclude.slnx new file mode 100644 index 00000000000..837662784ac --- /dev/null +++ b/test/cli/exclude/exclude.slnx @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/cli/exclude/exclude.vcxproj b/test/cli/exclude/exclude.vcxproj new file mode 100644 index 00000000000..cc326aaec2f --- /dev/null +++ b/test/cli/exclude/exclude.vcxproj @@ -0,0 +1,94 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 18.0 + Win32Proj + {c9d1dca1-d8ff-4c05-9159-f00816645319} + exclude + 10.0 + + + + StaticLibrary + true + v145 + Unicode + + + Application + false + v145 + true + Unicode + + + + + + + + + + + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + + + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + + + true + + + + + true + + + true + + + + + + + + + \ No newline at end of file diff --git a/test/cli/exclude/foo.h b/test/cli/exclude/foo.h new file mode 100644 index 00000000000..5d5f8f0c9e7 --- /dev/null +++ b/test/cli/exclude/foo.h @@ -0,0 +1 @@ +int foo(); diff --git a/test/cli/exclude_test.py b/test/cli/exclude_test.py new file mode 100644 index 00000000000..4f674aac34e --- /dev/null +++ b/test/cli/exclude_test.py @@ -0,0 +1,20 @@ + +# python -m pytest exclude_test.py + +import os + +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) +__proj_dir = os.path.join(__script_dir, 'exclude') + +def test_exclude(): + args = [ + '--template=cppcheck1', + '--project=exclude/exclude.cppcheck', + '--no-cppcheck-build-dir' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + filename = os.path.join('exclude', 'DebugX64.cpp') + assert ret == 0, stdout + assert stderr == '[%s:6]: (error) Division by zero.\n' % filename