写一个小工具来可视化VisualStudio中项目之间的依赖关系(2.补全vcxproj中的信息)

目标

在上一篇博客《写一个小工具来可视化VisualStudio中项目之间的依赖关系(1.针对sln中的信息)》中,我通过实验的方式知道了.sln文件中是如何存储依赖关系的,并编写了代码解析它。但问题是之后发现在.vcxproj中也有依赖信息。

本篇的目标是:

  • 解析出.vcxproj中的项目依赖关系,与之前从.sln得到的信息结合,最终得到完整项目依赖图。

分析

经过上一篇的观察,我知道两个关键信息:

(1).vcxproj的格式是xml,因此我需要一个读取xml的第三方库来帮助我,这里我选择leethomason/tinyxml2。下载其中的tinyxml2.htinyxml2.cpp添加入工程。

(2).vcxprojProjectReference节点将存储了其依赖的项目的信息
在这里插入图片描述

代码

代码上不复杂,主要就是遍历文件夹下所有的vcxproj文件,然后找到对应的节点,进而找到依赖信息。

完整代码如下:

#include <iostream>
#include <vector>
#include <map>
#include <io.h>
#include <algorithm>

#include"tinyxml2/tinyxml2.h"

using namespace std;
using namespace tinyxml2;

//一个项目所对应的信息
struct ProjectInfo
{
    
    
    //读取到的每一行信息
    vector<string> lines;

    //项目名字
    string Name;
    //项目GUID
    string GUID;

    //所依赖的项目
    vector<ProjectInfo* >DependProjects;
};

//从字符串中分割得到信息的辅助函数
vector<string> StringSplitter(string source, char startChar, char endChar)
{
    
    
    vector<string> result;
    string current;
    bool working = false;
    for (int i = 0; i < source.size(); i++)
    {
    
    
        if (working)
        {
    
    
            if (source[i] == endChar)
            {
    
    
                result.push_back(current);
                current.clear();
                working = false;
            }
            else
                current.push_back(source[i]);
        }          
        else if (source[i] == startChar)
            working = true;        
    }

    return result;
}

//递归地方式找到所有的文件
void GetFilesInFolder_recursion(string folder, vector<string>& files)
{
    
    
    _finddata_t fileInfo;	//储存得到的文件信息
    //找第一个
    auto findResult = _findfirst((folder + "\\*").c_str(), &fileInfo);
    if (findResult == -1)
    {
    
    
        _findclose(findResult);
        return;
    }
    //找之后的
    do
    {
    
    
        string fileName = fileInfo.name;
        if ((fileInfo.attrib == _A_NORMAL) || (fileInfo.attrib == _A_ARCH))  //是文件
        {
    
    
            files.push_back(folder + "/" + fileName);
        }
        else if (fileInfo.attrib == _A_SUBDIR)//是文件夹
        {
    
    
            if (fileName == ".")	//跳过得到的这个路径
                continue;
            if (fileName == "..")	//跳过得到的这个路径
                continue;
            GetFilesInFolder_recursion(folder + "/" + fileName, files);
        }
    } while (_findnext(findResult, &fileInfo) == 0);

    _findclose(findResult);
}

//找到一个节点的所有子节点
vector<XMLElement*> FindAllChildrenElement(XMLElement* Parent)
{
    
    
    vector<XMLElement*> result;

    XMLElement* child = Parent->FirstChildElement();
    if (child == nullptr)
        return result;

    while (true)
    {
    
    
        result.push_back(child);

        if (child == Parent->LastChildElement())
            break;
        
        child = child->NextSiblingElement();
    }
    
    return result;
}

int main()
{
    
    
    //.sln文件的路径
    const string slnFile = "D:/000_NewNewWorkSpace/renderdoc/renderdoc.sln";
    //.vcxproj所在的文件夹
    const string vcxprojFolder = "D:/000_NewNewWorkSpace/renderdoc";

    //读取文件
    FILE* file;
    {
    
    
        fopen_s(&file, slnFile.c_str(), "r");
        if (!file)
        {
    
    
            cout << "打开文件失败" << endl;
            return -1;
        }
    }

    //读取所有的项目信息
    vector<ProjectInfo> projects;
    {
    
    
        ProjectInfo* CurrentProject = nullptr;//当前正在添加信息的项目

         //读取每一行
        char buff[1024];
        while (fgets(buff, 1024, file) != nullptr)
        {
    
    
            string line = buff;

            if (line.find("Project") == 0)//如果找到"Project"且是在开头,则说明是新项目
            {
    
    
                projects.push_back(ProjectInfo());
                CurrentProject = &projects[projects.size() - 1];
            }

            if (CurrentProject != nullptr)//如果当前项目不为空,则添加此行到项目信息中
                CurrentProject->lines.push_back(line);
            
            if (line.find("EndProject") == 0)//如果找到"EndProject"且是在开头,则说明项目结束
                CurrentProject = nullptr;
        }
    }

    //一个Map用来映射项目的GUID与项目
    std::map<string, ProjectInfo*> ProjectGUIDMapping;

    //遍历每一个项目找到其名字与编号
    for (ProjectInfo& p : projects)
    {
    
    
        //Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "renderdocshim", "renderdocshim\renderdocshim.vcxproj", "{6DEE3F12-F2F8-42CA-865A-578D0FD11387}"
        auto SubStrs = StringSplitter(p.lines[0],'\"', '\"');
        
        p.Name = SubStrs[1];

        p.GUID = StringSplitter(SubStrs[3], '{', '}')[0];

        ProjectGUIDMapping[p.GUID] = &p;
    }

    //遍历每一个项目找到其依赖的项目
    for (ProjectInfo& p : projects)
    {
    
    
        bool DependenciesLines = false;

        for (auto line : p.lines)
        {
    
    
            if (DependenciesLines)
            {
    
    
                if (line.find("EndProjectSection") != string::npos)//如果找到"EndProjectSection"则结束写依赖的项目
                    DependenciesLines = false;
                else    //这是一条表示依赖的行
                {
    
    
                    //依赖的项目的GUID
                    string DpdProjGUID = StringSplitter(line, '{', '}')[0];
                    //加入此条依赖信息
                    if (std::find(p.DependProjects.begin(), p.DependProjects.end(), ProjectGUIDMapping[DpdProjGUID]) == p.DependProjects.end())
                        p.DependProjects.push_back(ProjectGUIDMapping[DpdProjGUID]);
                }
            }

            if (line.find("ProjectSection(ProjectDependencies) = postProject") != string::npos)//如果找到"ProjectSection(ProjectDependencies) = postProject"则开始写依赖的项目
                DependenciesLines = true;
        }
    }

    //遍历找到所有的vcxproj文件
    vector<string> vcxprojs;
    {
    
    
        vector<string> files;
        GetFilesInFolder_recursion(vcxprojFolder, files);
        for (auto f : files)
        {
    
    
            if (f.find(".vcxproj") == (f.size() - string(".vcxproj").size()))//只针对.vcxproj为后缀的
                vcxprojs.push_back(f);
        }
    }

    //遍历每一个vcxproj文件
    for (auto f : vcxprojs)
    {
    
    
        //从文件中读取
        XMLDocument doc;
        doc.LoadFile(f.c_str());

        //根节点
        auto ProjectNode = doc.FirstChildElement("Project");

        //找到此项目的GUID
        string MyGUID;
        for (auto ProjectNode_child : FindAllChildrenElement(ProjectNode))
        {
    
    
            if (string(ProjectNode_child->Name()).compare("PropertyGroup") == 0)
            {
    
    
                auto label = ProjectNode_child->FindAttribute("Label");

                if (label && (string(label->Value()).compare("Globals") == 0))
                {
    
    
                    MyGUID = StringSplitter(string(ProjectNode_child->FirstChildElement("ProjectGuid")->GetText()), '{', '}')[0];
                }
            }
        }

        //找到依赖项
        for (auto ProjectNode_child : FindAllChildrenElement(ProjectNode))
        {
    
    
            if (string(ProjectNode_child->Name()).compare("ItemGroup") == 0)
            {
    
    
                for (auto ItemGroup_child : FindAllChildrenElement(ProjectNode_child))
                {
    
    
                    if (string(ItemGroup_child->Name()).compare("ProjectReference") == 0)
                    {
    
    
                        auto DpdProject = ItemGroup_child->FirstChildElement("Project");
                        if (DpdProject != nullptr)
                        {
    
    
                            //依赖的项目的GUID
                            string DpdProjGUID = StringSplitter(string(DpdProject->GetText()), '{', '}')[0];
                            //变为大写
                            std::transform(DpdProjGUID.begin(), DpdProjGUID.end(), DpdProjGUID.begin(), ::toupper);
                            //加入此条依赖信息
                            if (std::find(ProjectGUIDMapping[MyGUID]->DependProjects.begin(), ProjectGUIDMapping[MyGUID]->DependProjects.end(), ProjectGUIDMapping[DpdProjGUID]) == ProjectGUIDMapping[MyGUID]->DependProjects.end())
                                ProjectGUIDMapping[MyGUID]->DependProjects.push_back(ProjectGUIDMapping[DpdProjGUID]);
                        }
                    }
                }
            }
        }
    }    

    //画Mermaid图
    for (auto p : projects)
    {
    
    
        for (auto d : p.DependProjects)
            cout << p.Name << "-->" << d->Name << endl;
    }
}

测试

测试和上一次一样,还是 baldurk / renderdoc

qrenderdoc
renderdoc
version
crash_generation_server
crash_generation_client
exception_handler
breakpad_common
NV
d3d11
d3d12
d3d8
d3d9
dxgi
gl
AMD
ARM
Intel
vulkan
renderdoccmd
spirv
dxbc
dxil
pyrenderdoc_module
qrenderdoc_module

现在的依赖关系应该是完整的了。

猜你喜欢

转载自blog.csdn.net/u013412391/article/details/113796790
今日推荐