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)
534- 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- }
535+ mCondition = condAttr;
575536 }
576537
577538 static void replaceAll (std::string &c, const std::string &from, const std::string &to) {
@@ -585,9 +546,9 @@ namespace {
585546 // see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
586547 // properties are .NET String objects and you can call any of its members on them
587548 bool conditionIsTrue (const ProjectConfiguration &p) const {
588- if (condition .empty ())
549+ if (mCondition .empty ())
589550 return true ;
590- std::string c = ' (' + condition + " );" ;
551+ std::string c = ' (' + mCondition + " );" ;
591552 replaceAll (c, " $(Configuration)" , p.configuration );
592553 replaceAll (c, " $(Platform)" , p.platformStr );
593554
@@ -623,13 +584,75 @@ namespace {
623584 }
624585 return false ;
625586 }
626- std::string condition;
587+ private:
588+ std::string mCondition ;
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 ConfigurationPropertyGroup : ConditionalGroup {
643+ explicit ConfigurationPropertyGroup (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
635658static std::list<std::string> toStringList (const std::string &s)
@@ -648,17 +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 )
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-
662676 const char * labelAttribute = node->Attribute (" Label" );
663677 if (labelAttribute && std::strcmp (labelAttribute, " UserMacros" ) == 0 ) {
664678 for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement (); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement ()) {
@@ -719,31 +733,39 @@ static void loadVisualStudioProperties(const std::string &props, std::map<std::s
719733 }
720734 }
721735 } else if (std::strcmp (name," PropertyGroup" )==0 ) {
722- importPropertyGroup (node, variables, includePath, nullptr );
736+ importPropertyGroup (node, variables, includePath);
723737 } else if (std::strcmp (name," ItemDefinitionGroup" )==0 ) {
724738 itemDefinitionGroupList.emplace_back (node, additionalIncludeDirectories);
725739 }
726740 }
727741}
728742
729- 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)
730759{
731760 variables[" ProjectDir" ] = Path::simplifyPath (Path::getPathFromFilename (filename));
732761
733762 std::list<ProjectConfiguration> projectConfigurationList;
734763 std::list<std::string> compileList;
735764 std::list<ItemDefinitionGroup> itemDefinitionGroupList;
765+ std::vector<ConfigurationPropertyGroup> configurationPropertyGroups;
736766 std::string includePath;
737767 std::vector<SharedItemsProject> sharedItemsProjects;
738768
739- bool useOfMfc = false ;
740-
741- tinyxml2::XMLDocument doc;
742- const tinyxml2::XMLError error = doc.LoadFile (filename.c_str ());
743- if (error != tinyxml2::XML_SUCCESS) {
744- printError (std::string (" Visual Studio project file is not a valid XML - " ) + tinyxml2::XMLDocument::ErrorIDToName (error));
745- return false ;
746- }
747769 const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement ();
748770 if (rootnode == nullptr ) {
749771 printError (" Visual Studio project file has no XML root node" );
@@ -777,7 +799,12 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
777799 } else if (std::strcmp (name, " ItemDefinitionGroup" ) == 0 ) {
778800 itemDefinitionGroupList.emplace_back (node, additionalIncludeDirectories);
779801 } else if (std::strcmp (name, " PropertyGroup" ) == 0 ) {
780- importPropertyGroup (node, variables, includePath, &useOfMfc);
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+ }
781808 } else if (std::strcmp (name, " ImportGroup" ) == 0 ) {
782809 const char *labelAttribute = node->Attribute (" Label" );
783810 if (labelAttribute && std::strcmp (labelAttribute, " PropertySheets" ) == 0 ) {
@@ -853,7 +880,6 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
853880 fs.cfg = p.name ;
854881 // TODO: detect actual MSC version
855882 fs.msc = true ;
856- fs.useMfc = useOfMfc;
857883 fs.defines = " _WIN32=1" ;
858884 if (p.platform == ProjectConfiguration::Win32)
859885 fs.platformType = Platform::Type::Win32W;
@@ -879,6 +905,17 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
879905 fs.defines += " ;__AVX512__" ;
880906 additionalIncludePaths += ' ;' + i.additionalIncludePaths ;
881907 }
908+ bool useUnicode = false ;
909+ for (const ConfigurationPropertyGroup &c : configurationPropertyGroups) {
910+ if (!c.conditionIsTrue (p))
911+ continue ;
912+ // in msbuild the last definition wins
913+ useUnicode = c.useUnicode ;
914+ fs.useMfc = c.useOfMfc ;
915+ }
916+ if (useUnicode) {
917+ fs.defines += " ;UNICODE=1;_UNICODE=1" ;
918+ }
882919 fsSetDefines (fs, fs.defines );
883920 fsSetIncludePaths (fs, Path::getPathFromFilename (filename), toStringList (includePath + ' ;' + additionalIncludePaths), variables);
884921 for (const auto &path : sharedItemsIncludePaths) {
0 commit comments