Как мне управлять зависимостью нескольких проектов (направленный ациклический путь) с помощью IBM RAD ant?
Я работаю над сценарием ant для создания java-проектов, разработанных с помощью IBM RAD 7.5.
Сценарий вызывает API-интерфейс расширения IBM RAD. Я использую Task для загрузки файла набора проектов (*. Psf) в память и вызываю Task для компиляции проектов, перечисленных в projectSetImport.
Проблема в том, что проекты, перечисленные в файле psf, не упорядочены по зависимости проекта, при компиляции происходит сбой, потому что зависимость неверна.
Есть ли какой-либо API или метод для автоматического управления зависимостями? Обработка I am в файлах 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". Затем реализуйте алгоритм направленного ациклического пути, чтобы изменить порядок зависимостей. Этот подход может быть более чем убить. Это должно быть распространенной проблемой в командах, создающих Java-проекты IBM, есть ли решение?
1 ответ
Наконец, я написал некоторый код на Python для вычисления зависимости. Я перечислил логику ниже:
- считайте файл psf в список, файл psf представляет собой файл xml, а имя проекта находится в теге.
- для каждого проекта в списке перейдите к исходному коду проекта и прочитайте файл.project и файл.classpath, эти два файла содержат проект зависимостей. для файла.project (xml) извлеките имя проекта из тега, для файла.classpath. получить строку с атрибутом kind='src'
- теперь у вас есть [source]->[dependened_project_list], реализуйте направленную ациклическую карту (см. прилагаемый код).
- загрузите [source]->[dependened_project] в AdjecentListDigraph и вызовите topoSort() для возврата зависимости.
создать новый упорядоченный файл PSF.
/////////////////////// dap_graph.py///////////////////////////// # -*- coding: utf-8 -*-
'' 'Использовать направленный ациклический путь для вычисления зависимости' '' Класс Vertex: def init(self, name): self._name = name self.visited = True
Класс 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 '''
выведите '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(имя_проекта) ''' 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