#!/usr/bin/env python # # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. # """Tool for automating creation of a Dart bundle.""" import optparse import os from os import path import shutil import subprocess import sys import utils class BundleMaker(object): """Main class for building a Dart bundle.""" def __init__(self, top_dir=None, dest=None, verbose=False, skip_build=False): self._top_dir = top_dir self._dest = dest self._verbose = verbose self._skip_build = skip_build self._os = utils.GuessOS() self._release_build_root = utils.GetBuildRoot(self._os, mode='release', arch='ia32') self._debug_build_root = utils.GetBuildRoot(self._os, mode='debug', arch='ia32') self._dartc_build_root = utils.GetBuildRoot(self._os, mode='release', arch='dartc') @staticmethod def BuildOptions(): """Make an option parser with the options supported by this tool. Returns: A newly created OptionParser. """ op = optparse.OptionParser('usage: %prog [options]') op.add_option('-d', '--dest') op.add_option('-v', '--verbose', default=False, action='store_true') op.add_option('--skip-build', default=False, action='store_true') return op @staticmethod def CheckOptions(op, top_dir, cmd_line_args): """Check the command line arguments. Args: op: An OptionParser (see BuildOptions). top_dir: The top-level source directory. cmd_line_args: The command line arguments. Returns: A dict with the analyzed options and other values. The dict includes these keys: dest: The destition directory for storing the bundle. verbose: Whether the tool should be verbose. top_dir: Same as top_dir argument. skip_build: Whether the tool should skip the build steps. """ (options, args) = op.parse_args(args=cmd_line_args) if args: # Terminate program. op.error('extra arguments on command line') dest = options.dest if not dest: dest = path.normpath(path.join(top_dir, 'new_bundle')) print 'Bundle is saved to %r' % dest if not path.exists(dest): os.makedirs(dest) elif not path.isdir(dest): # Terminate program. op.error('%s: is not a directory' % dest) return { 'dest': dest, 'verbose': options.verbose, 'top_dir': top_dir, 'skip_build': options.skip_build, } def _PrintConfiguration(self): for member in [m for m in dir(self) if not m.startswith('_')]: value = getattr(self, member) if not callable(value): print '%s = %r' % (member, value) def _GetTool(self, name): return self._GetLocation('tools', name) def _GetLocation(self, *arguments): location = path.join(self._top_dir, *arguments) if not path.exists(location): raise utils.Error('%s: does not exist' % location) return location def _InvokeTool(self, project, name, *arguments): location = self._GetLocation(project) tool = path.relpath(self._GetTool(name), location) command_array = [tool] for argument in arguments: command_array.append(str(argument)) stdout = subprocess.PIPE if self._verbose: print 'Invoking', ' '.join(command_array) print 'in', location stdout = None # In verbose mode we want to see the output from the tool. proc = subprocess.Popen(command_array, cwd=location, stdout=stdout, stderr=subprocess.STDOUT) stdout = proc.communicate()[0] exit_code = proc.wait() if exit_code != 0: sys.stderr.write(stdout) raise utils.Error('%s returned %s' % (name, exit_code)) elif self._verbose: print name, 'returned', exit_code def _GetReleaseOutput(self, project, name): return self._GetLocation(project, self._release_build_root, name) def _GetDebugOutput(self, project, name): return self._GetLocation(project, self._debug_build_root, name) def _GetDartcOutput(self, project, name): return self._GetLocation(project, self._dartc_build_root, name) def _GetNativeDest(self, mode, name): return path.join('native', self._os, utils.GetBuildConf(mode, 'ia32'), name) def _EnsureExists(self, artifact): if not path.exists(artifact): raise utils.Error('%s: does not exist' % artifact) def _BuildArtifacts(self): if not self._skip_build: self._InvokeTool('runtime', 'build.py', '--arch=ia32', '--mode=release,debug') self._InvokeTool('compiler', 'build.py', '--arch=dartc', '--mode=release') self._InvokeTool('language', 'build.py', '--arch=ia32', '--mode=release') release_vm = self._GetReleaseOutput('runtime', 'dart_bin') self._EnsureExists(release_vm) release_vm_dest = self._GetNativeDest('release', 'dart_bin') debug_vm = self._GetDebugOutput('runtime', 'dart_bin') self._EnsureExists(debug_vm) debug_vm_dest = self._GetNativeDest('debug', 'dart_bin') dartc_bundle = self._GetDartcOutput('compiler', 'compiler') self._EnsureExists(dartc_bundle) return ( (self._GetLocation('bundle', 'bin', 'dart'), 'dart', False), (release_vm, release_vm_dest, True), (debug_vm, debug_vm_dest, True), (dartc_bundle, 'compiler', False), (self._GetLocation('bundle', 'samples'), 'samples', False), (self._GetLocation('bundle', 'README'), 'README.txt', False), (self._GetReleaseOutput('language', 'guide'), 'guide', False), ) def _CopyCorelib(self): def ReadSources(sources, *paths): p = path.join(*paths) return [self._GetLocation(p, s) for s in sources if s.endswith('.dart')] gypi = self._GetLocation('corelib', 'src', 'corelib_sources.gypi') sources = [] with open(gypi, 'r') as f: text = f.read() sources.extend(ReadSources(eval(text)['sources'], 'corelib', 'src')) gypi = self._GetLocation('runtime', 'lib', 'lib_sources.gypi') with open(gypi, 'r') as f: text = f.read() sources.extend(ReadSources(eval(text)['sources'], 'runtime', 'lib')) dest = path.join(self._dest, 'lib', 'core') if not path.exists(dest): os.makedirs(dest) for source in sources: if self._verbose: print 'Copying', source, 'to', dest shutil.copy2(source, dest) def MakeBundle(self): """Build and install all the components of a bundle. Returns: 0 if the bundle was created successfully. """ if self._verbose: self._PrintConfiguration() for artifact, reldest, strip in self._BuildArtifacts(): dest = path.join(self._dest, reldest) if not path.exists(path.dirname(dest)): os.makedirs(path.dirname(dest)) if self._verbose: print 'Copying', artifact, 'to', dest if path.isdir(artifact): assert not strip if path.exists(dest): shutil.rmtree(dest) shutil.copytree(artifact, dest) else: if strip: os.system('strip -o %s %s' % (dest, artifact)) else: shutil.copy2(artifact, dest) self._CopyCorelib() os.system('chmod -R a+rX %s' % self._dest) return 0 def main(): top_dir = path.normpath(path.join(path.dirname(sys.argv[0]), os.pardir)) cmd_line_args = sys.argv[1:] try: op = BundleMaker.BuildOptions() options = BundleMaker.CheckOptions(op, top_dir, cmd_line_args) except utils.Error: return 1 sys.exit(BundleMaker(**options).MakeBundle()) if __name__ == '__main__': main()