Skip to content

Commit 9408a1d

Browse files
authored
add support for vcxproj ExcludedFromBuild (#8771)
CMake generates a vcxproj file for the cppcheck GUI that has generated files for all configurations but adds ExcludedFromBuild so the files only gets compiled for a specific configuration. ``` <ClCompile Include="C:\Users\Bob\forks\cppcheck\build\gui\cppcheck-gui_autogen\mocs_compilation_Debug.cpp"> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|x64'">true</ExcludedFromBuild> </ClCompile> ``` This patch adds support for ExcludedFromBuild so a file only gets checked for a specific configuration.
1 parent 569f3d6 commit 9408a1d

8 files changed

Lines changed: 206 additions & 16 deletions

File tree

lib/importproject.cpp

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -617,12 +617,13 @@ namespace {
617617
std::string platformStr;
618618
};
619619

620-
struct ConditionalGroup {
621-
explicit ConditionalGroup(const tinyxml2::XMLElement *idg){
620+
struct Conditional {
621+
explicit Conditional(const tinyxml2::XMLElement *idg){
622622
const char *condAttr = idg->Attribute("Condition");
623623
if (condAttr)
624624
mCondition = condAttr;
625625
}
626+
explicit Conditional(std::string condition) : mCondition(std::move(condition)) {}
626627

627628
static void replaceAll(std::string &c, const std::string &from, const std::string &to) {
628629
std::string::size_type pos;
@@ -751,8 +752,8 @@ namespace {
751752
std::string mCondition;
752753
};
753754

754-
struct ItemDefinitionGroup : ConditionalGroup {
755-
explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : ConditionalGroup(idg), additionalIncludePaths(std::move(includePaths)) {
755+
struct ItemDefinitionGroup : Conditional {
756+
explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : Conditional(idg), additionalIncludePaths(std::move(includePaths)) {
756757
for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) {
757758
const char* name = e1->Name();
758759
if (std::strcmp(name, "ClCompile") == 0) {
@@ -802,8 +803,8 @@ namespace {
802803
Standards::cppstd_t cppstd = Standards::CPPLatest;
803804
};
804805

805-
struct ConfigurationPropertyGroup : ConditionalGroup {
806-
explicit ConfigurationPropertyGroup(const tinyxml2::XMLElement *idg) : ConditionalGroup(idg) {
806+
struct ConfigurationPropertyGroup : Conditional {
807+
explicit ConfigurationPropertyGroup(const tinyxml2::XMLElement *idg) : Conditional(idg) {
807808
for (const tinyxml2::XMLElement *e = idg->FirstChildElement(); e; e = e->NextSiblingElement()) {
808809
if (std::strcmp(e->Name(), "UseOfMfc") == 0) {
809810
useOfMfc = true;
@@ -816,6 +817,37 @@ namespace {
816817
bool useOfMfc = false;
817818
bool useUnicode = false;
818819
};
820+
821+
struct ItemGroupClCompile {
822+
explicit ItemGroupClCompile(std::string filename) : mFilename(std::move(filename)) {}
823+
ItemGroupClCompile(const tinyxml2::XMLElement *element, std::string file) : mFilename(std::move(file)) {
824+
for (const tinyxml2::XMLElement* childElement = element->FirstChildElement(); childElement; childElement = childElement->NextSiblingElement()) {
825+
const char *name = childElement->Name();
826+
if (!name)
827+
continue;
828+
if (std::strcmp(name, "ExcludedFromBuild") == 0) {
829+
const char *condition = childElement->Attribute("Condition");
830+
const char *text = childElement->GetText();
831+
if (!condition || !text || std::strcmp(text, "true") != 0)
832+
continue;
833+
mConditions.emplace_back(condition);
834+
}
835+
// TODO: ForcedIncludeFiles and PrecompiledHeaderFile
836+
}
837+
}
838+
bool exclude(const ProjectConfiguration& p, std::vector<std::string>& errors) const {
839+
if (mConditions.empty())
840+
return false;
841+
for (const std::string& condition : mConditions) {
842+
Conditional conditional(condition);
843+
if (conditional.conditionIsTrue(p, mFilename, errors))
844+
return true;
845+
}
846+
return false;
847+
}
848+
std::string mFilename;
849+
std::list<std::string> mConditions;
850+
};
819851
}
820852

821853
static std::list<std::string> toStringList(const std::string &s)
@@ -923,7 +955,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
923955
variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename));
924956

925957
std::list<ProjectConfiguration> projectConfigurationList;
926-
std::list<std::string> compileList;
958+
std::list<ItemGroupClCompile> compileList;
927959
std::list<ItemDefinitionGroup> itemDefinitionGroupList;
928960
std::vector<ConfigurationPropertyGroup> configurationPropertyGroups;
929961
std::string includePath;
@@ -954,7 +986,8 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
954986
const char *include = e->Attribute("Include");
955987
if (include && Path::acceptFile(include)) {
956988
std::string toInclude = Path::simplifyPath(Path::isAbsolute(include) ? include : Path::getPathFromFilename(filename) + include);
957-
compileList.emplace_back(toInclude);
989+
findAndReplace(toInclude, "$(MSBuildThisFileDirectory)", "./");
990+
compileList.emplace_back(e, toInclude);
958991
}
959992
}
960993
}
@@ -1016,7 +1049,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
10161049
for (const auto& sharedProject : sharedItemsProjects) {
10171050
for (const auto &file : sharedProject.sourceFiles) {
10181051
std::string pathToFile = Path::simplifyPath(Path::getPathFromFilename(sharedProject.pathToProjectFile) + file);
1019-
compileList.emplace_back(std::move(pathToFile));
1052+
compileList.emplace_back(pathToFile);
10201053
}
10211054
for (const auto &p : sharedProject.includePaths) {
10221055
std::string path = Path::simplifyPath(Path::getPathFromFilename(sharedProject.pathToProjectFile) + p);
@@ -1026,8 +1059,8 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
10261059

10271060
// Project files
10281061
PathMatch filtermatcher(fileFilters, Path::getCurrentPath());
1029-
for (const std::string &cfilename : compileList) {
1030-
if (!fileFilters.empty() && !filtermatcher.match(cfilename))
1062+
for (const ItemGroupClCompile& compile : compileList) {
1063+
if (!fileFilters.empty() && !filtermatcher.match(compile.mFilename))
10311064
continue;
10321065

10331066
for (const ProjectConfiguration &p : projectConfigurationList) {
@@ -1040,7 +1073,11 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
10401073
continue;
10411074
}
10421075

1043-
FileSettings fs{cfilename, Standards::Language::None, 0}; // file will be identified later on
1076+
// check if the file should be excluded for this configuration
1077+
if (compile.exclude(p, errors))
1078+
continue;
1079+
1080+
FileSettings fs{ compile.mFilename, Standards::Language::None, 0}; // file will be identified later on
10441081
fs.cfg = p.name;
10451082
// TODO: detect actual MSC version
10461083
fs.msc = true;
@@ -1053,7 +1090,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
10531090
}
10541091
std::string additionalIncludePaths;
10551092
for (const ItemDefinitionGroup &i : itemDefinitionGroupList) {
1056-
if (!i.conditionIsTrue(p, cfilename, errors))
1093+
if (!i.conditionIsTrue(p, compile.mFilename, errors))
10571094
continue;
10581095
fs.standard = Standards::getCPP(i.cppstd);
10591096
fs.defines += ';' + i.preprocessorDefinitions;
@@ -1071,7 +1108,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
10711108
}
10721109
bool useUnicode = false;
10731110
for (const ConfigurationPropertyGroup &c : configurationPropertyGroups) {
1074-
if (!c.conditionIsTrue(p, cfilename, errors))
1111+
if (!c.conditionIsTrue(p, compile.mFilename, errors))
10751112
continue;
10761113
// in msbuild the last definition wins
10771114
useUnicode = c.useUnicode;
@@ -1081,7 +1118,7 @@ bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::X
10811118
fs.defines += ";UNICODE=1;_UNICODE=1";
10821119
}
10831120
fsSetDefines(fs, fs.defines);
1084-
fsSetIncludePaths(fs, Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables);
1121+
fsSetIncludePaths(fs, Path::getPathFromFilename(compile.mFilename), toStringList(includePath + ';' + additionalIncludePaths), variables);
10851122
for (const auto &path : sharedItemsIncludePaths) {
10861123
fs.includePaths.emplace_back(path);
10871124
}
@@ -1754,5 +1791,5 @@ bool cppcheck::testing::evaluateVcxprojCondition(const std::string& condition, c
17541791
ProjectConfiguration p;
17551792
p.configuration = configuration;
17561793
p.platformStr = platform;
1757-
return ConditionalGroup::evalCondition(condition, p);
1794+
return Conditional::evalCondition(condition, p);
17581795
}

test/cli/exclude/DebugX64.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <iostream>
2+
3+
int foo()
4+
{
5+
std::cout << "DebugX64\n";
6+
int x = 3 / 0; (void)x; // ERROR
7+
return 0;
8+
}

test/cli/exclude/ReleaseX64.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <iostream>
2+
3+
int foo()
4+
{
5+
std::cout << "ReleaseX64\n";
6+
int x = 3 / 0; (void)x; // ERROR
7+
return 0;
8+
}

test/cli/exclude/exclude.cppcheck

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project version="1">
3+
<builddir>exclude-cppcheck-build-dir</builddir>
4+
<importproject>exclude.slnx</importproject>
5+
<analyze-all-vs-configs>false</analyze-all-vs-configs>
6+
<check-headers>true</check-headers>
7+
<check-unused-templates>true</check-unused-templates>
8+
<inline-suppression>true</inline-suppression>
9+
<max-ctu-depth>2</max-ctu-depth>
10+
<max-template-recursion>100</max-template-recursion>
11+
<vs-configurations>
12+
<config>Debug</config>
13+
</vs-configurations>
14+
<check-level-normal/>
15+
<project-name>exclude</project-name>
16+
</project>

test/cli/exclude/exclude.slnx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Solution>
2+
<Configurations>
3+
<Platform Name="x64" />
4+
</Configurations>
5+
<Project Path="exclude.vcxproj" Id="c9d1dca1-d8ff-4c05-9159-f00816645319" />
6+
</Solution>

test/cli/exclude/exclude.vcxproj

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup Label="ProjectConfigurations">
4+
<ProjectConfiguration Include="Debug|x64">
5+
<Configuration>Debug</Configuration>
6+
<Platform>x64</Platform>
7+
</ProjectConfiguration>
8+
<ProjectConfiguration Include="Release|x64">
9+
<Configuration>Release</Configuration>
10+
<Platform>x64</Platform>
11+
</ProjectConfiguration>
12+
</ItemGroup>
13+
<PropertyGroup Label="Globals">
14+
<VCProjectVersion>18.0</VCProjectVersion>
15+
<Keyword>Win32Proj</Keyword>
16+
<ProjectGuid>{c9d1dca1-d8ff-4c05-9159-f00816645319}</ProjectGuid>
17+
<RootNamespace>exclude</RootNamespace>
18+
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
19+
</PropertyGroup>
20+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
21+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
22+
<ConfigurationType>StaticLibrary</ConfigurationType>
23+
<UseDebugLibraries>true</UseDebugLibraries>
24+
<PlatformToolset>v145</PlatformToolset>
25+
<CharacterSet>Unicode</CharacterSet>
26+
</PropertyGroup>
27+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
28+
<ConfigurationType>Application</ConfigurationType>
29+
<UseDebugLibraries>false</UseDebugLibraries>
30+
<PlatformToolset>v145</PlatformToolset>
31+
<WholeProgramOptimization>true</WholeProgramOptimization>
32+
<CharacterSet>Unicode</CharacterSet>
33+
</PropertyGroup>
34+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
35+
<ImportGroup Label="ExtensionSettings">
36+
</ImportGroup>
37+
<ImportGroup Label="Shared">
38+
</ImportGroup>
39+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
40+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
41+
</ImportGroup>
42+
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
43+
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
44+
</ImportGroup>
45+
<PropertyGroup Label="UserMacros" />
46+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
47+
<ClCompile>
48+
<WarningLevel>Level3</WarningLevel>
49+
<SDLCheck>true</SDLCheck>
50+
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
51+
<ConformanceMode>true</ConformanceMode>
52+
<LanguageStandard>stdcpp20</LanguageStandard>
53+
</ClCompile>
54+
<Link>
55+
<SubSystem>Console</SubSystem>
56+
<GenerateDebugInformation>true</GenerateDebugInformation>
57+
</Link>
58+
<Manifest>
59+
<EnableSegmentHeap>true</EnableSegmentHeap>
60+
</Manifest>
61+
</ItemDefinitionGroup>
62+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
63+
<ClCompile>
64+
<WarningLevel>Level3</WarningLevel>
65+
<FunctionLevelLinking>true</FunctionLevelLinking>
66+
<IntrinsicFunctions>true</IntrinsicFunctions>
67+
<SDLCheck>true</SDLCheck>
68+
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
69+
<ConformanceMode>true</ConformanceMode>
70+
<LanguageStandard>stdcpp20</LanguageStandard>
71+
</ClCompile>
72+
<Link>
73+
<SubSystem>Console</SubSystem>
74+
<GenerateDebugInformation>true</GenerateDebugInformation>
75+
</Link>
76+
<Manifest>
77+
<EnableSegmentHeap>true</EnableSegmentHeap>
78+
</Manifest>
79+
</ItemDefinitionGroup>
80+
<ItemGroup>
81+
<ClCompile Include="DebugX64.cpp">
82+
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
83+
</ClCompile>
84+
<ClCompile Include="ReleaseX64.cpp">
85+
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
86+
</ClCompile>
87+
</ItemGroup>
88+
<ItemGroup>
89+
<ClInclude Include="foo.h" />
90+
</ItemGroup>
91+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
92+
<ImportGroup Label="ExtensionTargets">
93+
</ImportGroup>
94+
</Project>

test/cli/exclude/foo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int foo();

test/cli/exclude_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
# python -m pytest exclude_test.py
3+
4+
import os
5+
6+
from testutils import cppcheck
7+
8+
__script_dir = os.path.dirname(os.path.abspath(__file__))
9+
__proj_dir = os.path.join(__script_dir, 'exclude')
10+
11+
def test_exclude():
12+
args = [
13+
'--template=cppcheck1',
14+
'--project=exclude/exclude.cppcheck',
15+
'--no-cppcheck-build-dir'
16+
]
17+
ret, stdout, stderr = cppcheck(args, cwd=__script_dir)
18+
filename = os.path.join('exclude', 'DebugX64.cpp')
19+
assert ret == 0, stdout
20+
assert stderr == '[%s:6]: (error) Division by zero.\n' % filename

0 commit comments

Comments
 (0)