Skip to content

Commit 9a3e9e7

Browse files
committed
Respect Condition on PropertyGroup with Label="Configuration" and add tests
1 parent d619325 commit 9a3e9e7

3 files changed

Lines changed: 151 additions & 75 deletions

File tree

lib/importproject.cpp

Lines changed: 97 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <stack>
3737
#include <unordered_set>
3838
#include <utility>
39+
#include <vector>
3940

4041
#include "xml.h"
4142

@@ -527,51 +528,11 @@ namespace {
527528
std::string platformStr;
528529
};
529530

530-
struct ItemDefinitionGroup {
531-
explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : additionalIncludePaths(std::move(includePaths)) {
531+
struct ConditionalGroup {
532+
explicit ConditionalGroup(const tinyxml2::XMLElement *idg){
532533
const char *condAttr = idg->Attribute("Condition");
533534
if (condAttr)
534535
condition = condAttr;
535-
for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) {
536-
const char* name = e1->Name();
537-
if (std::strcmp(name, "ClCompile") == 0) {
538-
enhancedInstructionSet = "StreamingSIMDExtensions2";
539-
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
540-
const char * const text = e->GetText();
541-
if (!text)
542-
continue;
543-
const char * const ename = e->Name();
544-
if (std::strcmp(ename, "PreprocessorDefinitions") == 0)
545-
preprocessorDefinitions = text;
546-
else if (std::strcmp(ename, "AdditionalIncludeDirectories") == 0) {
547-
if (!additionalIncludePaths.empty())
548-
additionalIncludePaths += ';';
549-
additionalIncludePaths += text;
550-
} else if (std::strcmp(ename, "LanguageStandard") == 0) {
551-
if (std::strcmp(text, "stdcpp14") == 0)
552-
cppstd = Standards::CPP14;
553-
else if (std::strcmp(text, "stdcpp17") == 0)
554-
cppstd = Standards::CPP17;
555-
else if (std::strcmp(text, "stdcpp20") == 0)
556-
cppstd = Standards::CPP20;
557-
else if (std::strcmp(text, "stdcpplatest") == 0)
558-
cppstd = Standards::CPPLatest;
559-
} else if (std::strcmp(ename, "EnableEnhancedInstructionSet") == 0) {
560-
enhancedInstructionSet = text;
561-
}
562-
}
563-
}
564-
else if (std::strcmp(name, "Link") == 0) {
565-
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
566-
const char * const text = e->GetText();
567-
if (!text)
568-
continue;
569-
if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) {
570-
entryPointSymbol = text;
571-
}
572-
}
573-
}
574-
}
575536
}
576537

577538
static void replaceAll(std::string &c, const std::string &from, const std::string &to) {
@@ -623,13 +584,75 @@ namespace {
623584
}
624585
return false;
625586
}
587+
private:
626588
std::string condition;
589+
};
590+
591+
struct ItemDefinitionGroup : ConditionalGroup {
592+
explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : ConditionalGroup(idg), additionalIncludePaths(std::move(includePaths)) {
593+
for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) {
594+
const char* name = e1->Name();
595+
if (std::strcmp(name, "ClCompile") == 0) {
596+
enhancedInstructionSet = "StreamingSIMDExtensions2";
597+
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
598+
const char * const text = e->GetText();
599+
if (!text)
600+
continue;
601+
const char * const ename = e->Name();
602+
if (std::strcmp(ename, "PreprocessorDefinitions") == 0)
603+
preprocessorDefinitions = text;
604+
else if (std::strcmp(ename, "AdditionalIncludeDirectories") == 0) {
605+
if (!additionalIncludePaths.empty())
606+
additionalIncludePaths += ';';
607+
additionalIncludePaths += text;
608+
} else if (std::strcmp(ename, "LanguageStandard") == 0) {
609+
if (std::strcmp(text, "stdcpp14") == 0)
610+
cppstd = Standards::CPP14;
611+
else if (std::strcmp(text, "stdcpp17") == 0)
612+
cppstd = Standards::CPP17;
613+
else if (std::strcmp(text, "stdcpp20") == 0)
614+
cppstd = Standards::CPP20;
615+
else if (std::strcmp(text, "stdcpplatest") == 0)
616+
cppstd = Standards::CPPLatest;
617+
} else if (std::strcmp(ename, "EnableEnhancedInstructionSet") == 0) {
618+
enhancedInstructionSet = text;
619+
}
620+
}
621+
}
622+
else if (std::strcmp(name, "Link") == 0) {
623+
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
624+
const char * const text = e->GetText();
625+
if (!text)
626+
continue;
627+
if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) {
628+
entryPointSymbol = text;
629+
}
630+
}
631+
}
632+
}
633+
}
634+
627635
std::string enhancedInstructionSet;
628636
std::string preprocessorDefinitions;
629637
std::string additionalIncludePaths;
630638
std::string entryPointSymbol; // TODO: use this
631639
Standards::cppstd_t cppstd = Standards::CPPLatest;
632640
};
641+
642+
struct ConfigutrationPropertyGroup : ConditionalGroup {
643+
explicit ConfigutrationPropertyGroup(const tinyxml2::XMLElement *idg) : ConditionalGroup(idg) {
644+
for (const tinyxml2::XMLElement *e = idg->FirstChildElement(); e; e = e->NextSiblingElement()) {
645+
if (std::strcmp(e->Name(), "UseOfMfc") == 0) {
646+
useOfMfc = true;
647+
} else if (std::strcmp(e->Name(), "CharacterSet") == 0) {
648+
useUnicode = std::strcmp(e->GetText(), "Unicode") == 0;
649+
}
650+
}
651+
}
652+
653+
bool useOfMfc = false;
654+
bool useUnicode = false;
655+
};
633656
}
634657

635658
static std::list<std::string> toStringList(const std::string &s)
@@ -648,25 +671,8 @@ static std::list<std::string> toStringList(const std::string &s)
648671
return ret;
649672
}
650673

651-
static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map<std::string, std::string, cppcheck::stricmp> &variables, std::string &includePath, bool *useOfMfc, bool *useUnicode)
674+
static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map<std::string, std::string, cppcheck::stricmp> &variables, std::string &includePath)
652675
{
653-
if (useOfMfc) {
654-
for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) {
655-
if (std::strcmp(e->Name(), "UseOfMfc") == 0) {
656-
*useOfMfc = true;
657-
break;
658-
}
659-
}
660-
}
661-
if (useUnicode) {
662-
for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) {
663-
if (std::strcmp(e->Name(), "CharacterSet") == 0) {
664-
*useUnicode = std::strcmp(e->GetText(), "Unicode") == 0;
665-
break;
666-
}
667-
}
668-
}
669-
670676
const char* labelAttribute = node->Attribute("Label");
671677
if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) {
672678
for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) {
@@ -727,32 +733,39 @@ static void loadVisualStudioProperties(const std::string &props, std::map<std::s
727733
}
728734
}
729735
} else if (std::strcmp(name,"PropertyGroup")==0) {
730-
importPropertyGroup(node, variables, includePath, nullptr, nullptr);
736+
importPropertyGroup(node, variables, includePath);
731737
} else if (std::strcmp(name,"ItemDefinitionGroup")==0) {
732738
itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories);
733739
}
734740
}
735741
}
736742

737-
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, std::vector<SharedItemsProject> &cache)
743+
bool ImportProject::importVcxproj(const std::string &filename,
744+
std::map<std::string, std::string, cppcheck::stricmp> &variables,
745+
const std::string &additionalIncludeDirectories,
746+
const std::vector<std::string> &fileFilters,
747+
std::vector<SharedItemsProject> &cache)
748+
{
749+
tinyxml2::XMLDocument doc;
750+
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
751+
if (error != tinyxml2::XML_SUCCESS) {
752+
printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
753+
return false;
754+
}
755+
return importVcxproj(filename, doc, variables, additionalIncludeDirectories, fileFilters, cache);
756+
}
757+
758+
bool ImportProject::importVcxproj(const std::string &filename, const tinyxml2::XMLDocument &doc, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache)
738759
{
739760
variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename));
740761

741762
std::list<ProjectConfiguration> projectConfigurationList;
742763
std::list<std::string> compileList;
743764
std::list<ItemDefinitionGroup> itemDefinitionGroupList;
765+
std::vector<ConfigutrationPropertyGroup> configurationPropertyGroups;
744766
std::string includePath;
745767
std::vector<SharedItemsProject> sharedItemsProjects;
746768

747-
bool useOfMfc = false;
748-
bool useUnicode = false;
749-
750-
tinyxml2::XMLDocument doc;
751-
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
752-
if (error != tinyxml2::XML_SUCCESS) {
753-
printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
754-
return false;
755-
}
756769
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
757770
if (rootnode == nullptr) {
758771
printError("Visual Studio project file has no XML root node");
@@ -786,7 +799,12 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
786799
} else if (std::strcmp(name, "ItemDefinitionGroup") == 0) {
787800
itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories);
788801
} else if (std::strcmp(name, "PropertyGroup") == 0) {
789-
importPropertyGroup(node, variables, includePath, &useOfMfc, &useUnicode);
802+
const char* labelAttribute = node->Attribute("Label");
803+
if (labelAttribute && std::strcmp(labelAttribute, "Configuration") == 0) {
804+
configurationPropertyGroups.emplace_back(node);
805+
} else {
806+
importPropertyGroup(node, variables, includePath);
807+
}
790808
} else if (std::strcmp(name, "ImportGroup") == 0) {
791809
const char *labelAttribute = node->Attribute("Label");
792810
if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) {
@@ -862,7 +880,6 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
862880
fs.cfg = p.name;
863881
// TODO: detect actual MSC version
864882
fs.msc = true;
865-
fs.useMfc = useOfMfc;
866883
fs.defines = "_WIN32=1";
867884
if (p.platform == ProjectConfiguration::Win32)
868885
fs.platformType = Platform::Type::Win32W;
@@ -888,8 +905,13 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
888905
fs.defines += ";__AVX512__";
889906
additionalIncludePaths += ';' + i.additionalIncludePaths;
890907
}
891-
if (useUnicode) {
892-
fs.defines += ";UNICODE=1;_UNICODE=1";
908+
for (const ConfigutrationPropertyGroup &c : configurationPropertyGroups) {
909+
if (!c.conditionIsTrue(p))
910+
continue;
911+
if (c.useUnicode) {
912+
fs.defines += ";UNICODE=1;_UNICODE=1";
913+
}
914+
fs.useMfc = c.useOfMfc;
893915
}
894916
fsSetDefines(fs, fs.defines);
895917
fsSetIncludePaths(fs, Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables);

lib/importproject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ namespace cppcheck {
5252
* @brief Importing project settings.
5353
*/
5454
class CPPCHECKLIB WARN_UNUSED ImportProject {
55+
friend class TestImporter;
5556
public:
5657
enum class Type : std::uint8_t {
5758
NONE,
@@ -111,6 +112,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
111112
bool importSln(std::istream &istr, const std::string &path, const std::vector<std::string> &fileFilters);
112113
static SharedItemsProject importVcxitems(const std::string &filename, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
113114
bool importVcxproj(const std::string &filename, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
115+
bool importVcxproj(const std::string &filename, const tinyxml2::XMLDocument &doc, std::map<std::string, std::string, cppcheck::stricmp> &variables, const std::string &additionalIncludeDirectories, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
114116
bool importBcb6Prj(const std::string &projectFilename);
115117

116118
static void printError(const std::string &message);

test/testimportproject.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "redirect.h"
2323
#include "settings.h"
2424
#include "suppressions.h"
25+
#include "tinyxml2.h"
2526

2627
#include <list>
2728
#include <map>
@@ -34,6 +35,8 @@ class TestImporter : public ImportProject {
3435
public:
3536
using ImportProject::importCompileCommands;
3637
using ImportProject::importCppcheckGuiProject;
38+
using ImportProject::importVcxproj;
39+
using ImportProject::SharedItemsProject;
3740

3841
bool sourceFileExists(const std::string & /*file*/) override {
3942
return true;
@@ -71,6 +74,7 @@ class TestImportProject : public TestFixture {
7174
TEST_CASE(importCompileCommandsDirectoryInvalid); // 'directory' field not a string
7275
TEST_CASE(importCppcheckGuiProject);
7376
TEST_CASE(ignorePaths);
77+
TEST_CASE(testVcxprojUnicode);
7478
}
7579

7680
void setDefines() const {
@@ -455,6 +459,54 @@ class TestImportProject : public TestFixture {
455459
ASSERT_EQUALS(0, project.fileSettings.size());
456460
}
457461

462+
void testVcxprojUnicode()
463+
{
464+
const char vcxproj[] = R"-(
465+
<?xml version="1.0" encoding="utf-8"?>
466+
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
467+
<ItemGroup Label="ProjectConfigurations">
468+
<ProjectConfiguration Include="Debug|Win32">
469+
<Configuration>Debug</Configuration>
470+
<Platform>Win32</Platform>
471+
</ProjectConfiguration>
472+
<ProjectConfiguration Include="Release|Win32">
473+
<Configuration>Release</Configuration>
474+
<Platform>Win32</Platform>
475+
</ProjectConfiguration>
476+
</ItemGroup>
477+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
478+
<ConfigurationType>Application</ConfigurationType>
479+
<UseDebugLibraries>true</UseDebugLibraries>
480+
<PlatformToolset>v143</PlatformToolset>
481+
<CharacterSet>Unicode</CharacterSet>
482+
</PropertyGroup>
483+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
484+
<ConfigurationType>Application</ConfigurationType>
485+
<UseDebugLibraries>false</UseDebugLibraries>
486+
<PlatformToolset>v143</PlatformToolset>
487+
<CharacterSet>NotSet</CharacterSet>
488+
<UseOfMfc>Static</UseOfMfc>
489+
</PropertyGroup>
490+
<ItemGroup>
491+
<ClCompile Include="main.cpp" />
492+
</ItemGroup>
493+
</Project>
494+
)-";
495+
tinyxml2::XMLDocument doc;
496+
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, doc.Parse(vcxproj, sizeof(vcxproj)));
497+
TestImporter project;
498+
std::map<std::string, std::string, cppcheck::stricmp> variables;
499+
std::vector<TestImporter::SharedItemsProject> cache;
500+
ASSERT_EQUALS(project.importVcxproj("test.vcxproj", doc, variables, {}, {}, cache), true);
501+
ASSERT_EQUALS(project.fileSettings.size(), 2);
502+
ASSERT(project.fileSettings.front().defines.find(";UNICODE=1;") != std::string::npos);
503+
ASSERT(project.fileSettings.front().defines.find(";_UNICODE=1") != std::string::npos);
504+
ASSERT_EQUALS(project.fileSettings.front().useMfc, false);
505+
ASSERT(project.fileSettings.back().defines.find(";UNICODE=1;") == std::string::npos);
506+
ASSERT(project.fileSettings.back().defines.find(";_UNICODE=1") == std::string::npos);
507+
ASSERT_EQUALS(project.fileSettings.back().useMfc, true);
508+
}
509+
458510
// TODO: test fsParseCommand()
459511

460512
// TODO: test vcxproj conditions

0 commit comments

Comments
 (0)