我正在开发一个ant脚本来构建使用IBM RAD 7.5开发的java prject。
一个脚本正在调用IBM RAD ant extenstion API。我使用Task将项目集文件(* .psf)加载到内存中,并调用Task来编译projectSetImport中列出的项目。
问题是psf文件中列出的项目没有按项目依赖项排序,编译时失败,因为依赖项不正确。
是否有任何API或方法可以自动管理依赖项?我处理的psf文件相当大,每个文件中有200多个项目,并且它正在不断变化(例如,一些项目被删除,每周都会添加一些新项目)
这里是问题的详细说明: 项目依赖性如下: 1)项目A取决于B和D. 2)项目B取决于C 3)项目E取决于F
A -> B -> C
A -> D
E-> F
sample.psf文件只列出了所有项目:
A
B
C
D
E
F
加载sample.psf,它有一个项目列表[A,B,C,D,E,F] 从中构建项目列表 构建在A处失败,因为A需要首先构建B和D.
我目前的解决方案是手动重建sample.psf,例如 sample.psf文件:
C
B
D
A
F
E
但这很难维护,因为psf文件中有200多个项目,而且它们正在不断变化。
解决此问题的一种方法是编写解析器来读取每个项目的.project文件,依赖项目列在“projects”标记中。然后实现Directed非循环路径算法以重新排序依赖项。这种方法可能会过度杀戮。这必须是团队构建IBM java项目的常见问题,有解决方案吗?
答案 0 :(得分:0)
最后,我编写了一些python代码来计算依赖关系。我列出了以下逻辑:
生成一个新的有序psf文件。
/////////////////////// dap_graph.py/////////////////////////////
# -*- coding: utf-8 -*-
'''使用有向非循环路径计算依赖关系''' class Vertex: def init (self,name): self._name = name self.visited = True
class InValidDigraphError(RuntimeError): def init (self,arg): self.args = arg
类AdjecentListDigraph: '''代表相邻列表'''
的有向图def __init__(self):
'''use a table to store edges,
the key is the vertex name, value is vertex list
'''
self._edge_table = {}
self._vertex_name_set = set()
def __addVertex(self, vertex_name):
self._vertex_name_set.add(vertex_name)
def addEdge(self, start_vertex, end_vertex):
if not self._edge_table.has_key(start_vertex._name):
self._edge_table[start_vertex._name] = []
self._edge_table[start_vertex._name].append(end_vertex)
# populate vertex set
self.__addVertex(start_vertex._name)
self.__addVertex(end_vertex._name)
def getNextLeaf(self, vertex_name_set, edge_table):
'''pick up a vertex which has no end vertex. return vertex.name.
algorithm:
for v in vertex_set:
get vertexes not in edge_table.keys()
then get vertex whose end_vertex is empty
'''
leaf_set = vertex_name_set - set(edge_table.keys())
if len(leaf_set) == 0:
if len(edge_table) > 0:
raise InValidDigraphError("Error: Cyclic directed graph")
else:
vertex_name = leaf_set.pop()
vertex_name_set.remove(vertex_name)
# remove any occurrence of vertext_name in edge_table
for key, vertex_list in edge_table.items():
if vertex_name in vertex_list:
vertex_list.remove(vertex_name)
# remove the vertex who has no end vertex from edge_table
if len(vertex_list) == 0:
del edge_table[key]
return vertex_name
def topoSort(self):
'''topological sort, return list of vertex. Throw error if it is
a cyclic graph'''
sorted_vertex = []
edge_table = self.dumpEdges()
vertex_name_set = set(self.dumpVertexes())
while len(vertex_name_set) > 0:
next_vertex = self.getNextLeaf(vertex_name_set, edge_table)
sorted_vertex.append(next_vertex)
return sorted_vertex
def dumpEdges(self):
'''return the _edge_list for debugging'''
edge_table = {}
for key in self._edge_table:
if not edge_table.has_key(key):
edge_table[key] = []
edge_table[key] = [v._name for v in self._edge_table[key]]
return edge_table
def dumpVertexes(self):
return self._vertex_name_set
//////////////////////projects_loader.py///////////////////////
''” 该模块将从psf和compute中加载每个项目的依赖项 有向无环路径。
依赖关系被加载到如下结构的地图中: dependency_map { “project_A”:设置(A1,A2,A3), “A1:设置(B1,B2,B3)}
算法是: 1)阅读 2)调用readProjectDependency(project_name) “”” import os,xml.dom.minidom 来自utils.setting导入配置
类ProjectsLoader:
def __init__(self, application_name):
self.dependency_map = {}
self.source_dir = configuration.get('Build', 'base.dir')
self.application_name = application_name
self.src_filter_list = configuration.getCollection('psf',\
'src.filter.list')
def loadDependenciesFromProjects(self, project_list):
for project_name in project_list:
self.readProjectDependency(project_name)
def readProjectDependency(self, project_name):
project_path = self.source_dir + '\\' + self.application_name + '\\'\
+ project_name
project_file_path = os.path.join(project_path,'.project')
projects_from_project_file = self.readProjectFile(project_file_path)
classpath_file_path = os.path.join(project_path,'.classpath')
projects_from_classpath_file = self.\
readClasspathFile(classpath_file_path)
projects = (projects_from_project_file | projects_from_classpath_file)
if self.dependency_map.has_key(project_name):
self.dependency_map[project_name] |= projects
else:
self.dependency_map[project_name] = projects
def loadDependencyByProjectName(self, project_name):
project_path = self.source_dir + '\\' + self.application_name + '\\'\
+ project_name
project_file_path = os.path.join(project_path,'.project')
projects_from_project_file = self.readProjectFile(project_file_path)
classpath_file_path = os.path.join(project_path,'.classpath')
projects_from_classpath_file = self.\
readClasspathFile(classpath_file_path)
projects = list(set(projects_from_project_file\
+ projects_from_classpath_file))
self.dependency_map[project_name] = projects
for project in projects:
self.loadDependencyByProjectName(project)
def readProjectFile(self, project_file_path):
DOMTree = xml.dom.minidom.parse(project_file_path)
projects = DOMTree.documentElement.getElementsByTagName('project')
return set([project.childNodes[0].data for project in projects])
def readClasspathFile(self, classpath_file_path):
dependency_projects = set([])
if os.path.isfile(classpath_file_path):
DOMTree = xml.dom.minidom.parse(classpath_file_path)
projects = DOMTree.documentElement.\
getElementsByTagName('classpathentry')
for project in projects:
if project.hasAttribute('kind') and project.getAttribute\
('kind') == 'src' and project.hasAttribute('path') and \
project.getAttribute('path') not in self.src_filter_list:
project_name = project.getAttribute('path').lstrip('/')
dependency_projects.add(project_name)
return dependency_projects
def getDependencyMap(self):
return self.dependency_map