#!/usr/bin/env python import abc import os import os.path import re import sys import yaml CACHE_DIR = os.path.join(os.path.abspath(os.path.curdir), '.ae') ROOT_DIR = os.path.join(CACHE_DIR, 'root') # We'll need these everywhere basically os.putenv('SCHEMATIC_INSTALL_PREFIX', ROOT_DIR) os.putenv('SCHEMATIC_PARALLELISM', '1') # Put our own ROOT_DIR bits in the path first to make sure `ae run` can execute # schematic installed binaries os.environ['PATH'] = os.pathsep.join([os.path.join(ROOT_DIR, 'bin'), os.environ['PATH']]) def read_pipe(name, pipe): while True: line = pipe.readline() if not line: break sys.stdout.write("[%s] %s" % (name, line)) sys.stdout.flush() class SchematicHandler: def __init__(self, schematic): self.schematic = schematic self.name = schematic['name'] def bootstrap(self): if not os.path.isdir(CACHE_DIR): os.mkdir(CACHE_DIR) return self def checkout(self): print '>> checkout' url = self.schematic['source']['url'] branch = self.schematic['source']['ref'] or 'master' repos_dir = os.path.join(CACHE_DIR, 'repos') self.checkout_dir = os.path.join(repos_dir, self.name) if not os.path.isdir(repos_dir): os.mkdir(repos_dir) if not os.path.isdir(self.checkout_dir): command = "git clone --depth 1 %s %s" % (url, self.checkout_dir) else: command = "cd %s && git fetch" % (self.checkout_dir) read_pipe(self.name, os.popen(command)) read_pipe(self.name, os.popen("cd %s && git checkout origin/%s" % (self.checkout_dir, branch))) return self def prepare(self): print '>> prepare' steps = self.schematic['prepare'] or [] self.__run_steps__(steps) return self def build(self): print '>> build' steps = self.schematic['build'] or [] self.__run_steps__(steps) return self def install(self): print '>> install' steps = self.schematic['install'] or [] self.__run_steps__(steps) return self def __run_steps__(self, steps): for s in steps: read_pipe(self.name, os.popen("cd %s && %s" % (self.checkout_dir, s))) def traverse_schematic_graph(graph, key=None): if not key: install = [] for key in graph.iterkeys(): result = traverse_schematic_graph(graph, key) if result: install.append(result) install.append(key) return install if not graph.has_key(key): return key for key in graph[key]: return traverse_schematic_graph(graph, key) class Catalog(object): def __init__(self, uri): self.uri = uri self.schematics = {} def has_schematic(self, schematic): if self.schematics.has_key(schematic): return self.schematics[schematic] return None class FileCatalog(Catalog): def load(self): path = re.split('file:\/\/', self.uri)[1] catalog_dir = os.path.join(path, 'catalog') if not os.path.isdir(catalog_dir): return for root, dirs, files in os.walk(catalog_dir): for f in files: if not f.endswith('.yaml'): continue publisher = os.path.basename(root) name = re.sub('\.yaml', '', f) qualified_name = '/'.join([publisher, name]) self.schematics[qualified_name] = yaml.load(file(os.path.join(root, f))) def __str__(self): return "FileCatalog(%s)" % self.uri class GitCatalog(FileCatalog): def load(self): repo_dir = os.path.join(CACHE_DIR, 'repos') repo = re.match('git:\/\/(.*)\/(.*)\.git', self.uri).group(2) full_path = os.path.join(repo_dir, repo) if os.path.isdir(full_path): read_pipe('git', os.popen('cd %s && git pull --rebase' % full_path)) else: read_pipe('git', os.popen('cd %s && git clone --depth 1 %s' % (repo_dir, self.uri))) self.uri = ''.join(['file://', full_path]) return super(GitCatalog, self).load() def install(): if not os.path.isfile('schematics.yaml'): return 1 schematics = yaml.load(file('schematics.yaml')) catalogs = [] for catalog in schematics['catalogs']: c = None if re.match('file:\/\/', catalog): c = FileCatalog(catalog) elif re.match('git:\/\/', catalog): c = GitCatalog(catalog) c.load() catalogs.append(c) graph = {} todo = {} dependencies = [] for schematic in schematics['schematics']: components = schematic.split('@') version = None schematic = components[0] if len(components) == 2: version = components[1] for catalog in catalogs: graph[schematic] = [] s = catalog.has_schematic(schematic) if not s: print "!!! Could not find schematic for %s !!!" % schematic return 1 todo[schematic] = SchematicHandler(s) depends = s['schematics'] or [] graph[schematic].extend(depends) dependencies.extend(depends) for schematic in dependencies: for catalog in catalogs: s = catalog.has_schematic(schematic) if not s: print "!!! Could not find schematic for %s !!!" % schematic return 1 todo[schematic] = SchematicHandler(s) depends = s['schematics'] or [] for d in depends: if not graph.has_key(d): graph[d] = [] graph[d].append(schematic) for schematic in traverse_schematic_graph(graph): print "Installing schematic %s" % schematic todo[schematic].bootstrap().checkout().prepare().build().install() return 0 def run(): if len(sys.argv) < 3: print "Must specify a command to run" return -1 env = {} for e in os.environ: env[e] = os.environ[e] project_path = os.path.join(ROOT_DIR, 'lib', 'gnat') key = 'ADA_PROJECT_PATH' if os.environ.has_key(key): env[key] = ':'.join([project_path, os.environ[key]]) else: env[key] = project_path key = 'GPR_PROJECT_PATH' if os.environ.has_key(key): env[key] = ':'.join([project_path, os.environ[key]]) else: env[key] = project_path key = 'ADA_INCLUDE_PATH' include_path = os.path.join(ROOT_DIR, 'include') if os.environ.has_key(key): env[key] = ':'.join([include_path, os.environ[key]]) else: env[key] = include_path return os.execvpe(sys.argv[2], sys.argv[2:], env) def main(): if len(sys.argv) > 1: if sys.argv[1] == 'install': sys.exit(install()) if sys.argv[1] == 'run': sys.exit(run()) else: print "Welcome to the Analytical Engine" if __name__ == '__main__': main()