Skip to content

Commit d32c01d

Browse files
authored
Fix #14880 add support for Folders in slnx project file and skip non C++ projects (#8681)
1 parent bc9349a commit d32c01d

10 files changed

Lines changed: 399 additions & 11 deletions

File tree

lib/importproject.cpp

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -529,27 +529,51 @@ bool ImportProject::importSlnx(const std::string& filename, const std::vector<st
529529
return false;
530530
}
531531

532+
if (std::strcmp(rootnode->Name(), "Solution") != 0) {
533+
errors.emplace_back("Invalid Visual Studio solution file format");
534+
return false;
535+
}
536+
532537
std::map<std::string, std::string, cppcheck::stricmp> variables;
533538
variables["SolutionDir"] = Path::simplifyPath(Path::getPathFromFilename(filename));
534539

535540
bool found = false;
536541
std::vector<SharedItemsProject> sharedItemsProjects;
537542

543+
auto processProject = [&](const tinyxml2::XMLElement* projectNode) {
544+
const char* pathAttribute = projectNode->Attribute("Path");
545+
if (pathAttribute == nullptr)
546+
return true;
547+
548+
std::string vcxproj(pathAttribute);
549+
vcxproj = Path::toNativeSeparators(std::move(vcxproj));
550+
551+
if (Path::getFilenameExtensionInLowerCase(vcxproj) != ".vcxproj")
552+
return true; // skip other project types
553+
554+
if (!Path::isAbsolute(vcxproj))
555+
vcxproj = variables["SolutionDir"] + vcxproj;
556+
557+
vcxproj = Path::fromNativeSeparators(std::move(vcxproj));
558+
if (!importVcxproj(vcxproj, variables, "", fileFilters, sharedItemsProjects)) {
559+
errors.emplace_back("failed to load '" + vcxproj + "' from Visual Studio solution");
560+
return false;
561+
}
562+
found = true;
563+
return true;
564+
};
565+
538566
for (const tinyxml2::XMLElement* node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) {
539567
const char* name = node->Name();
540568
if (std::strcmp(name, "Project") == 0) {
541-
const char* labelAttribute = node->Attribute("Path");
542-
if (labelAttribute) {
543-
std::string vcxproj(labelAttribute);
544-
vcxproj = Path::toNativeSeparators(std::move(vcxproj));
545-
if (!Path::isAbsolute(vcxproj))
546-
vcxproj = variables["SolutionDir"] + vcxproj;
547-
vcxproj = Path::fromNativeSeparators(std::move(vcxproj));
548-
if (!importVcxproj(vcxproj, variables, "", fileFilters, sharedItemsProjects)) {
549-
errors.emplace_back("failed to load '" + vcxproj + "' from Visual Studio solution");
550-
return false;
569+
if (!processProject(node))
570+
return false;
571+
} else if (std::strcmp(name, "Folder") == 0) {
572+
for (const tinyxml2::XMLElement* childNode = node->FirstChildElement(); childNode; childNode = childNode->NextSiblingElement()) {
573+
if (std::strcmp(childNode->Name(), "Project") == 0) {
574+
if (!processProject(childNode))
575+
return false;
551576
}
552-
found = true;
553577
}
554578
}
555579
}

test/cli/project_test.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@ def test_slnx_no_xml_root(tmpdir):
147147
__test_project_error(tmpdir, "slnx", content, expected)
148148

149149

150+
def test_slnx_invalid_xml_root(tmpdir):
151+
content = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
152+
"<Invalid>\r\n" \
153+
"</Invalid>\r\n"
154+
155+
expected = "Invalid Visual Studio solution file format"
156+
157+
__test_project_error(tmpdir, "slnx", content, expected)
158+
159+
150160
def test_slnx_no_projects(tmpdir):
151161
content = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
152162
"<Solution>\r\n" \
@@ -161,6 +171,22 @@ def test_slnx_no_projects(tmpdir):
161171
__test_project_error(tmpdir, "slnx", content, expected)
162172

163173

174+
def test_slnx_no_projects_in_folder(tmpdir):
175+
content = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
176+
"<Solution>\r\n" \
177+
" <Configurations>\r\n" \
178+
' <Platform Name="x64" />\r\n' \
179+
' <Platform Name="x86" />\r\n' \
180+
" </Configurations>\r\n" \
181+
' <Folder Name="/common/">\r\n' \
182+
' </Folder>\r\n' \
183+
"</Solution>\r\n"
184+
185+
expected = "no projects found in Visual Studio solution file"
186+
187+
__test_project_error(tmpdir, "slnx", content, expected)
188+
189+
164190
def test_slnx_project_file_not_found(tmpdir):
165191
content = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
166192
"<Solution>\r\n" \
@@ -179,6 +205,26 @@ def test_slnx_project_file_not_found(tmpdir):
179205
__test_project_error(tmpdir, "slnx", content, expected)
180206

181207

208+
def test_slnx_project_file_in_folder_not_found(tmpdir):
209+
content = '<?xml version="1.0" encoding="UTF-8"?>\r\n' \
210+
"<Solution>\r\n" \
211+
" <Configurations>\r\n" \
212+
' <Platform Name="x64" />\r\n' \
213+
' <Platform Name="x86" />\r\n' \
214+
" </Configurations>\r\n" \
215+
' <Folder Name="/common/">\r\n' \
216+
' <Project Path="common/test.vcxproj" />\r\n' \
217+
' </Folder>\r\n' \
218+
"</Solution>\r\n"
219+
220+
expected = "Visual Studio project file is not a valid XML - XML_ERROR_FILE_NOT_FOUND\n" \
221+
"cppcheck: error: failed to load '{}' from Visual Studio solution".format(os.path.join(tmpdir, "common/test.vcxproj"))
222+
if sys.platform == "win32":
223+
expected = expected.replace('\\', '/')
224+
225+
__test_project_error(tmpdir, "slnx", content, expected)
226+
227+
182228
def test_vcxproj_no_xml_root(tmpdir):
183229
content = '<?xml version="1.0" encoding="utf-8"?>'
184230

test/cli/slnx-folders/app/app.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "../lib/lib.h"
2+
3+
int main(int argc, char *argv[])
4+
{
5+
int x = 3 / 0; (void)x; // ERROR
6+
return foo();
7+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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>{0de77c38-881a-4f9f-bdfa-2c429968985e}</ProjectGuid>
17+
<RootNamespace>unused</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>Application</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+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
47+
<OutDir>..\x64\Debug\</OutDir>
48+
<IntDir>x64\Debug\</IntDir>
49+
</PropertyGroup>
50+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
51+
<OutDir>..\x64\Release\</OutDir>
52+
<IntDir>x64\Release\</IntDir>
53+
</PropertyGroup>
54+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
55+
<ClCompile>
56+
<WarningLevel>Level3</WarningLevel>
57+
<SDLCheck>true</SDLCheck>
58+
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
59+
<ConformanceMode>true</ConformanceMode>
60+
<LanguageStandard>stdcpp20</LanguageStandard>
61+
<AdditionalIncludeDirectories>
62+
</AdditionalIncludeDirectories>
63+
</ClCompile>
64+
<Link>
65+
<SubSystem>Console</SubSystem>
66+
<GenerateDebugInformation>true</GenerateDebugInformation>
67+
<AdditionalLibraryDirectories>../lib</AdditionalLibraryDirectories>
68+
</Link>
69+
<Manifest>
70+
<EnableSegmentHeap>true</EnableSegmentHeap>
71+
</Manifest>
72+
</ItemDefinitionGroup>
73+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
74+
<ClCompile>
75+
<WarningLevel>Level3</WarningLevel>
76+
<FunctionLevelLinking>true</FunctionLevelLinking>
77+
<IntrinsicFunctions>true</IntrinsicFunctions>
78+
<SDLCheck>true</SDLCheck>
79+
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
80+
<ConformanceMode>true</ConformanceMode>
81+
<LanguageStandard>stdcpp20</LanguageStandard>
82+
</ClCompile>
83+
<Link>
84+
<SubSystem>Console</SubSystem>
85+
<GenerateDebugInformation>true</GenerateDebugInformation>
86+
</Link>
87+
<Manifest>
88+
<EnableSegmentHeap>true</EnableSegmentHeap>
89+
</Manifest>
90+
</ItemDefinitionGroup>
91+
<ItemGroup>
92+
<ClCompile Include="app.cpp" />
93+
</ItemGroup>
94+
<ItemGroup>
95+
<ProjectReference Include="..\lib\lib.vcxproj">
96+
<Project>{93BE1430-BE74-35DF-1882-AF70E49C9898}</Project>
97+
</ProjectReference>
98+
</ItemGroup>
99+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
100+
<ImportGroup Label="ExtensionTargets">
101+
</ImportGroup>
102+
</Project>

test/cli/slnx-folders/lib/lib.cpp

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

test/cli/slnx-folders/lib/lib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extern int foo();
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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>{93BE1430-BE74-35DF-1882-AF70E49C9898}</ProjectGuid>
17+
<RootNamespace>unused</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>StaticLibrary</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+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
47+
<OutDir>..\x64\Debug\</OutDir>
48+
<IntDir>x64\Debug\</IntDir>
49+
</PropertyGroup>
50+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
51+
<OutDir>..\x64\Release\</OutDir>
52+
<IntDir>x64\Release\</IntDir>
53+
</PropertyGroup>
54+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
55+
<ClCompile>
56+
<WarningLevel>Level3</WarningLevel>
57+
<SDLCheck>true</SDLCheck>
58+
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
59+
<ConformanceMode>true</ConformanceMode>
60+
<LanguageStandard>stdcpp20</LanguageStandard>
61+
<AdditionalIncludeDirectories>
62+
</AdditionalIncludeDirectories>
63+
</ClCompile>
64+
<Link>
65+
<SubSystem>Console</SubSystem>
66+
<GenerateDebugInformation>true</GenerateDebugInformation>
67+
<AdditionalLibraryDirectories>../lib</AdditionalLibraryDirectories>
68+
</Link>
69+
<Manifest>
70+
<EnableSegmentHeap>true</EnableSegmentHeap>
71+
</Manifest>
72+
</ItemDefinitionGroup>
73+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
74+
<ClCompile>
75+
<WarningLevel>Level3</WarningLevel>
76+
<FunctionLevelLinking>true</FunctionLevelLinking>
77+
<IntrinsicFunctions>true</IntrinsicFunctions>
78+
<SDLCheck>true</SDLCheck>
79+
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
80+
<ConformanceMode>true</ConformanceMode>
81+
<LanguageStandard>stdcpp20</LanguageStandard>
82+
</ClCompile>
83+
<Link>
84+
<SubSystem>Console</SubSystem>
85+
<GenerateDebugInformation>true</GenerateDebugInformation>
86+
</Link>
87+
<Manifest>
88+
<EnableSegmentHeap>true</EnableSegmentHeap>
89+
</Manifest>
90+
</ItemDefinitionGroup>
91+
<ItemGroup>
92+
<ClCompile Include="lib.cpp" />
93+
</ItemGroup>
94+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
95+
<ImportGroup Label="ExtensionTargets">
96+
</ImportGroup>
97+
</Project>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project version="1">
3+
<builddir>slnx-folders-cppcheck-build-dir</builddir>
4+
<importproject>slnx-folders.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+
<config>Release</config>
14+
</vs-configurations>
15+
<check-level-normal/>
16+
<project-name>slnx-folders</project-name>
17+
</project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Solution>
2+
<Configurations>
3+
<Platform Name="x64" />
4+
</Configurations>
5+
<Folder Name="/Lib/">
6+
<File Path="lib/lib.h" />
7+
<Project Path="lib\lib.vcxproj" />
8+
</Folder>
9+
<Folder Name="/App/">
10+
<Project Path="app\app.vcxproj" Id="0de77c38-881a-4f9f-bdfa-2c429968985e" />
11+
</Folder>
12+
</Solution>

0 commit comments

Comments
 (0)