Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ test/testfunctions.o: test/testfunctions.cpp lib/addoninfo.h lib/check.h lib/che
test/testgarbage.o: test/testgarbage.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testgarbage.cpp

test/testimportproject.o: test/testimportproject.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/redirect.h
test/testimportproject.o: test/testimportproject.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h test/fixture.h test/redirect.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testimportproject.cpp

test/testincompletestatement.o: test/testincompletestatement.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
Expand Down
170 changes: 102 additions & 68 deletions lib/importproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <stack>
#include <unordered_set>
#include <utility>
#include <vector>

#include "xml.h"

Expand Down Expand Up @@ -527,51 +528,11 @@ namespace {
std::string platformStr;
};

struct ItemDefinitionGroup {
explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : additionalIncludePaths(std::move(includePaths)) {
struct ConditionalGroup {
explicit ConditionalGroup(const tinyxml2::XMLElement *idg){
const char *condAttr = idg->Attribute("Condition");
if (condAttr)
condition = condAttr;
for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) {
const char* name = e1->Name();
if (std::strcmp(name, "ClCompile") == 0) {
enhancedInstructionSet = "StreamingSIMDExtensions2";
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
const char * const text = e->GetText();
if (!text)
continue;
const char * const ename = e->Name();
if (std::strcmp(ename, "PreprocessorDefinitions") == 0)
preprocessorDefinitions = text;
else if (std::strcmp(ename, "AdditionalIncludeDirectories") == 0) {
if (!additionalIncludePaths.empty())
additionalIncludePaths += ';';
additionalIncludePaths += text;
} else if (std::strcmp(ename, "LanguageStandard") == 0) {
if (std::strcmp(text, "stdcpp14") == 0)
cppstd = Standards::CPP14;
else if (std::strcmp(text, "stdcpp17") == 0)
cppstd = Standards::CPP17;
else if (std::strcmp(text, "stdcpp20") == 0)
cppstd = Standards::CPP20;
else if (std::strcmp(text, "stdcpplatest") == 0)
cppstd = Standards::CPPLatest;
} else if (std::strcmp(ename, "EnableEnhancedInstructionSet") == 0) {
enhancedInstructionSet = text;
}
}
}
else if (std::strcmp(name, "Link") == 0) {
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
const char * const text = e->GetText();
if (!text)
continue;
if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) {
entryPointSymbol = text;
}
}
}
}
mCondition_ = condAttr;
}

static void replaceAll(std::string &c, const std::string &from, const std::string &to) {
Expand All @@ -585,9 +546,9 @@ namespace {
// see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions
// properties are .NET String objects and you can call any of its members on them
bool conditionIsTrue(const ProjectConfiguration &p) const {
if (condition.empty())
if (mCondition_.empty())
return true;
std::string c = '(' + condition + ");";
std::string c = '(' + mCondition_ + ");";
replaceAll(c, "$(Configuration)", p.configuration);
replaceAll(c, "$(Platform)", p.platformStr);

Expand Down Expand Up @@ -623,13 +584,75 @@ namespace {
}
return false;
}
std::string condition;
private:
std::string mCondition_;
Comment thread
danmar marked this conversation as resolved.
Outdated
};

struct ItemDefinitionGroup : ConditionalGroup {
Comment thread
danmar marked this conversation as resolved.
explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : ConditionalGroup(idg), additionalIncludePaths(std::move(includePaths)) {
for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) {
const char* name = e1->Name();
if (std::strcmp(name, "ClCompile") == 0) {
enhancedInstructionSet = "StreamingSIMDExtensions2";
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
const char * const text = e->GetText();
if (!text)
continue;
const char * const ename = e->Name();
if (std::strcmp(ename, "PreprocessorDefinitions") == 0)
preprocessorDefinitions = text;
else if (std::strcmp(ename, "AdditionalIncludeDirectories") == 0) {
if (!additionalIncludePaths.empty())
additionalIncludePaths += ';';
additionalIncludePaths += text;
} else if (std::strcmp(ename, "LanguageStandard") == 0) {
if (std::strcmp(text, "stdcpp14") == 0)
cppstd = Standards::CPP14;
else if (std::strcmp(text, "stdcpp17") == 0)
cppstd = Standards::CPP17;
else if (std::strcmp(text, "stdcpp20") == 0)
cppstd = Standards::CPP20;
else if (std::strcmp(text, "stdcpplatest") == 0)
cppstd = Standards::CPPLatest;
} else if (std::strcmp(ename, "EnableEnhancedInstructionSet") == 0) {
enhancedInstructionSet = text;
}
}
}
else if (std::strcmp(name, "Link") == 0) {
for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) {
const char * const text = e->GetText();
if (!text)
continue;
if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) {
entryPointSymbol = text;
}
}
}
}
}

std::string enhancedInstructionSet;
std::string preprocessorDefinitions;
std::string additionalIncludePaths;
std::string entryPointSymbol; // TODO: use this
Standards::cppstd_t cppstd = Standards::CPPLatest;
};

struct ConfigutrationPropertyGroup : ConditionalGroup {
Comment thread
autoantwort marked this conversation as resolved.
Outdated
explicit ConfigutrationPropertyGroup(const tinyxml2::XMLElement *idg) : ConditionalGroup(idg) {
for (const tinyxml2::XMLElement *e = idg->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(), "UseOfMfc") == 0) {
useOfMfc = true;
} else if (std::strcmp(e->Name(), "CharacterSet") == 0) {
useUnicode = std::strcmp(e->GetText(), "Unicode") == 0;
}
}
}

bool useOfMfc = false;
bool useUnicode = false;
};
}

static std::list<std::string> toStringList(const std::string &s)
Expand All @@ -648,17 +671,8 @@ static std::list<std::string> toStringList(const std::string &s)
return ret;
}

static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map<std::string,std::string,cppcheck::stricmp> &variables, std::string &includePath, bool *useOfMfc)
static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map<std::string, std::string, cppcheck::stricmp> &variables, std::string &includePath)
{
if (useOfMfc) {
for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(), "UseOfMfc") == 0) {
*useOfMfc = true;
break;
}
}
}

const char* labelAttribute = node->Attribute("Label");
if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) {
for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) {
Expand Down Expand Up @@ -719,31 +733,39 @@ static void loadVisualStudioProperties(const std::string &props, std::map<std::s
}
}
} else if (std::strcmp(name,"PropertyGroup")==0) {
importPropertyGroup(node, variables, includePath, nullptr);
importPropertyGroup(node, variables, includePath);
} else if (std::strcmp(name,"ItemDefinitionGroup")==0) {
itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories);
}
}
}

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)
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)
{
tinyxml2::XMLDocument doc;
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
if (error != tinyxml2::XML_SUCCESS) {
printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
return false;
}
return importVcxproj(filename, doc, variables, additionalIncludeDirectories, fileFilters, cache);
}

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)
{
variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename));

std::list<ProjectConfiguration> projectConfigurationList;
std::list<std::string> compileList;
std::list<ItemDefinitionGroup> itemDefinitionGroupList;
std::vector<ConfigutrationPropertyGroup> configurationPropertyGroups;
std::string includePath;
std::vector<SharedItemsProject> sharedItemsProjects;

bool useOfMfc = false;

tinyxml2::XMLDocument doc;
const tinyxml2::XMLError error = doc.LoadFile(filename.c_str());
if (error != tinyxml2::XML_SUCCESS) {
printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error));
return false;
}
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
if (rootnode == nullptr) {
printError("Visual Studio project file has no XML root node");
Expand Down Expand Up @@ -777,7 +799,12 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
} else if (std::strcmp(name, "ItemDefinitionGroup") == 0) {
itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories);
} else if (std::strcmp(name, "PropertyGroup") == 0) {
importPropertyGroup(node, variables, includePath, &useOfMfc);
const char* labelAttribute = node->Attribute("Label");
if (labelAttribute && std::strcmp(labelAttribute, "Configuration") == 0) {
configurationPropertyGroups.emplace_back(node);
} else {
importPropertyGroup(node, variables, includePath);
}
} else if (std::strcmp(name, "ImportGroup") == 0) {
const char *labelAttribute = node->Attribute("Label");
if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) {
Expand Down Expand Up @@ -853,7 +880,6 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
fs.cfg = p.name;
// TODO: detect actual MSC version
fs.msc = true;
fs.useMfc = useOfMfc;
fs.defines = "_WIN32=1";
if (p.platform == ProjectConfiguration::Win32)
fs.platformType = Platform::Type::Win32W;
Expand All @@ -879,6 +905,14 @@ bool ImportProject::importVcxproj(const std::string &filename, std::map<std::str
fs.defines += ";__AVX512__";
additionalIncludePaths += ';' + i.additionalIncludePaths;
}
for (const ConfigutrationPropertyGroup &c : configurationPropertyGroups) {
if (!c.conditionIsTrue(p))
continue;
if (c.useUnicode) {
Comment thread
danmar marked this conversation as resolved.
Outdated
fs.defines += ";UNICODE=1;_UNICODE=1";
}
fs.useMfc = c.useOfMfc;
}
fsSetDefines(fs, fs.defines);
fsSetIncludePaths(fs, Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables);
for (const auto &path : sharedItemsIncludePaths) {
Expand Down
2 changes: 2 additions & 0 deletions lib/importproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace cppcheck {
* @brief Importing project settings.
*/
class CPPCHECKLIB WARN_UNUSED ImportProject {
friend class TestImporter;
public:
enum class Type : std::uint8_t {
NONE,
Expand Down Expand Up @@ -111,6 +112,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject {
bool importSln(std::istream &istr, const std::string &path, const std::vector<std::string> &fileFilters);
static SharedItemsProject importVcxitems(const std::string &filename, const std::vector<std::string> &fileFilters, std::vector<SharedItemsProject> &cache);
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);
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);
bool importBcb6Prj(const std::string &projectFilename);

static void printError(const std::string &message);
Expand Down
52 changes: 52 additions & 0 deletions test/testimportproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "redirect.h"
#include "settings.h"
#include "suppressions.h"
#include "xml.h"

#include <list>
#include <map>
Expand All @@ -34,6 +35,8 @@ class TestImporter : public ImportProject {
public:
using ImportProject::importCompileCommands;
using ImportProject::importCppcheckGuiProject;
using ImportProject::importVcxproj;
using ImportProject::SharedItemsProject;

bool sourceFileExists(const std::string & /*file*/) override {
return true;
Expand Down Expand Up @@ -71,6 +74,7 @@ class TestImportProject : public TestFixture {
TEST_CASE(importCompileCommandsDirectoryInvalid); // 'directory' field not a string
TEST_CASE(importCppcheckGuiProject);
TEST_CASE(ignorePaths);
TEST_CASE(testVcxprojUnicode);
}

void setDefines() const {
Expand Down Expand Up @@ -455,6 +459,54 @@ class TestImportProject : public TestFixture {
ASSERT_EQUALS(0, project.fileSettings.size());
}

void testVcxprojUnicode() const
{
const char vcxproj[] = R"-(
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
<UseOfMfc>Static</UseOfMfc>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
</Project>
)-";
tinyxml2::XMLDocument doc;
ASSERT_EQUALS(tinyxml2::XML_SUCCESS, doc.Parse(vcxproj, sizeof(vcxproj)));
TestImporter project;
std::map<std::string, std::string, cppcheck::stricmp> variables;
std::vector<TestImporter::SharedItemsProject> cache;
ASSERT_EQUALS(project.importVcxproj("test.vcxproj", doc, variables, {}, {}, cache), true);
ASSERT_EQUALS(project.fileSettings.size(), 2);
ASSERT(project.fileSettings.front().defines.find(";UNICODE=1;") != std::string::npos);
ASSERT(project.fileSettings.front().defines.find(";_UNICODE=1") != std::string::npos);
ASSERT_EQUALS(project.fileSettings.front().useMfc, false);
ASSERT(project.fileSettings.back().defines.find(";UNICODE=1;") == std::string::npos);
ASSERT(project.fileSettings.back().defines.find(";_UNICODE=1") == std::string::npos);
ASSERT_EQUALS(project.fileSettings.back().useMfc, true);
}

// TODO: test fsParseCommand()

// TODO: test vcxproj conditions
Expand Down
Loading