@@ -186,9 +186,12 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings
186186 mPath = Path::getPathFromFilename (Path::fromNativeSeparators (filename));
187187 if (!mPath .empty () && !endsWith (mPath ,' /' ))
188188 mPath += ' /' ;
189+ if (mPath .empty ())
190+ mPath = std::string (" ./" );
189191
190192 const std::vector<std::string> fileFilters =
191193 settings ? settings->fileFilters : std::vector<std::string>();
194+ std::vector<SharedItemsProject> sharedItemsProjects{};
192195
193196 if (endsWith (filename, " .json" )) {
194197 if (importCompileCommands (fin)) {
@@ -202,7 +205,7 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings
202205 }
203206 } else if (endsWith (filename, " .vcxproj" )) {
204207 std::map<std::string, std::string, cppcheck::stricmp> variables;
205- if (importVcxproj (filename, variables, emptyString, fileFilters)) {
208+ if (importVcxproj (filename, variables, emptyString, fileFilters, sharedItemsProjects )) {
206209 setRelativePaths (filename);
207210 return ImportProject::Type::VS_VCXPROJ;
208211 }
@@ -444,7 +447,7 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const
444447 variables[" SolutionDir" ] = path;
445448
446449 bool found = false ;
447-
450+ std::vector<SharedItemsProject> sharedItemsProjects{};
448451 while (std::getline (istr,line)) {
449452 if (!startsWith (line," Project(" ))
450453 continue ;
@@ -459,7 +462,7 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const
459462 if (!Path::isAbsolute (vcxproj))
460463 vcxproj = path + vcxproj;
461464 vcxproj = Path::fromNativeSeparators (std::move (vcxproj));
462- if (!importVcxproj (vcxproj, variables, emptyString, fileFilters)) {
465+ if (!importVcxproj (vcxproj, variables, emptyString, fileFilters, sharedItemsProjects )) {
463466 printError (" failed to load '" + vcxproj + " ' from Visual Studio solution" );
464467 return false ;
465468 }
@@ -680,14 +683,15 @@ static void loadVisualStudioProperties(const std::string &props, std::map<std::s
680683 }
681684}
682685
683- 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)
686+ 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 )
684687{
685688 variables[" ProjectDir" ] = Path::simplifyPath (Path::getPathFromFilename (filename));
686689
687690 std::list<ProjectConfiguration> projectConfigurationList;
688691 std::list<std::string> compileList;
689692 std::list<ItemDefinitionGroup> itemDefinitionGroupList;
690693 std::string includePath;
694+ std::vector<SharedItemsProject> sharedItemsProjects;
691695
692696 bool useOfMfc = false ;
693697
@@ -715,37 +719,85 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
715719 }
716720 }
717721 }
718- } else {
719- for (const tinyxml2::XMLElement *e = node->FirstChildElement (); e; e = e->NextSiblingElement ()) {
722+ }
723+ else {
724+ for (const tinyxml2::XMLElement* e = node->FirstChildElement (); e; e = e->NextSiblingElement ()) {
720725 if (std::strcmp (e->Name (), " ClCompile" ) == 0 ) {
721- const char *include = e->Attribute (" Include" );
722- if (include && Path::acceptFile (include))
723- compileList.emplace_back (include);
726+ const char * include = e->Attribute (" Include" );
727+ if (include && Path::acceptFile (include)) {
728+ std::string toInclude = Path::simplifyPath (Path::isAbsolute (include) ? include : Path::getPathFromFilename (filename) + include);
729+ compileList.emplace_back (toInclude);
730+ }
724731 }
725732 }
726733 }
727- } else if (std::strcmp (node->Name (), " ItemDefinitionGroup" ) == 0 ) {
734+ }
735+ else if (std::strcmp (node->Name (), " ItemDefinitionGroup" ) == 0 ) {
728736 itemDefinitionGroupList.emplace_back (node, additionalIncludeDirectories);
729- } else if (std::strcmp (node->Name (), " PropertyGroup" ) == 0 ) {
737+ }
738+ else if (std::strcmp (node->Name (), " PropertyGroup" ) == 0 ) {
730739 importPropertyGroup (node, variables, includePath, &useOfMfc);
731- } else if (std::strcmp (node->Name (), " ImportGroup" ) == 0 ) {
732- const char *labelAttribute = node->Attribute (" Label" );
740+ }
741+ else if (std::strcmp (node->Name (), " ImportGroup" ) == 0 ) {
742+ const char * labelAttribute = node->Attribute (" Label" );
733743 if (labelAttribute && std::strcmp (labelAttribute, " PropertySheets" ) == 0 ) {
734- for (const tinyxml2::XMLElement * e = node->FirstChildElement (); e; e = e->NextSiblingElement ()) {
744+ for (const tinyxml2::XMLElement* e = node->FirstChildElement (); e; e = e->NextSiblingElement ()) {
735745 if (std::strcmp (e->Name (), " Import" ) == 0 ) {
736- const char * projectAttribute = e->Attribute (" Project" );
746+ const char * projectAttribute = e->Attribute (" Project" );
737747 if (projectAttribute)
738748 loadVisualStudioProperties (projectAttribute, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList);
739749 }
740750 }
741751 }
752+ else if (labelAttribute && std::strcmp (labelAttribute, " Shared" ) == 0 ) {
753+ for (const tinyxml2::XMLElement* e = node->FirstChildElement (); e; e = e->NextSiblingElement ()) {
754+ if (std::strcmp (e->Name (), " Import" ) == 0 ) {
755+ const char * projectAttribute = e->Attribute (" Project" );
756+ if (projectAttribute)
757+ {
758+ // Path to shared items project is relative to current project directory,
759+ // unless the string starts with $(SolutionDir)
760+ std::string pathToSharedItemsFile;
761+ if (std::string (projectAttribute).rfind (" $(SolutionDir)" , 0 ) == 0 ) {
762+ pathToSharedItemsFile = std::string (projectAttribute);
763+ } else {
764+ pathToSharedItemsFile = variables[" ProjectDir" ] + std::string (projectAttribute);
765+ }
766+ if (!simplifyPathWithVariables (pathToSharedItemsFile, variables)) {
767+ printError (" Could not simplify path to referenced shared items project" );
768+ exit (-1 );
769+ }
770+ SharedItemsProject toAdd = importVcxitems (pathToSharedItemsFile, fileFilters, cache);
771+ if (!toAdd.successFull )
772+ {
773+ printError (" Could not load shared items project \" " + pathToSharedItemsFile + " \" from original path \" " + std::string (projectAttribute) + " \" ." );
774+ return false ;
775+ }
776+ sharedItemsProjects.emplace_back (toAdd);
777+ }
778+ }
779+ }
780+ }
742781 }
743782 }
744783 // # TODO: support signedness of char via /J (and potential XML option for it)?
745784 // we can only set it globally but in this context it needs to be treated per file
746785
747- for (const std::string &c : compileList) {
748- const std::string cfilename = Path::simplifyPath (Path::isAbsolute (c) ? c : Path::getPathFromFilename (filename) + c);
786+ // Include shared items project files
787+ std::vector<std::string> sharedItemsIncludePaths{};
788+ for (const auto & sharedProject : sharedItemsProjects) {
789+ for (const auto & file : sharedProject.sourceFiles ) {
790+ std::string pathToFile = Path::simplifyPath (Path::getPathFromFilename (sharedProject.pathToProjectFile ) + file);
791+ compileList.emplace_back (std::move (pathToFile));
792+ }
793+ for (const auto & p : sharedProject.includePaths ) {
794+ std::string path = Path::simplifyPath (Path::getPathFromFilename (sharedProject.pathToProjectFile ) + p);
795+ sharedItemsIncludePaths.emplace_back (std::move (path));
796+ }
797+ }
798+
799+ // Project files
800+ for (const std::string& cfilename : compileList) {
749801 if (!fileFilters.empty () && !matchglobs (fileFilters, cfilename))
750802 continue ;
751803
@@ -792,13 +844,87 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
792844 }
793845 fsSetDefines (fs, fs.defines );
794846 fsSetIncludePaths (fs, Path::getPathFromFilename (filename), toStringList (includePath + ' ;' + additionalIncludePaths), variables);
847+ for (const auto & path : sharedItemsIncludePaths) {
848+ fs.includePaths .emplace_back (path);
849+ }
795850 fileSettings.push_back (std::move (fs));
796851 }
797852 }
798853
799854 return true ;
800855}
801856
857+ static std::string stringReplace (const std::string& original, const std::string& toReplace, const std::string& replaceWith)
858+ {
859+ std::string result (original);
860+ size_t pos = result.find (toReplace);
861+ while (pos != std::string::npos) {
862+ result.replace (pos, toReplace.length (), replaceWith);
863+ pos = result.find (toReplace);
864+ }
865+ return result;
866+ }
867+
868+ ImportProject::SharedItemsProject ImportProject::importVcxitems (const std::string& filename, const std::vector<std::string>& fileFilters, std::vector<SharedItemsProject> &cache)
869+ {
870+ for (const auto & entry : cache)
871+ {
872+ if (filename == entry.pathToProjectFile )
873+ {
874+ return entry;
875+ }
876+ }
877+
878+ SharedItemsProject result{};
879+ result.pathToProjectFile = filename;
880+
881+ tinyxml2::XMLDocument doc;
882+ const tinyxml2::XMLError error = doc.LoadFile (filename.c_str ());
883+ if (error != tinyxml2::XML_SUCCESS) {
884+ printError (std::string (" Visual Studio project file is not a valid XML - " ) + tinyxml2::XMLDocument::ErrorIDToName (error));
885+ return result;
886+ }
887+ const tinyxml2::XMLElement* const rootnode = doc.FirstChildElement ();
888+ if (rootnode == nullptr ) {
889+ printError (" Visual Studio project file has no XML root node" );
890+ return result;
891+ }
892+ for (const tinyxml2::XMLElement* node = rootnode->FirstChildElement (); node; node = node->NextSiblingElement ()) {
893+ if (std::strcmp (node->Name (), " ItemGroup" ) == 0 ) {
894+ for (const tinyxml2::XMLElement* e = node->FirstChildElement (); e; e = e->NextSiblingElement ()) {
895+ if (std::strcmp (e->Name (), " ClCompile" ) == 0 ) {
896+ const char * include = e->Attribute (" Include" );
897+ if (include && Path::acceptFile (include)) {
898+ std::string file = stringReplace (include, " $(MSBuildThisFileDirectory)" , " ./" );
899+
900+ // Don't include file if it matches the filter
901+ if (!fileFilters.empty () && !matchglobs (fileFilters, file))
902+ continue ;
903+
904+ result.sourceFiles .emplace_back (file);
905+ } else {
906+ printError (" Could not find shared items source file" );
907+ return result;
908+ }
909+ }
910+ }
911+ }
912+ else if (std::strcmp (node->Name (), " ItemDefinitionGroup" ) == 0 ) {
913+ ItemDefinitionGroup temp (node, " " );
914+ for (const auto & includePath : toStringList (temp.additionalIncludePaths )) {
915+ if (includePath == std::string (" %(AdditionalIncludeDirectories)" ))
916+ continue ;
917+
918+ result.includePaths .emplace_back (stringReplace (includePath, " $(MSBuildThisFileDirectory)" , " ./" ));
919+ }
920+ }
921+ }
922+
923+ result.successFull = true ;
924+ cache.emplace_back (result);
925+ return result;
926+ }
927+
802928bool ImportProject::importBcb6Prj (const std::string &projectFilename)
803929{
804930 tinyxml2::XMLDocument doc;
0 commit comments