Mercurial > hg > config
comparison python/example/INSTALL.py @ 753:05fef8e5b8a9
example step of the conductor example
| author | Jeff Hammel <k0scist@gmail.com> |
|---|---|
| date | Fri, 03 Jul 2015 16:39:33 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 752:e6fb1a8fe66b | 753:05fef8e5b8a9 |
|---|---|
| 1 #!/usr/bin/env python | |
| 2 | |
| 3 """ | |
| 4 install and setup stuff | |
| 5 """ | |
| 6 | |
| 7 ### imports | |
| 8 import argparse | |
| 9 import inspect | |
| 10 import os | |
| 11 import subprocess | |
| 12 import sys | |
| 13 | |
| 14 ### globals | |
| 15 string = (str, unicode) | |
| 16 BASEDIR = os.environ.get('VIRTUAL_ENV', os.getcwd()) | |
| 17 | |
| 18 def comment(text, comment_character='#', join=False): | |
| 19 """ | |
| 20 comment some text | |
| 21 | |
| 22 text -- text to comment | |
| 23 join -- whether to ``str.join`` the lines of text | |
| 24 """ | |
| 25 text = text.strip() | |
| 26 if text: | |
| 27 retval = ['{} {}'.format(comment_character, line.rstrip()) | |
| 28 for line in text.splitlines()] | |
| 29 else: | |
| 30 retval = [] | |
| 31 if join: | |
| 32 retval = '\n'.join(retval) | |
| 33 return retval | |
| 34 | |
| 35 | |
| 36 class Command(object): | |
| 37 """an individual command""" | |
| 38 def __init__(self, command): | |
| 39 self.command = command | |
| 40 def shell(self): | |
| 41 return isinstance(self.command, string) | |
| 42 def interpolate(self, **variables): | |
| 43 if self.shell(): | |
| 44 return self.command.format(**variables) | |
| 45 return [i.format(**variables) for i in self.command] | |
| 46 def commandline(self, **variables): | |
| 47 command = self.interpolate(**variables) | |
| 48 if self.shell(): | |
| 49 return command | |
| 50 else: | |
| 51 return subprocess.list2cmdline(command) | |
| 52 __str__ = commandline | |
| 53 def __call__(self, variables=None, **kwargs): | |
| 54 variables = variables or {} | |
| 55 commandline = self.commandline(**variables) | |
| 56 kwargs['shell'] = self.shell() | |
| 57 print (commandline) | |
| 58 code = subprocess.call(self.interpolate(**variables), **kwargs) | |
| 59 if code: | |
| 60 raise subprocess.CalledProcessError(code, commandline) | |
| 61 | |
| 62 | |
| 63 ### setup and installation steps | |
| 64 | |
| 65 class Step(object): | |
| 66 commands = [] | |
| 67 env = {} | |
| 68 comment_character = '#' | |
| 69 | |
| 70 @classmethod | |
| 71 def name(cls): | |
| 72 return cls.__name__ | |
| 73 @classmethod | |
| 74 def description(cls): | |
| 75 return (getattr(cls, '__doc__', '') or '').strip() | |
| 76 @classmethod | |
| 77 def name_and_description(cls): | |
| 78 description = cls.description() | |
| 79 return '{}{}'.format(cls.name(), ': {}'.format(description if description else '')) | |
| 80 def __init__(self, **variables): | |
| 81 self.commands = self.commands[:] | |
| 82 self.variables = variables | |
| 83 for key in self.env: | |
| 84 os.environ[key] = self.env[key].format(**variables) | |
| 85 def command_objs(self): | |
| 86 return [Command(command) for command in self.commands] | |
| 87 def interpolate(self): | |
| 88 return [command.interpolate(**self.variables) | |
| 89 for command in self.command_objs()] | |
| 90 def write(self, output=sys.stdout): | |
| 91 for command in self.command_objs(): | |
| 92 output.write(str(command) + '\n') | |
| 93 def __call__(self): | |
| 94 for command in self.command_objs(): | |
| 95 command(variables=self.variables) | |
| 96 def script(self): | |
| 97 retval = comment(self.name(), self.comment_character) | |
| 98 description = self.description() | |
| 99 if description: | |
| 100 retval.extend(comment(description, self.comment_character)) | |
| 101 for command in self.command_objs(): | |
| 102 retval.append(command.commandline(**self.variables)) | |
| 103 return '\n'.join(retval) | |
| 104 | |
| 105 class UbuntuPackages(Step): | |
| 106 commands = [['sudo', 'apt-get', '-y', 'update'], | |
| 107 ['sudo', 'apt-get', '-y', 'upgrade'], | |
| 108 ] | |
| 109 packages = [] | |
| 110 def __init__(self, **kwargs): | |
| 111 Step.__init__(self, **kwargs) | |
| 112 self.commands.append(['sudo', 'apt-get', '-y', 'install'] + self.packages) | |
| 113 | |
| 114 class InstallXinePrerequisites(UbuntuPackages): | |
| 115 """install the prerequisites for the xine-based client""" | |
| 116 packages = ['libcurl4-gnutls-dev', | |
| 117 'g++', | |
| 118 'libx11-dev', | |
| 119 'libxext-dev', | |
| 120 'libxine1', | |
| 121 'libxine-dev', | |
| 122 'gxine', | |
| 123 ] | |
| 124 | |
| 125 class InstallFFMPEG(UbuntuPackages): | |
| 126 """install prerequisites for the FFMPEG-based client""" | |
| 127 packages = ['subversion', | |
| 128 'make', | |
| 129 'gcc', | |
| 130 'g++', | |
| 131 'libcurl4-gnutls-dev'] | |
| 132 yasm = '1.2.0' | |
| 133 ffmpeg = '1.2' | |
| 134 def __init__(self, client=os.path.join(BASEDIR, 'linux_client')): | |
| 135 UbuntuPackages.__init__(self, client=client, yasm=self.yasm, ffmpeg=self.ffmpeg) | |
| 136 | |
| 137 self.commands.extend([['mkdir', '-p', '{client}'], | |
| 138 ['wget', 'http://www.tortall.net/projects/yasm/releases/yasm-{yasm}.tar.gz', '-O', '{client}/yasm-{yasm}.tar.gz'], | |
| 139 ['wget', 'http://ffmpeg.org/releases/ffmpeg-{ffmpeg}.tar.gz', '-O', '{client}/ffmpeg-{ffmpeg}.tar.gz'], | |
| 140 ['tar', 'zfvx', '{client}/yasm-{yasm}.tar.gz', '-C', '{client}'], | |
| 141 ['tar', 'zfvx', '{client}/ffmpeg-{ffmpeg}.tar.gz', '-C', '{client}'], | |
| 142 | |
| 143 # YASM | |
| 144 'cd {client}/yasm-{yasm} && ./configure', | |
| 145 'cd {client}/yasm-{yasm} && make', | |
| 146 'cd {client}/yasm-{yasm} && sudo make install', | |
| 147 | |
| 148 # FFMPEG | |
| 149 'cd {client}/ffmpeg-{ffmpeg} && ./configure --enable-shared', | |
| 150 'cd {client}/ffmpeg-{ffmpeg} && make', | |
| 151 'cd {client}/ffmpeg-{ffmpeg} && sudo make install', | |
| 152 ]) | |
| 153 | |
| 154 | |
| 155 ### functionality for running multiple steps | |
| 156 | |
| 157 class Steps(object): | |
| 158 """run a series of steps""" | |
| 159 | |
| 160 comment_character = '#' | |
| 161 | |
| 162 # instance defaults | |
| 163 defaults = {} | |
| 164 | |
| 165 # variable descriptions | |
| 166 descriptions = dict() | |
| 167 | |
| 168 def __init__(self, *steps, **variables): | |
| 169 self.steps = steps | |
| 170 self.variables = variables | |
| 171 self.step_dict = {step.name():step for step in steps} | |
| 172 | |
| 173 def parser(self, description=None): | |
| 174 """return argument parser""" | |
| 175 parser = argparse.ArgumentParser(description=description) | |
| 176 parser.add_argument('--list', dest='list_steps', | |
| 177 action='store_true', default=False, | |
| 178 help="list available steps and exit") | |
| 179 parser.add_argument('--commands', dest='list_commands', | |
| 180 action='store_true', default=False, | |
| 181 help="list commands to be run and exit") | |
| 182 return parser | |
| 183 | |
| 184 def comment(self, text, join=False): | |
| 185 """ | |
| 186 comment some text | |
| 187 | |
| 188 text -- text to comment | |
| 189 join -- whether to ``str.join`` the lines of text | |
| 190 """ | |
| 191 return comment(text, self.comment_character, join) | |
| 192 | |
| 193 def script(self, steps=None, description=None, variables=None): | |
| 194 """returns a script""" | |
| 195 variables = variables or {} | |
| 196 retval = ['#!/bin/bash', ''] | |
| 197 if description: | |
| 198 retval.extend(self.comment(description)) | |
| 199 retval.append('') # whitespace delimiter | |
| 200 steps = self.instantiate(*steps, **variables) | |
| 201 for step in steps: | |
| 202 retval.append(step.script()) | |
| 203 retval.append('') # whitespace | |
| 204 | |
| 205 return '\n'.join(retval) | |
| 206 | |
| 207 @staticmethod | |
| 208 def args(step): | |
| 209 args, varargs, varkw, defaults = inspect.getargspec(step.__init__) | |
| 210 return args[1:] | |
| 211 | |
| 212 def all_args(self, steps=None): | |
| 213 retval = [] | |
| 214 steps = steps or self.steps | |
| 215 for step in steps: | |
| 216 args = self.args(step) | |
| 217 retval.extend([arg for arg in args if arg not in retval]) | |
| 218 return retval | |
| 219 | |
| 220 def get_variables(self, options, steps=None): | |
| 221 """get variables from a namespace""" | |
| 222 return {i:getattr(options, i, None) or self.defaults.get(i) | |
| 223 for i in self.all_args(steps)} | |
| 224 | |
| 225 def instantiate(self, *steps, **variables): | |
| 226 """instantiate a set of steps with variables""" | |
| 227 return [step(**{arg:variables.get(arg) | |
| 228 for arg in self.args(step)}) | |
| 229 for step in steps] | |
| 230 | |
| 231 def parse(self, args=sys.argv[1:], description=None): | |
| 232 | |
| 233 # create a parser | |
| 234 parser = self.parser(description) | |
| 235 | |
| 236 # add step names as arguments | |
| 237 parser.add_argument('steps', nargs='*', metavar='step', | |
| 238 help="steps to run; if omitted, all steps will be run") | |
| 239 | |
| 240 # add step arguments | |
| 241 for arg in self.all_args(): | |
| 242 variable_description = self.descriptions.get(arg) | |
| 243 default = self.defaults.get(arg) | |
| 244 if variable_description and default: | |
| 245 variable_description += ' [DEFAULT: %(default)s]' | |
| 246 parser.add_argument('--{}'.format(arg), dest=arg, | |
| 247 default=default, help=variable_description) | |
| 248 | |
| 249 # parse arguments | |
| 250 options = parser.parse_args(args) | |
| 251 | |
| 252 # get steps to run | |
| 253 if options.steps: | |
| 254 missing = [i for i in options.steps if i not in self.step_dict] | |
| 255 if missing: | |
| 256 parser.error("No such step: {}".format(', '.join(missing))) | |
| 257 steps = [self.step_dict[i] for i in options.steps] | |
| 258 else: | |
| 259 steps = self.steps[:] | |
| 260 | |
| 261 # get variables for execution | |
| 262 variables = self.get_variables(options) | |
| 263 | |
| 264 if options.list_steps: | |
| 265 # list steps and exit | |
| 266 for step in steps: | |
| 267 print (step.name_and_description()) | |
| 268 variables = self.args(step) | |
| 269 if variables: | |
| 270 print ('Variables: {}'.format(', '.join(variables))) | |
| 271 print ('') # whitespace | |
| 272 sys.exit(0) | |
| 273 | |
| 274 # instantiate steps | |
| 275 step_objs = self.instantiate(*steps, **variables) | |
| 276 | |
| 277 if options.list_commands: | |
| 278 # print commands and exit | |
| 279 print (self.script(steps, description, variables)) | |
| 280 sys.exit(0) | |
| 281 | |
| 282 # run steps | |
| 283 for step in step_objs: | |
| 284 step() | |
| 285 | |
| 286 | |
| 287 ### main | |
| 288 | |
| 289 def main(args=sys.argv[1:]): | |
| 290 """CLI""" | |
| 291 Steps(*[InstallXinePrerequisites, | |
| 292 InstallFFMPEG]).parse(args=args, description=__doc__) | |
| 293 | |
| 294 if __name__ == '__main__': | |
| 295 main() |
