1

我正在编写一个 ant 脚本来构建使用 IBM RAD 7.5 开发的 java 项目。

脚本正在调用 IBM RAD ant 扩展 API。我正在使用Task将项目集文件(*.psf)加载到内存中,并调用Task编译projectSetImport中列出的项目。

问题是psf文件中列出的项目不是按项目依赖排序的,编译时因为依赖不正确而失败。

是否有任何 API 或方法可以自动管理依赖关系?Iam 处理的 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 文件,依赖项目列在“项目”标签中。然后实现一个有向无环路径算法来重新排序依赖关系。这种方法可能过头了。这在构建 IBM java 项目的团队中一定是一个常见问题,有解决方案吗?

4

1 回答 1

0

最后,我编写了一些 python 代码来计算依赖关系。我列出了以下逻辑:

  1. 将psf文件读入列表,psf文件为xml文件,项目名在tag中。
  2. 对于列表中的每个项目,转到项目源代码并读取 .project 文件和 .classpath 文件,这两个文件包含依赖项项目。对于 .project 文件(xml),从标签中获取项目名称,对于 .classpath 文件。获取具有属性 kind='src' 的行
  3. 现在你得到了[source]->[dependened_project_list],实现一个有向无环映射。(见附件代码)
  4. 将 [source]->[dependened_project] 加载到 AdjecentListDigraph,并调用 topoSort() 以返回依赖项。
  5. 生成一个新的有序 psf 文件。

    /////////////////////// dap_graph.py/////////////////////////////
    # -*- coding: utf-8 -*-
    

    '''使用有向无环路径计算依赖''' class Vertex: def init (self, name): self._name = name self.visited = True

    类 InValidDigraphError(RuntimeError): def init (self, arg): self.args = arg

    class 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  
        '''
    

    print 'TODO: 验证这是一个连接的树'

        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///////////////////////
    

    - - 编码:utf-8 - -

    ''' 该模块将从 psf 中加载每个项目的依赖项,并计算有向无环路径。

    依赖项被加载到一个结构如下的映射中:dependency_map{"project_A":set(A1,A2,A3), "A1:set(B1,B2,B3)}

    算法是:1)读取 2)调用 readProjectDependency(project_name) ''' import os, xml.dom.minidom from utils.setting 导入配置

    类项目加载器:

    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
    
于 2013-05-22T19:30:53.000 回答