mirror of
https://github.com/SynologyOpenSource/pkgscripts-ng.git
synced 2025-07-20 18:20:23 +00:00
160 lines
5.4 KiB
Python
Executable File
160 lines
5.4 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# Copyright (c) 2000-2020 Synology Inc. All rights reserved.
|
|
|
|
from ProjectDepends import ProjectNode, DepGraph # use for pickle
|
|
import shelve
|
|
from collections import defaultdict
|
|
import os
|
|
import pickle
|
|
import argparse
|
|
import sys
|
|
|
|
|
|
def parse_args(args):
|
|
parser = argparse.ArgumentParser()
|
|
group = parser.add_mutually_exclusive_group()
|
|
parser.add_argument('--init', help='Init a project generator')
|
|
group.add_argument('-r', dest='reverse', action='store_false', help="Direct: reverse")
|
|
group.add_argument('-x', dest='traverse', action='store_true', help="Direct: traverse [Default]")
|
|
parser.add_argument('-c', dest='count', type=int, help="Depends level.")
|
|
parser.add_argument('--next', action='store_true', help='Get next build projects.')
|
|
parser.add_argument('--failed', action='store_true', help="Tag given project failed.")
|
|
parser.add_argument('--show', action='store_true', help="show build status.")
|
|
parser.add_argument('--purge', action='store_true', help="Purge this task.")
|
|
parser.add_argument('projects', nargs='*', help='Projects to serve')
|
|
|
|
args = parser.parse_args(args)
|
|
if args.init:
|
|
args.direct = args.reverse
|
|
|
|
return args
|
|
|
|
|
|
class GraphTraverser:
|
|
def __init__(self, direct, graph, builds):
|
|
self.direct = direct
|
|
self.graph = graph
|
|
self.builds = sorted(builds)
|
|
self.success = []
|
|
self.failed = []
|
|
self.skiped = defaultdict(set)
|
|
self.visit_unreachable_node()
|
|
|
|
# We need to update not built projects to `Success`
|
|
def visit_unreachable_node(self):
|
|
set_builds = set(self.builds)
|
|
for proj, node in self.graph.created_projects.items():
|
|
if not set(node.getProjectDepends(0)) & set_builds:
|
|
node.update_status(True)
|
|
|
|
def update_infected_projects_failed(self, depend_nodes, failed_root):
|
|
for dep_node in depend_nodes:
|
|
# If parent failed, we dont need to build all depend projects.
|
|
if dep_node.visited:
|
|
continue
|
|
|
|
dep_node.update_status(False)
|
|
|
|
if dep_node.proj in self.builds:
|
|
self.skiped[failed_root].add(dep_node.proj)
|
|
self.builds.remove(dep_node.proj)
|
|
|
|
self.update_infected_projects_failed(self.get_infected_projects(dep_node), failed_root)
|
|
|
|
def update_failed_projects(self, projs):
|
|
for proj in projs:
|
|
proj_node = self.graph.getProjectNode(proj)
|
|
proj_node.update_status(False)
|
|
self.failed.append(proj)
|
|
if proj in self.builds:
|
|
self.builds.remove(proj)
|
|
self.update_infected_projects_failed(self.get_infected_projects(proj_node), proj)
|
|
print('%s:%s' % (proj, ",".join(self.skiped[proj])))
|
|
|
|
def update_infected_projects_success(self, depend_nodes):
|
|
for dep_node in depend_nodes:
|
|
# We need to build depend proj later, dont update need built dep to success
|
|
if dep_node.visited or dep_node.proj in self.builds:
|
|
continue
|
|
|
|
if self.project_ready(dep_node):
|
|
dep_node.update_status(True)
|
|
self.update_infected_projects_success(self.get_infected_projects(dep_node))
|
|
|
|
def update_success_projects(self, projs):
|
|
for proj in projs:
|
|
proj_node = self.graph.getProjectNode(proj)
|
|
proj_node.update_status(True)
|
|
self.success.append(proj)
|
|
if proj in self.builds:
|
|
self.builds.remove(proj)
|
|
self.update_infected_projects_success(self.get_infected_projects(proj_node))
|
|
|
|
def get_infected_projects(self, proj_node):
|
|
if self.direct:
|
|
return proj_node.rev_depends
|
|
else:
|
|
return proj_node.depends
|
|
|
|
def project_ready(self, node):
|
|
if self.direct:
|
|
return all([dep_node.success for dep_node in node.depends])
|
|
else:
|
|
return all([rev_node.success for rev_node in node.rev_depends])
|
|
|
|
def get_next(self, count):
|
|
if not self.builds:
|
|
return ['NULL']
|
|
|
|
output = []
|
|
for proj in self.builds:
|
|
proj_node = self.graph.getProjectNode(proj)
|
|
if self.project_ready(proj_node):
|
|
output.append(proj_node.proj)
|
|
if len(output) >= count:
|
|
break
|
|
|
|
return output
|
|
|
|
def show(self):
|
|
if self.success:
|
|
print("\n[Success projects]")
|
|
print(" ".join(self.success))
|
|
|
|
if self.failed:
|
|
print("\n[Failed projects -> Skiped projects]")
|
|
for fail in self.failed:
|
|
print("%s -> %s" % (fail, " ".join(self.skiped[fail])))
|
|
print()
|
|
|
|
|
|
def main(args):
|
|
args = parse_args(args)
|
|
sys.setrecursionlimit(3000)
|
|
shelve_file = os.path.join('/', str(os.getppid()) + '.shelve')
|
|
|
|
with shelve.open(shelve_file, writeback=True) as sv:
|
|
if args.init:
|
|
with open(args.init, 'rb') as fd:
|
|
depends = pickle.load(fd)
|
|
sv['traverser'] = GraphTraverser(args.direct, depends, args.projects)
|
|
return
|
|
|
|
traverser = sv['traverser']
|
|
if args.next:
|
|
traverser.update_success_projects(args.projects)
|
|
print(" ".join(traverser.get_next(args.count)))
|
|
|
|
if args.failed:
|
|
traverser.update_failed_projects(args.projects)
|
|
|
|
if args.show:
|
|
traverser.show()
|
|
|
|
if args.purge:
|
|
os.remove(shelve_file)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv[1:])
|