diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/expected_output/output.txt b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/expected_output/output.txt
new file mode 100644
index 00000000..c2ae80cc
--- /dev/null
+++ b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/expected_output/output.txt
@@ -0,0 +1,63 @@
+SourceFilePath: "src/tictactoe/src/artificial_player.cpp"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/artificial_player.cpp"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
+SourceFilePath: "src/tictactoe/src/field.cpp"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/field.cpp"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
+SourceFilePath: "src/tictactoe/src/human_player.cc"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/human_player.cc"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
+SourceFilePath: "src/tictactoe/src/io.cpp"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/io.cpp"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
+SourceFilePath: "src/tictactoe/src/main.cpp"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/main.cpp"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
+SourceFilePath: "src/tictactoe/src/player.cpp"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/player.cpp"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
+SourceFilePath: "src/tictactoe/src/tictactoe.cpp"
+ LanguageStandard: "c++17"
+ IndexedPath: "src/tictactoe/src/tictactoe.cpp"
+ IndexedPath: "test/indexed/header/path"
+ CompilerFlag: "-isystem"
+ CompilerFlag: "src/tictactoe/include"
+ CompilerFlag: "-v"
+ HeaderSearchPath: "test/header/search/path"
+ FrameworkSearchPath: "test/framework/search/path"
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/sonargraph/system.sonargraph b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/sonargraph/system.sonargraph
new file mode 100644
index 00000000..be848932
--- /dev/null
+++ b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/sonargraph/system.sonargraph
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ ../src/tictactoe
+ .cpp,.cc
+ -I./include
+-v
+
+
\ No newline at end of file
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/artificial_player.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/artificial_player.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/field.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/field.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/game_object.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/game_object.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/human_player.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/human_player.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/io.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/io.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/player.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/player.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/tictactoe.h b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/include/tictactoe.h
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/artificial_player.cpp b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/artificial_player.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/field.cpp b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/field.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/human_player.cc b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/human_player.cc
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/io.cpp b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/io.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/main.cpp b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/main.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/player.cpp b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/player.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/tictactoe.cpp b/bin/test/data/SonargraphProjectTestSuite/CxxCppManualModules/src/tictactoe/src/tictactoe.cpp
new file mode 100644
index 00000000..e69de29b
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 633cf90e..f7fdd0c5 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -542,6 +542,8 @@ add_files(
utility/sonargraph/SonargraphXsdAbstractSystemExtension.h
utility/sonargraph/SonargraphXsdCmakeJsonModule.cpp
utility/sonargraph/SonargraphXsdCmakeJsonModule.h
+ utility/sonargraph/SonargraphXsdCppManualModule.cpp
+ utility/sonargraph/SonargraphXsdCppManualModule.h
utility/sonargraph/SonargraphXsdCppSystemSettings.cpp
utility/sonargraph/SonargraphXsdCppSystemSettings.h
utility/sonargraph/SonargraphXsdJavaModule.cpp
diff --git a/src/lib/utility/sonargraph/SonargraphXsdAbstractModule.cpp b/src/lib/utility/sonargraph/SonargraphXsdAbstractModule.cpp
index 74e646ad..91f9935d 100644
--- a/src/lib/utility/sonargraph/SonargraphXsdAbstractModule.cpp
+++ b/src/lib/utility/sonargraph/SonargraphXsdAbstractModule.cpp
@@ -4,6 +4,7 @@
#include "utility/logging/logging.h"
#include "utility/sonargraph/SonargraphXsdCmakeJsonModule.h"
+#include "utility/sonargraph/SonargraphXsdCppManualModule.h"
#include "utility/sonargraph/SonargraphXsdJavaModule.h"
#include "utility/sonargraph/SonargraphXsdRootPath.h"
#include "utility/sonargraph/SonargraphSoftwareSystem.h"
@@ -24,6 +25,10 @@ namespace Sonargraph
{
return module;
}
+ if (std::shared_ptr module = XsdCppManualModule::create(element, parent))
+ {
+ return module;
+ }
if (std::shared_ptr module = XsdJavaModule::create(element, parent))
{
return module;
diff --git a/src/lib/utility/sonargraph/SonargraphXsdCppManualModule.cpp b/src/lib/utility/sonargraph/SonargraphXsdCppManualModule.cpp
new file mode 100644
index 00000000..3c574711
--- /dev/null
+++ b/src/lib/utility/sonargraph/SonargraphXsdCppManualModule.cpp
@@ -0,0 +1,299 @@
+#include "utility/sonargraph/SonargraphXsdCppManualModule.h"
+
+#include "tinyxml/tinyxml.h"
+
+#include "data/indexer/IndexerCommandCxxEmpty.h"
+#include "settings/ApplicationSettings.h"
+#include "settings/SourceGroupSettingsCxxSonargraph.h"
+#include "utility/file/FileSystem.h"
+#include "utility/sonargraph/SonargraphSoftwareSystem.h"
+#include "utility/sonargraph/utilitySonargraph.h"
+#include "utility/utility.h"
+#include "utility/utilityFile.h"
+#include "utility/utilityXml.h"
+
+namespace Sonargraph
+{
+ std::string XsdCppManualModule::getXsdTypeName()
+ {
+ return "xsdCppManualModule";
+ }
+
+ std::vector XsdCppManualModule::getIncludeOptionPrefixes()
+ {
+ // make sure that none of these prefixes is the prefix of a prefix that appears further down in the list
+ return {
+ L"-I",
+ L"--include_directory=",
+ L"--sys_include="
+ };
+ }
+
+ std::shared_ptr XsdCppManualModule::create(const TiXmlElement* element, std::weak_ptr parent)
+ {
+ if (!utility::xmlElementHasAttribute(element, "xsi:type") || utility::sonargraphXmlElementIsType(element, getXsdTypeName()))
+ {
+ std::shared_ptr module = std::shared_ptr(new XsdCppManualModule());
+ if (module->init(element, parent))
+ {
+ return module;
+ }
+ }
+ return std::shared_ptr();
+ }
+
+ LanguageType XsdCppManualModule::getSupportedLanguage() const
+ {
+ return LANGUAGE_CPP;
+ }
+
+ std::set XsdCppManualModule::getAllSourcePaths() const
+ {
+ FilePath baseDirectory;
+ if (std::shared_ptr softwareSystem = getSoftwareSystem())
+ {
+ baseDirectory = softwareSystem->getBaseDirectory();
+ }
+
+ std::set sourcePaths;
+ for (std::shared_ptr rootPath : getRootPaths())
+ {
+ sourcePaths.insert(rootPath->getFilePath(baseDirectory));
+ }
+
+ return sourcePaths;
+ }
+
+ std::set XsdCppManualModule::getAllSourceFilePathsCanonical() const
+ {
+ const std::vector excludeFilters = getDerivedExcludeFilters();
+ const std::vector includeFilters = getDerivedIncludeFilters();
+
+ std::set sourceFilePaths;
+
+ for (const FileInfo& fileInfo : FileSystem::getFileInfosFromPaths(utility::getTopLevelPaths(getAllSourcePaths()), m_sourceFileExtensions))
+ {
+ const FilePath sourceFilePath = fileInfo.path.getCanonical();
+
+ bool excludeMatches = false;
+ for (const FilePathFilter& excludeFilter : excludeFilters)
+ {
+ if (excludeFilter.isMatching(sourceFilePath))
+ {
+ excludeMatches = true;
+ break;
+ }
+ }
+ if (excludeMatches)
+ {
+ for (const FilePathFilter& includeFilter : includeFilters)
+ {
+ if (includeFilter.isMatching(sourceFilePath))
+ {
+ excludeMatches = false;
+ break;
+ }
+ }
+ }
+ if (!excludeMatches)
+ {
+ sourceFilePaths.insert(sourceFilePath);
+ }
+ }
+
+ return sourceFilePaths;
+ }
+
+ std::set XsdCppManualModule::getAllCxxHeaderSearchPathsCanonical() const
+ {
+ std::shared_ptr softwareSystem = getSoftwareSystem();
+ if (!softwareSystem)
+ {
+ return std::set();
+ }
+
+ const std::vector optionPrefixes = getIncludeOptionPrefixes();
+ const FilePath baseDir = softwareSystem->getBaseDirectory();
+ const FilePath baseIncludeDir = m_basePathForIncludes.isAbsolute() ? m_basePathForIncludes : baseDir.getConcatenated(m_basePathForIncludes);
+
+ std::set headerSearchPaths;
+ for (const std::wstring& compilerOption : m_moduleCompilerOptions)
+ {
+ for (const std::wstring& optionPrefix : optionPrefixes)
+ {
+ if (utility::isPrefix(optionPrefix, compilerOption))
+ {
+ FilePath headerSearchPath(utility::trim(compilerOption.substr(optionPrefix.size())));
+ if (headerSearchPath.isAbsolute())
+ {
+ headerSearchPaths.insert(headerSearchPath);
+ }
+ else
+ {
+ headerSearchPaths.insert(baseIncludeDir.getConcatenated(headerSearchPath));
+ }
+ break;
+ }
+ }
+ }
+
+ return headerSearchPaths;
+ }
+
+ std::vector> XsdCppManualModule::getIndexerCommands(
+ std::shared_ptr sourceGroupSettings,
+ std::shared_ptr appSettings) const
+ {
+ std::shared_ptr softwareSystem = getSoftwareSystem();
+ if (!softwareSystem)
+ {
+ return std::vector>();
+ }
+
+ std::vector> indexerCommands;
+
+ std::set indexedHeaderPaths;
+ std::string languageStandard = SourceGroupSettingsWithCppStandard::getDefaultCppStandardStatic();
+
+ if (std::shared_ptr sonargraphSettings =
+ std::dynamic_pointer_cast(sourceGroupSettings))
+ {
+ indexedHeaderPaths = utility::toSet(sonargraphSettings->getIndexedHeaderPathsExpandedAndAbsolute());
+ languageStandard = sonargraphSettings->getCppStandard();
+ }
+ else
+ {
+ LOG_ERROR("Source group doesn't specify language standard. Falling back to \"" + languageStandard + "\".");
+ LOG_ERROR("Source group doesn't specify any indexed header paths");
+ }
+
+ const std::vector systemHeaderSearchPaths = (appSettings ? appSettings->getHeaderSearchPathsExpanded() : std::vector());
+ const std::vector frameworkSearchPaths = (appSettings ? appSettings->getFrameworkSearchPathsExpanded() : std::vector());
+
+ const std::set excludeFilters = utility::toSet(getDerivedExcludeFilters());
+ const std::set includeFilters = utility::toSet(getDerivedIncludeFilters());
+
+
+ std::vector processedOptions;
+ {
+ const std::vector optionPrefixes = getIncludeOptionPrefixes();
+ const FilePath baseIncludeDir = m_basePathForIncludes.isAbsolute() ? m_basePathForIncludes : softwareSystem->getBaseDirectory().getConcatenated(m_basePathForIncludes);
+
+ for (const std::wstring& compilerOption : m_moduleCompilerOptions)
+ {
+ bool isIncludeOption = false;
+ for (const std::wstring& optionPrefix : optionPrefixes)
+ {
+ if (utility::isPrefix(optionPrefix, compilerOption))
+ {
+ isIncludeOption = true;
+
+ processedOptions.push_back(L"-isystem");
+
+ FilePath headerSearchPath(utility::trim(compilerOption.substr(optionPrefix.size())));
+ if (headerSearchPath.isAbsolute())
+ {
+ processedOptions.push_back(headerSearchPath.wstr());
+ }
+ else
+ {
+ processedOptions.push_back(baseIncludeDir.getConcatenated(headerSearchPath).wstr());
+ }
+ break;
+ }
+ }
+
+ if (!isIncludeOption)
+ {
+ processedOptions.push_back(compilerOption);
+ }
+ }
+ }
+
+ for (const FilePath& sourceFilePath : getAllSourceFilePathsCanonical())
+ {
+ indexerCommands.push_back(std::make_shared(
+ sourceFilePath,
+ utility::concat(indexedHeaderPaths, { sourceFilePath }),
+ excludeFilters,
+ includeFilters,
+ softwareSystem->getBaseDirectory(),
+ systemHeaderSearchPaths,
+ frameworkSearchPaths,
+ processedOptions,
+ languageStandard
+ ));
+ }
+
+ return indexerCommands;
+ }
+
+ XsdCppManualModule::XsdCppManualModule()
+ : m_basePathForIncludes(L".")
+ , m_sourceFileExtensions({L".cpp"})
+ {
+ }
+
+ bool XsdCppManualModule::init(const TiXmlElement* element, std::weak_ptr parent)
+ {
+ if (!XsdAbstractModule::init(element, parent))
+ {
+ return false;
+ }
+
+ if (element != nullptr)
+ {
+
+ {
+ const TiXmlElement* basePathForIncludesElement = element->FirstChildElement("basePathForIncludes");
+ if (basePathForIncludesElement != nullptr)
+ {
+ const char* text = basePathForIncludesElement->GetText();
+ if (text != nullptr)
+ {
+ m_basePathForIncludes = FilePath(utility::decodeFromUtf8(text));
+ }
+ else
+ {
+ LOG_ERROR("Unable to parse \"basePathForIncludes\" element of Sonargraph " + getXsdTypeName() + ".");
+ return false;
+ }
+ }
+ }
+ {
+ const TiXmlElement* sourceFileExtensionsElement = element->FirstChildElement("sourceFileExtensions");
+ if (sourceFileExtensionsElement != nullptr)
+ {
+ const char* text = sourceFileExtensionsElement->GetText();
+ if (text != nullptr)
+ {
+ m_sourceFileExtensions.clear();
+ for (const std::wstring extension : utility::split>(utility::decodeFromUtf8(text), L","))
+ {
+ m_sourceFileExtensions.push_back(utility::trim(extension));
+ }
+ }
+ else
+ {
+ LOG_ERROR("Unable to parse \"sourceFileExtensions\" element of Sonargraph " + getXsdTypeName() + ".");
+ return false;
+ }
+ }
+ }
+
+ for (const TiXmlElement* moduleCompilerOptionsElement : utility::getXmlChildElementsWithName(element, "moduleCompilerOptions"))
+ {
+ const char* text = moduleCompilerOptionsElement->GetText();
+ if (text != nullptr)
+ {
+ for (const std::wstring& s : utility::splitToVector(utility::decodeFromUtf8(text), L' '))
+ {
+ m_moduleCompilerOptions.push_back(utility::trim(s));
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/lib/utility/sonargraph/SonargraphXsdCppManualModule.h b/src/lib/utility/sonargraph/SonargraphXsdCppManualModule.h
new file mode 100644
index 00000000..67b4ca1c
--- /dev/null
+++ b/src/lib/utility/sonargraph/SonargraphXsdCppManualModule.h
@@ -0,0 +1,35 @@
+#ifndef SONARGRAPH_XSD_CPP_MANUAL_MODULE_H
+#define SONARGRAPH_XSD_CPP_MANUAL_MODULE_H
+
+#include "utility/sonargraph/SonargraphXsdAbstractModule.h"
+#include "utility/sonargraph/SonargraphXsdRootPathWithFiles.h"
+
+namespace Sonargraph
+{
+ class XsdCppManualModule : public XsdAbstractModule
+ {
+ public:
+ static std::string getXsdTypeName();
+ static std::vector getIncludeOptionPrefixes();
+ static std::shared_ptr create(const TiXmlElement* element, std::weak_ptr parent);
+
+ LanguageType getSupportedLanguage() const override;
+
+ std::set getAllSourcePaths() const override;
+ std::set getAllSourceFilePathsCanonical() const override;
+ std::set getAllCxxHeaderSearchPathsCanonical() const override;
+ std::vector> getIndexerCommands(
+ std::shared_ptr sourceGroupSettings,
+ std::shared_ptr appSettings) const override;
+
+ protected:
+ XsdCppManualModule();
+ bool init(const TiXmlElement* element, std::weak_ptr parent);
+
+ FilePath m_basePathForIncludes;
+ std::vector m_sourceFileExtensions;
+ std::vector m_moduleCompilerOptions;
+ };
+}
+
+#endif // SONARGRAPH_XSD_CPP_MANUAL_MODULE_H
diff --git a/src/test/SonargraphProjectTestSuite.h b/src/test/SonargraphProjectTestSuite.h
index a389106b..fcd70c30 100644
--- a/src/test/SonargraphProjectTestSuite.h
+++ b/src/test/SonargraphProjectTestSuite.h
@@ -33,6 +33,21 @@ public:
generateAndCompareExpectedOutputForSonargraphProject(projectName, sourceGroupSettings, applicationSettings);
}
+ void test_sonargraph_project_with_cpp_manual_modules_generates_expected_output()
+ {
+ const std::wstring projectName = L"CxxCppManualModules";
+
+ ProjectSettings projectSettings;
+ std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings);
+ sourceGroupSettings->setIndexedHeaderPaths({ FilePath(L"test/indexed/header/path") });
+
+ std::shared_ptr applicationSettings = std::make_shared();
+ applicationSettings->setHeaderSearchPaths({ FilePath(L"test/header/search/path") });
+ applicationSettings->setFrameworkSearchPaths({ FilePath(L"test/framework/search/path") });
+
+ generateAndCompareExpectedOutputForSonargraphProject(projectName, sourceGroupSettings, applicationSettings);
+ }
+
void test_sonargraph_project_with_java_modules_generates_expected_output()
{
const std::wstring projectName = L"JavaModules";
@@ -157,8 +172,13 @@ public:
{
result += L"\tIndexedPath: \"" + indexedPath.getRelativeTo(baseDirectory).wstr() + L"\"\n";
}
- for (const std::wstring& compilerFlag : indexerCommand->getCompilerFlags())
+ for (std::wstring compilerFlag : indexerCommand->getCompilerFlags())
{
+ FilePath flagAsPath(compilerFlag);
+ if (flagAsPath.exists())
+ {
+ compilerFlag = flagAsPath.getRelativeTo(baseDirectory).wstr();
+ }
result += L"\tCompilerFlag: \"" + compilerFlag + L"\"\n";
}
for (const FilePath& headerSearchPath : indexerCommand->getSystemHeaderSearchPaths())