图形化VS201x工程中的项目依赖关系

目录

1. 背景

2. 入手

2.1 分析sln文件

2.2 给出正则表达式

3 程序

4. demo

5. 补充 - 另外一种情况

6. 补充 - 完整代码


1. 背景

       初次接手一个大工程时, 往往因为复杂的项目依赖而遇到各种编译问题, 同时如果能图形化其中的依赖关系对理解整个项目有莫大的帮助, 就像能从山顶俯瞰大陆的感觉. 本文即着重于实现此功能. 
 

2. 入手

2.1 分析sln文件

下面给出了两个Project, 其中project1的GUID为E37ACEB3-9187-43F7-AA78-000000000001

project2的GUID为E37ACEB3-9187-43F7-AA78-000000000002, 

postProject部分给出了依赖关系: project2依赖于project1

Project("{2150E333-AAAA-42A3-9474-1A3956D46DE9}") = "project1", "project1.vcxproj", "{E37ACEB3-9187-43F7-AA78-000000000001}"
EndProject

Project("{2150E333-AAAA-42A3-9474-1A3956D46DE9}") = "project2", "project2.vcxproj", "{E37ACEB3-9187-43F7-AA78-000000000002}"
	ProjectSection(ProjectDependencies) = postProject
		{E37ACEB3-9187-43F7-AA78-000000000001} = {E37ACEB3-9187-43F7-AA78-000000000001}
	EndProjectSection
EndProject

2.2 给出正则表达式

Python正则表达式解析project: r'Project[^=]+=\s*"([^"]+)"\s*,\s*"([^"]+)"\s*,\s*"\{([^"]+)\}"'

                   group(1)为project_name, group(2)为project_vcproj, group(3)为GUID

Python正则表达式解析postProject: r'\{([\w-]+)\}\s=\s\{([\w-]+)\}'

                   group(1)应该等于group(2), 都是依赖的项目的GUID.

3 程序

      请见visual_Project_dependency-Python工具类资源-CSDN下载

4. demo

      

5. 补充 - 另外一种情况

还有一种依赖关系保存在project文件里,形式如下:

<ProjectReference Include="..\MsgCreator\MsgCreator.vcxproj">
      <Project>{d3eada85-50f9-4a1d-a2f7-18612358b436}</Project>
      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>

表示当前工程依赖MsgCreator. vcxproj其实是个XML文件,直接用BeautifulSoup打开查找<Project>项即可。

6. 补充 - 完整代码

#!/usr/bin/python
# -*- coding: gb2312 -*-

#########################################################################
#2022-09-24 support case 2: denpendency in vcxproj.
#2018-08-01 born 
########################################################################

import sys,re

from bs4 import BeautifulSoup
import os
#import os for opening project file to search below kind of dependencies(case 2).
#<ProjectReference Include="..\MsgCreator\MsgCreator.vcxproj">
#      <Project>{d3eada85-50f9-4a1d-a2f7-18612358b436}</Project>
#      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
#</ProjectReference>

projectsCommentMap = {
	'project1':'comment1'
	,'project2': r'comment2: <BR/>aaa'
}

class Project:
	def __init__(self, projectStr, slnpath):
		self.comments = ''
		projectNameGUIDpattern = r'Project[^=]+=\s*"([^"]+)"\s*,\s*"([^"]+)"\s*,\s*"\{([^"]+)\}"'
		m = re.search(projectNameGUIDpattern,projectStr)
		self.name = m.group(1)
		self.GUID = m.group(3).upper()
		self._fullpath = os.path.join(slnpath,m.group(2))

		#case 1: dependency specified in solution file.
		projectDependenciesPattern = r'\{([\w-]+)\}\s=\s\{([\w-]+)\}'
		m = re.findall(projectDependenciesPattern,projectStr)
		self.dependencyList = []
		for match in m:
			if(match[0]==match[1]):
				upperDep = match[0].upper()
				self.dependencyList.append(upperDep)

		#case 2: denpendency specified in specific project file.
		# <ProjectReference Include="..\MsgCreator\MsgCreator.vcxproj">
		#      <Project>{d3eada85-50f9-4a1d-a2f7-18612358b436}</Project>
		#      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
		# </ProjectReference>
		with open(self._fullpath, "r") as file:
			contents = file.read()
			projxml = BeautifulSoup(contents, "xml")
			find_depen = projxml.find_all('Project')
			for dep in find_depen:
				if dep.parent and dep.parent.name=='ProjectReference':
					parentProjGUID = dep.text.strip('{}')
					self.dependencyList.append(parentProjGUID.upper())

		self.dependencyProjects = []

	def __hash__(self):
		return hash(self.GUID)
	def __eq__(self,other):
		if(self.name==other.name and  self.GUID==other.GUID):
			return True
		return False
	
	
new_node_nocomment = '''%d [shape=none label=<<table border="0" cellspacing="0"><tr><td port="port1" border="1" color="red"><B>%s</B></td></tr></table>>]\n'''
new_node_withcomment = '''%d [shape=none label=<<table border="0" cellspacing="0"><tr><td port="port1" border="1" bgcolor="green"><B>%s</B></td></tr><tr><td port="port2" border="1">%s</td></tr></table>>]\n'''
if __name__ == '__main__':
	'''Usage: this_file 2010_sln_file'''
	slnFileName = sys.argv[1]
	#slnFileName = r'C:\cpp\xalan-c-Xalan-C_1_11_0\Projects\Win32\VC15\Xalan.sln'
	import os
	slnpath = os.path.dirname(slnFileName)

	slnFile = open(slnFileName,'r')
	slnStr = slnFile.read()
	print(len(slnStr))
	
	projectStartPos = 0
	projectEndPos = 0
	PROJECT_START_CONSTANT = 'Project("{'
	PROJECT_END_CONSTANT = 'EndProject'
	project_list = []
	while(1):
		projectStartPos = slnStr.find(PROJECT_START_CONSTANT,projectStartPos)
		if(projectStartPos<0):
			break;
		projectEndPos = slnStr.find(PROJECT_END_CONSTANT,projectStartPos)
		if(projectEndPos<0):
			print('NO EndProject closes the project')
			break;
		project_string = slnStr[projectStartPos:projectEndPos]
		project_list.append(Project(project_string, slnpath))
		
		projectStartPos = projectEndPos
		
	print("Total# of projects: %d"%(len(project_list)))
	
	
	guid2ProjectMap = {}
	for project in project_list:
		guid2ProjectMap[project.GUID] = project
	#update dependencyProjects
	for project in project_list:
		for dependGuid in project.dependencyList:
			if dependGuid in guid2ProjectMap:
				project.dependencyProjects.append(guid2ProjectMap[dependGuid])
			else:
				print('project %s depend on a project not in solution:%s'%(project.name, dependGuid))
	
	#generate DOT file
	dot_file_name = 'temp.dot'
	dot = open(dot_file_name,'w+')
	dot.write("digraph Tree {\n")
	#dot.write('node [shape=record color=blue fontname="bold"] ;\n')
	#generate ID for project
	project2IDMap = {}
	refProjectList = set([])
	i = 0
	for project in project_list:
		project2IDMap[project]=i
		i+=1;
	for project in project_list:
		if(len(project.dependencyProjects)>0):
			refProjectList.add(project)
			for dependProject in project.dependencyProjects:
				refProjectList.add(dependProject)
				
	singlefile = open('single.txt','w+')
	for project in project_list:
		if(project in refProjectList):
			if(project.name in projectsCommentMap):
				projectcomments = projectsCommentMap[project.name]
				dot.write(new_node_withcomment%(project2IDMap[project], project.name,projectcomments))
			else:
				dot.write(new_node_nocomment%(project2IDMap[project], project.name))
			if(len(project.dependencyProjects)>0):
				for depend_project in project.dependencyProjects:
					dot.write("%d -> %d ;\n"%(project2IDMap[depend_project],project2IDMap[project]))
		else:
			singlefile.write(project.name)
			singlefile.write('\n')
	dot.write("}")
	dot.close()
	singlefile.close()
	

最后附一张开源库Xalan的项目依赖关系图:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深山老宅

鸡蛋不错的话,要不要激励下母鸡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值