@@ 1,122 @@
+#!/usr/bin/env python3
+
+import argparse
+import yaml
+import git
+from pathlib import Path
+import shutil
+import sys
+
+def repo_name_from_url(url):
+ print(url)
+ lastslash = url.rfind('/')
+ if lastslash < 0:
+ raise Exception('Malformed url')
+ lastslash = lastslash + 1
+
+ end = url.rfind('.git')
+ if end < 0:
+ return url[lastslash:]
+
+ return url[lastslash:end]
+
+
+def parse_config_file(config):
+ with open(config, 'r') as configfile:
+ config = yaml.safe_load(configfile)
+
+ # schemas are cool, but let's keep it simple for now
+ if not 'repositories' in config:
+ raise Exception('Configuration file invalid: repositories not defined')
+
+ repositories = config['repositories']
+ if not all(['url' in repo for repo in repositories]):
+ raise Exception('Configuration file invalid: not all repositories have url')
+
+ return config
+
+
+# https://chase-seibert.github.io/blog/2014/03/21/python-multilevel-argparse.html
+class SrcTree(object):
+ def __init__(self):
+ parser = argparse.ArgumentParser(
+ prog = 'src-tree',
+ description = 'A simple script to setup and build source tree',
+ usage = 'src-tree <command> [<args>]')
+ parser.add_argument('command')
+
+ args = parser.parse_args(sys.argv[1:2])
+ if not hasattr(self, args.command):
+ print('Unrecognized command')
+ parser.print_help()
+ exit(1)
+ getattr(self, args.command)()
+
+ def clone(self):
+ parser = argparse.ArgumentParser(
+ description='Clone source tree consisting of multiple repos as described by config file')
+ parser.add_argument('config')
+ parser.add_argument('--root')
+ args = parser.parse_args(sys.argv[2:])
+
+ config = parse_config_file(args.config)
+
+ root = Path(args.root) if args.root is not None else Path.cwd()
+
+ for repo in config['repositories']:
+ url = repo['url']
+ name = repo['name'] if 'name' in repo else repo_name_from_url(url)
+ branch = repo['branch'] if 'branch' in repo else 'trunk'
+
+ cloneto = root / name
+ git.Repo.clone_from(url, cloneto, single_branch = True, branch = branch)
+
+ def link(self):
+ parser = argparse.ArgumentParser(
+ description='Link build directory of multiple repos as described by config file')
+ parser.add_argument('config')
+ parser.add_argument('--builddir', required=True)
+ parser.add_argument('--targetdir')
+ parser.add_argument('--root')
+ parser.add_argument('--force', default = False)
+
+ args = parser.parse_args(sys.argv[2:])
+
+ config = parse_config_file(args.config)
+
+ root = Path(args.root) if args.root is not None else Path.cwd()
+ targetdir = root / (args.targetdir if args.targetdir is not None else args.builddir)
+ targetdir.mkdir(parents = True, exist_ok = True)
+
+ for repo in config['repositories']:
+ name = repo['name'] if 'name' in repo else repo_name_from_url(repo['url'])
+ dir = root / name / args.builddir
+ print(name)
+ print('{} => {}', repo['url'], dir)
+
+ if dir.is_symlink():
+ dir.unlink(missing_ok=True)
+
+ if dir.exists() and not dir.is_dir():
+ raise Exception(f'builddir=\'{dir}\' is not a directory')
+
+ if dir.is_dir():
+ count = 0
+ for _ in dir.iterdir():
+ count = count + 1
+ if count == 0:
+ dir.rmdir()
+ elif not dir.args.force:
+ raise Exception(f'builddir=\'{dir}\' is not a symlink')
+ else:
+ print(f'Warning: builddir=\'{dir}\' is not a symlink, removing anyway...', file = sys.stderr)
+ shutil.rmtree(dir)
+
+ print(dir)
+ dir.parent.mkdir(parents = True, exist_ok = True)
+ dir.symlink_to(targetdir)
+
+
+
+if __name__ == '__main__':
+ SrcTree()