背景
本来想解析VS的项目配置文件(*.vcxproj),配合cppclean来发现多余的#incldue
。
结果发现低估了难度,VS会间接引入许多目录。
略有不甘,暂且作为一个解析XML文件的示例。
代码
VSProjectParser.h
#include <QVector>
#include <memory>
class VSProjectParser {
public:
VSProjectParser (const QString &vsProjectFilePath);
QVector<QString> getIncludeDirectoriesRealPath ();
QVector<QString> getCppFilesRealPath ();
QVector<QString> getHeadFilesRealPath ();
private:
class Impl;
std::shared_ptr<Impl> m_impl = nullptr;
};
VSProjectParser.cpp
#include "VSProjectParser.h"
#include <QDebug>
#include <QDir>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNode>
#include <QFile>
#include <QFileInfo>
#include <QHash>
#include <memory>
class Document {
public:
Document (const QString &projFilePath);
QVector<QString> extractCppFiles ();
QVector<QString> extractHeadFiles ();
private:
void extractTasgInfo ();
QHash<QString, QVector<QString>> m_tagsInfo = {};
QString m_projFilePath = "";
};
class VSProjectParser::Impl {
public:
Impl (const QString &vsProjectFilePath) : m_vsProjectFileInfo (vsProjectFilePath), m_document (vsProjectFilePath)
{
}
QFileInfo m_vsProjectFileInfo = "";
Document m_document;
};
Document::Document (const QString &projFilePath) : m_projFilePath (projFilePath)
{
extractTasgInfo();
}
QVector<QString>
Document::extractCppFiles()
{
QVector<QString> cppTags {"ClCompile"};
QVector<QString> ret;
for (QString tag : cppTags) {
if (m_tagsInfo.contains (tag))
ret.append (m_tagsInfo.value (tag));
}
return ret;
}
QVector<QString>
Document::extractHeadFiles()
{
QVector<QString> headTags {"ClInclude", "QtMoc"};
QVector<QString> ret;
for (QString tag : headTags) {
if (m_tagsInfo.contains (tag))
ret.append (m_tagsInfo.value (tag));
}
return ret;
}
void
Document::extractTasgInfo()
{
QFile file (m_projFilePath);
if (!file.open (QIODevice::ReadOnly)) {
qDebug() << "Failed to open the file.";
return;
}
QDomDocument doc;
if (!doc.setContent (&file)) {
qDebug() << "Failed to parse the file into a DOM tree.";
return;
}
file.close();
QDomElement root = doc.documentElement();
QHash<QString, QVector<QString>> tags_with_include_attribute;
constexpr auto ATTR_INCLUDE = "Include";
QDomNodeList itemGroups = root.elementsByTagName ("ItemGroup");
for (int i = 0; i < itemGroups.count(); ++i) {
QDomNode itemGroup = itemGroups.at (i);
QDomNodeList children = itemGroup.childNodes();
for (int j = 0; j < children.count(); ++j) {
QDomNode child = children.at (j);
if (child.isElement()) {
QDomElement element = child.toElement();
QString tagName = element.tagName();
tags_with_include_attribute[tagName].push_back (element.attribute (ATTR_INCLUDE));
}
}
}
QVector<QString> keys;
m_tagsInfo = tags_with_include_attribute;
}
VSProjectParser::VSProjectParser (const QString &vsProjectFilePath) : m_impl (new Impl (vsProjectFilePath))
{
}
QVector<QString>
VSProjectParser::getIncludeDirectoriesRealPath()
{
QVector<QString> headers = getHeadFilesRealPath();
QVector<QString> ret;
for (QString &file : headers) {
QFileInfo fileInfo (file);
if (!fileInfo.exists()) {
qDebug() << __FUNCTION__ << "File not found:" << file;
continue;
}
ret.append (fileInfo.dir().absolutePath());
}
return ret;
}
QVector<QString>
VSProjectParser::getCppFilesRealPath()
{
QVector<QString> includes = m_impl->m_document.extractCppFiles();
QVector<QString> ret;
for (QString &file : includes) {
file = m_impl->m_vsProjectFileInfo.dir().absolutePath() + "/" + file;
QFileInfo fileInfo (file);
if (fileInfo.exists())
ret.append (fileInfo.absoluteFilePath());
else
qDebug() << __FUNCTION__ << "File not found:" << file;
}
return ret;
}
QVector<QString>
VSProjectParser::getHeadFilesRealPath()
{
QVector<QString> includes = m_impl->m_document.extractHeadFiles();
QVector<QString> ret;
for (QString &file : includes) {
file = m_impl->m_vsProjectFileInfo.dir().absolutePath() + "/" + file;
QFileInfo fileInfo (file);
if (fileInfo.exists())
ret.append (fileInfo.absoluteFilePath());
else
qDebug() << __FUNCTION__ << "File not found:" << file;
}
return ret;
}