Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Unified Diff: ios/crnet/build.py

Issue 1125293004: ios: add CrNet (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix last nits Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ios/crnet/Resources/README ('k') | ios/crnet/crnet.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/crnet/build.py
diff --git a/ios/crnet/build.py b/ios/crnet/build.py
new file mode 100755
index 0000000000000000000000000000000000000000..12874bf43bee59b0963ec34b40db4813364f1f15
--- /dev/null
+++ b/ios/crnet/build.py
@@ -0,0 +1,322 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import time
+
+
+SUPPORTED_ARCHES = ['i386', 'x86_64', 'armv7', 'arm64']
+
+
+class SubprocessError(Exception):
+ pass
+
+
+class ConfigurationError(Exception):
+ pass
+
+
+def out_directories(root):
+ """Returns all output directories containing crnet objects under root.
+
+ Currently this list is just hardcoded.
+
+ Args:
+ root: prefix for output directories.
+ """
+ out_dirs = ['Release-iphoneos', 'Release-iphonesimulator']
+ return map(lambda x: os.path.join(root, 'out', x), out_dirs)
+
+
+def check_command(command):
+ """Runs a command, raising an exception if it fails.
+
+ If the command returns a nonzero exit code, prints any data the command
+ emitted on stdout and stderr.
+
+ Args:
+ command: command to execute, in argv format.
+
+ Raises:
+ SubprocessError: the specified command returned nonzero exit status.
+ """
+ p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = p.communicate()
+ if p.returncode == 0:
+ return
+ message = 'Command failed: {0} (status {1})'.format(command, p.returncode)
+ print message
+ print 'stdout: {0}'.format(stdout)
+ print 'stderr: {0}'.format(stderr)
+ raise SubprocessError(message)
+
+
+def file_contains_string(path, string):
+ """Returns whether the file named by path contains string.
+
+ Args:
+ path: path of the file to search.
+ string: string to search the file for.
+
+ Returns:
+ True if file contains string, False otherwise.
+ """
+ with open(path, 'r') as f:
+ for line in f:
+ if string in line:
+ return True
+ return False
+
+
+def is_object_filename(filename):
+ """Returns whether the given filename names an object file.
+
+ Args:
+ filename: filename to inspect.
+
+ Returns:
+ True if filename names an object file, false otherwise.
+ """
+ (_, ext) = os.path.splitext(filename)
+ return ext in ('.a', '.o')
+
+
+class Step(object):
+ """Represents a single step of the crnet build process.
+
+ This parent class exists only to define the interface Steps present and keep
+ track of elapsed time for each step. Subclasses of Step should override the
+ run() method, which is called internally by start().
+
+ Attributes:
+ name: human-readable name of this step, used in debug output.
+ started_at: seconds since epoch that this step started running at.
+ """
+ def __init__(self, name):
+ self._name = name
+ self._started_at = None
+ self._ended_at = None
+
+ @property
+ def name(self):
+ return self._name
+
+ def start(self):
+ """Start running this step.
+
+ This method keeps track of how long the run() method takes to run and emits
+ the elapsed time after run() returns.
+ """
+ self._started_at = time.time()
+ print '{0}: '.format(self._name),
+ sys.stdout.flush()
+ self._run()
+ self._ended_at = time.time()
+ print '{0:.2f}s'.format(self._ended_at - self._started_at)
+
+ def _run(self):
+ """Actually run this step.
+
+ Subclasses should override this method to implement their own step logic.
+ """
+ raise NotImplementedError
+
+
+class CleanStep(Step):
+ """Clean the build output directories.
+
+ This step deletes intermediates generated by the build process. Some of these
+ intermediates (crnet_consumer.app and crnet_resources.bundle) are directories,
+ which contain files ninja doesn't know and hence won't remove, so the run()
+ method here explicitly deletes those directories before running 'ninja -t
+ clean'.
+
+ Attributes:
+ dirs: list of output directories to clean.
+ """
+ def __init__(self, root):
+ super(CleanStep, self).__init__('clean')
+ self._dirs = out_directories(root)
+
+ def _run(self):
+ """Runs the clean step.
+
+ Deletes crnet_consumer.app and crnet_resources.bundle in each output
+ directory and runs 'ninja -t clean' in each output directory.
+ """
+ for d in self._dirs:
+ if os.path.exists(os.path.join(d, 'crnet_consumer.app')):
+ shutil.rmtree(os.path.join(d, 'crnet_consumer.app'))
+ if os.path.exists(os.path.join(d, 'crnet_resources.bundle')):
+ shutil.rmtree(os.path.join(d, 'crnet_resources.bundle'))
+ check_command(['ninja', '-C', d, '-t', 'clean'])
+
+
+class HooksStep(Step):
+ """Validates the gyp config and reruns gclient hooks.
+
+ Attributes:
+ root: directory to find gyp config under.
+ """
+ def __init__(self, root):
+ super(HooksStep, self).__init__('hooks')
+ self._root = root
+
+ def _run(self):
+ """Runs the hooks step.
+
+ Checks that root/build/common.gypi contains target_subarch = both in a crude
+ way, then calls 'gclient runhooks'. TODO(ellyjones): parse common.gypi in a
+ more robust way.
+
+ Raises:
+ ConfigurationError: if target_subarch != both
+ """
+ common_gypi = os.path.join(self._root, 'build', 'common.gypi')
+ if not file_contains_string(common_gypi, "'target_subarch%': 'both'"):
+ raise ConfigurationError('target_subarch must be both in {0}'.format(
+ common_gypi))
+ check_command(['gclient', 'runhooks'])
+
+
+class BuildStep(Step):
+ """Builds all the intermediate crnet binaries.
+
+ All the hard work of this step is done by ninja; this step just shells out to
+ ninja to build the crnet_pack target.
+
+ Attributes:
+ dirs: output directories to run ninja in.
+ """
+ def __init__(self, root):
+ super(BuildStep, self).__init__('build')
+ self._dirs = out_directories(root)
+
+ def _run(self):
+ """Runs the build step.
+
+ For each output directory, run ninja to build the crnet_pack target in that
+ directory.
+ """
+ for d in self._dirs:
+ check_command(['ninja', '-C', d, 'crnet_pack'])
+
+
+class PackageStep(Step):
+ """Packages the built object files for release.
+
+ The release format is a tarball, containing one gzipped tarball per
+ architecture and a manifest file, which lists metadata about the build.
+
+ Attributes:
+ outdirs: directories containing built object files.
+ workdir: temporary working directory. Deleted at end of the step.
+ archdir: temporary directory under workdir. Used for collecting per-arch
+ binaries.
+ proddir: temporary directory under workdir. Used for intermediate per-arch
+ tarballs.
+ """
+ def __init__(self, root, outfile):
+ super(PackageStep, self).__init__('package')
+ self._outdirs = out_directories(root)
+ self._outfile = outfile
+
+ def _run(self):
+ """Runs the package step.
+
+ Packages each architecture from |root| into an individual .tar.gz file, then
+ packages all the .tar.gz files into one .tar file, which is written to
+ |outfile|.
+ """
+ (workdir, archdir, proddir) = self.create_work_dirs()
+ for arch in SUPPORTED_ARCHES:
+ self.package_arch(archdir, proddir, arch)
+ self.package(proddir)
+ shutil.rmtree(workdir)
+
+ def create_work_dirs(self):
+ """Creates working directories and returns their paths."""
+ workdir = tempfile.mkdtemp()
+ archdir = os.path.join(workdir, 'arch')
+ proddir = os.path.join(workdir, 'prod')
+ os.mkdir(archdir)
+ os.mkdir(proddir)
+ return (workdir, archdir, proddir)
+
+ def object_files_for_arch(self, arch):
+ """Returns a list of object files for the given architecture.
+
+ Under each outdir d, per-arch files are stored in d/arch, and object files
+ for a given arch contain the arch's name as a substring.
+
+ Args:
+ arch: architecture name. Must be in SUPPORTED_ARCHES.
+
+ Returns:
+ List of full pathnames to object files in outdirs for the named arch.
+ """
+ arch_files = []
+ for d in self._outdirs:
+ files = os.listdir(os.path.join(d, 'arch'))
+ for f in filter(is_object_filename, files):
+ if arch in f:
+ arch_files.append(os.path.join(d, 'arch', f))
+ return arch_files
+
+ def package_arch(self, archdir, proddir, arch):
+ """Packages an individual architecture.
+
+ Copies all the object files for the specified arch into a working directory
+ under self.archdir, then tars them up into a gzipped tarball under
+ self.proddir.
+
+ Args:
+ archdir: directory to stage architecture files in.
+ proddir: directory to stage result tarballs in.
+ arch: architecture name to package. Must be in SUPPORTED_ARCHES.
+ """
+ arch_files = self.object_files_for_arch(arch)
+ os.mkdir(os.path.join(archdir, arch))
+ for f in arch_files:
+ shutil.copy(f, os.path.join(archdir, arch))
+ out_filename = os.path.join(proddir, '{0}.tar.gz'.format(arch))
+ check_command(['tar', '-C', archdir, '-czf', out_filename, arch])
+
+ def package(self, proddir):
+ """Final packaging step. Packages all the arch tarballs into one tarball."""
+ arch_tarballs = []
+ for a in SUPPORTED_ARCHES:
+ arch_tarballs.append('{0}.tar.gz'.format(a))
+ check_command(['tar', '-C', proddir, '-cf', self._outfile] +
+ arch_tarballs)
+
+
+def main():
+ step_classes = {
+ 'clean': lambda: CleanStep(args.rootdir),
+ 'hooks': lambda: HooksStep(args.rootdir),
+ 'build': lambda: BuildStep(args.rootdir),
+ 'package': lambda: PackageStep(args.rootdir, args.outfile)
+ }
+ parser = argparse.ArgumentParser(description='Build and package crnet.')
+ parser.add_argument('--outfile', dest='outfile', default='crnet.tar',
+ help='Output file to generate (default: crnet.tar)')
+ parser.add_argument('--rootdir', dest='rootdir', default='../..',
+ help='Root directory to build from (default: ../..)')
+ parser.add_argument('steps', metavar='step', nargs='*')
+ args = parser.parse_args()
+ step_names = args.steps or ['clean', 'hooks', 'build', 'package']
+ steps = [step_classes[x]() for x in step_names]
+ for step in steps:
+ step.start()
+
+
+if __name__ == '__main__':
+ main()
« no previous file with comments | « ios/crnet/Resources/README ('k') | ios/crnet/crnet.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698