| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Downloads, builds (with instrumentation) and installs shared libraries.""" | 6 """Downloads, builds (with instrumentation) and installs shared libraries.""" |
| 7 | 7 |
| 8 import argparse | 8 import argparse |
| 9 import ast |
| 9 import os | 10 import os |
| 10 import platform | 11 import platform |
| 11 import re | 12 import re |
| 12 import shlex | 13 import shlex |
| 13 import shutil | 14 import shutil |
| 14 import subprocess | 15 import subprocess |
| 15 import sys | 16 import sys |
| 16 | 17 |
| 17 SCRIPT_ABSOLUTE_PATH = os.path.dirname(os.path.abspath(__file__)) | 18 SCRIPT_ABSOLUTE_PATH = os.path.dirname(os.path.abspath(__file__)) |
| 18 | 19 |
| 19 def unescape_flags(s): | 20 def unescape_flags(s): |
| 20 """Un-escapes build flags received from GYP. | 21 """Un-escapes build flags received from GYP. |
| 21 | 22 |
| 22 GYP escapes build flags as if they are to be inserted directly into a command | 23 GYP escapes build flags as if they are to be inserted directly into a command |
| 23 line, wrapping each flag in double quotes. When flags are passed via | 24 line, wrapping each flag in double quotes. When flags are passed via |
| 24 CFLAGS/LDFLAGS instead, double quotes must be dropped. | 25 CFLAGS/LDFLAGS instead, double quotes must be dropped. |
| 25 """ | 26 """ |
| 26 return ' '.join(shlex.split(s)) | 27 if not s: |
| 28 return '' |
| 29 try: |
| 30 return ' '.join(ast.literal_eval(s)) |
| 31 except (SyntaxError, ValueError): |
| 32 return ' '.join(shlex.split(s)) |
| 27 | 33 |
| 28 | 34 |
| 29 def real_path(path_relative_to_gyp): | 35 def real_path(path_relative_to_gyp): |
| 30 """Returns the absolute path to a file. | 36 """Returns the absolute path to a file. |
| 31 | 37 |
| 32 GYP generates paths relative to the location of the .gyp file, which is one | 38 GYP generates paths relative to the location of the .gyp file, which is one |
| 33 level above the location of this script. This function converts them to | 39 level above the location of this script. This function converts them to |
| 34 absolute paths. | 40 absolute paths. |
| 35 """ | 41 """ |
| 36 return os.path.realpath(os.path.join(SCRIPT_ABSOLUTE_PATH, '..', | 42 return os.path.realpath(os.path.join(SCRIPT_ABSOLUTE_PATH, '..', |
| 37 path_relative_to_gyp)) | 43 path_relative_to_gyp)) |
| 38 | 44 |
| 39 | 45 |
| 40 class InstrumentedPackageBuilder(object): | 46 class InstrumentedPackageBuilder(object): |
| 41 """Checks out and builds a single instrumented package.""" | 47 """Checks out and builds a single instrumented package.""" |
| 42 def __init__(self, args, clobber): | 48 def __init__(self, args, clobber): |
| 43 self._cc = args.cc | 49 self._cc = args.cc |
| 44 self._cxx = args.cxx | 50 self._cxx = args.cxx |
| 45 self._extra_configure_flags = args.extra_configure_flags | 51 self._extra_configure_flags = unescape_flags(args.extra_configure_flags) |
| 46 self._jobs = args.jobs | 52 self._jobs = args.jobs |
| 47 self._libdir = args.libdir | 53 self._libdir = args.libdir |
| 48 self._package = args.package | 54 self._package = args.package |
| 49 self._patch = real_path(args.patch) if args.patch else None | 55 self._patch = real_path(args.patch) if args.patch else None |
| 50 self._pre_build = \ | 56 self._pre_build = \ |
| 51 real_path(args.pre_build) if args.pre_build else None | 57 real_path(args.pre_build) if args.pre_build else None |
| 52 self._sanitizer = args.sanitizer | 58 self._sanitizer = args.sanitizer |
| 53 self._verbose = args.verbose | 59 self._verbose = args.verbose |
| 54 self._clobber = clobber | 60 self._clobber = clobber |
| 55 self._working_dir = os.path.join( | 61 self._working_dir = os.path.join( |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 raise Exception('Failed to run: %s' % command) | 114 raise Exception('Failed to run: %s' % command) |
| 109 | 115 |
| 110 def maybe_download_source(self): | 116 def maybe_download_source(self): |
| 111 """Checks out the source code (if needed). | 117 """Checks out the source code (if needed). |
| 112 | 118 |
| 113 Checks out the source code for the package, if required (i.e. unless running | 119 Checks out the source code for the package, if required (i.e. unless running |
| 114 in no-clobber mode). Initializes self._source_dir and self._source_archives. | 120 in no-clobber mode). Initializes self._source_dir and self._source_archives. |
| 115 """ | 121 """ |
| 116 get_fresh_source = self._clobber or not os.path.exists(self._working_dir) | 122 get_fresh_source = self._clobber or not os.path.exists(self._working_dir) |
| 117 if get_fresh_source: | 123 if get_fresh_source: |
| 118 self.shell_call('rm -rf %s' % self._working_dir) | 124 shutil.rmtree(self._working_dir, ignore_errors=True) |
| 119 os.makedirs(self._working_dir) | 125 os.makedirs(self._working_dir) |
| 120 self.shell_call('apt-get source %s' % self._package, | 126 self.shell_call('apt-get source %s' % self._package, |
| 121 cwd=self._working_dir) | 127 cwd=self._working_dir) |
| 122 | 128 |
| 123 (dirpath, dirnames, filenames) = os.walk(self._working_dir).next() | 129 (dirpath, dirnames, filenames) = os.walk(self._working_dir).next() |
| 124 | 130 |
| 125 if len(dirnames) != 1: | 131 if len(dirnames) != 1: |
| 126 raise Exception( | 132 raise Exception( |
| 127 '`apt-get source %s\' must create exactly one subdirectory.' | 133 '`apt-get source %s\' must create exactly one subdirectory.' |
| 128 % self._package) | 134 % self._package) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 141 self.shell_call('patch -p1 -i %s' % self._patch, cwd=self._source_dir) | 147 self.shell_call('patch -p1 -i %s' % self._patch, cwd=self._source_dir) |
| 142 if self._pre_build: | 148 if self._pre_build: |
| 143 self.shell_call(self._pre_build, cwd=self._source_dir) | 149 self.shell_call(self._pre_build, cwd=self._source_dir) |
| 144 | 150 |
| 145 def copy_source_archives(self): | 151 def copy_source_archives(self): |
| 146 """Copies the downloaded source archives to the output dir. | 152 """Copies the downloaded source archives to the output dir. |
| 147 | 153 |
| 148 For license compliance purposes, every Chromium build that includes | 154 For license compliance purposes, every Chromium build that includes |
| 149 instrumented libraries must include their full source code. | 155 instrumented libraries must include their full source code. |
| 150 """ | 156 """ |
| 151 self.shell_call('rm -rf %s' % self._source_archives_dir) | 157 shutil.rmtree(self._source_archives_dir, ignore_errors=True) |
| 152 os.makedirs(self._source_archives_dir) | 158 os.makedirs(self._source_archives_dir) |
| 153 for filename in self._source_archives: | 159 for filename in self._source_archives: |
| 154 shutil.copy(filename, self._source_archives_dir) | 160 shutil.copy(filename, self._source_archives_dir) |
| 155 if self._patch: | 161 if self._patch: |
| 156 shutil.copy(self._patch, self._source_archives_dir) | 162 shutil.copy(self._patch, self._source_archives_dir) |
| 157 | 163 |
| 158 def download_build_install(self): | 164 def download_build_install(self): |
| 159 got_fresh_source = self.maybe_download_source() | 165 got_fresh_source = self.maybe_download_source() |
| 160 if got_fresh_source: | 166 if got_fresh_source: |
| 161 self.patch_source() | 167 self.patch_source() |
| 162 self.copy_source_archives() | 168 self.copy_source_archives() |
| 163 | 169 |
| 164 self.shell_call('mkdir -p %s' % self.dest_libdir()) | 170 if not os.path.exists(self.dest_libdir()): |
| 171 os.makedirs(self.dest_libdir()) |
| 165 | 172 |
| 166 try: | 173 try: |
| 167 self.build_and_install() | 174 self.build_and_install() |
| 168 except Exception as exception: | 175 except Exception as exception: |
| 169 print 'ERROR: Failed to build package %s. Have you run ' \ | 176 print 'ERROR: Failed to build package %s. Have you run ' \ |
| 170 'src/third_party/instrumented_libraries/scripts/' \ | 177 'src/third_party/instrumented_libraries/scripts/' \ |
| 171 'install-build-deps.sh?' % \ | 178 'install-build-deps.sh?' % \ |
| 172 self._package | 179 self._package |
| 173 print | 180 print |
| 174 raise | 181 raise |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 359 # Make sure we don't override the default flags in the makefile. | 366 # Make sure we don't override the default flags in the makefile. |
| 360 for variable in ['CFLAGS', 'CXXFLAGS', 'LDFLAGS']: | 367 for variable in ['CFLAGS', 'CXXFLAGS', 'LDFLAGS']: |
| 361 del self._build_env[variable] | 368 del self._build_env[variable] |
| 362 | 369 |
| 363 # Hardcoded paths. | 370 # Hardcoded paths. |
| 364 temp_dir = os.path.join(self._source_dir, 'nss') | 371 temp_dir = os.path.join(self._source_dir, 'nss') |
| 365 temp_libdir = os.path.join(temp_dir, 'lib') | 372 temp_libdir = os.path.join(temp_dir, 'lib') |
| 366 | 373 |
| 367 # Parallel build is not supported. Also, the build happens in | 374 # Parallel build is not supported. Also, the build happens in |
| 368 # <source_dir>/nss. | 375 # <source_dir>/nss. |
| 369 self.make(make_args, jobs=1, cwd=temp_dir) | 376 try: |
| 377 self.make(make_args, jobs=1, cwd=temp_dir) |
| 378 except Exception: |
| 379 # Building fails after all the required DSOs have been built, so ignore |
| 380 # the error. |
| 381 pass |
| 370 | 382 |
| 371 self.fix_rpaths(temp_libdir) | 383 self.fix_rpaths(temp_libdir) |
| 372 | 384 |
| 373 # 'make install' is not supported. Copy the DSOs manually. | 385 # 'make install' is not supported. Copy the DSOs manually. |
| 374 for (dirpath, dirnames, filenames) in os.walk(temp_libdir): | 386 for (dirpath, dirnames, filenames) in os.walk(temp_libdir): |
| 375 for filename in filenames: | 387 for filename in filenames: |
| 376 if filename.endswith('.so'): | 388 if filename.endswith('.so'): |
| 377 full_path = os.path.join(dirpath, filename) | 389 full_path = os.path.join(dirpath, filename) |
| 378 if self._verbose: | 390 if self._verbose: |
| 379 print 'download_build_install.py: installing %s' % full_path | 391 print 'download_build_install.py: installing %s' % full_path |
| 380 shutil.copy(full_path, self.dest_libdir()) | 392 shutil.copy(full_path, self.dest_libdir()) |
| 381 | 393 |
| 382 | 394 |
| 395 class StubBuilder(InstrumentedPackageBuilder): |
| 396 def download_build_install(self): |
| 397 self._touch(os.path.join(self._destdir, '%s.txt' % self._package)) |
| 398 self._touch(os.path.join(self.dest_libdir(), '%s.so.0' % self._package)) |
| 399 |
| 400 def _touch(self, path): |
| 401 with open(path, 'w'): |
| 402 pass |
| 403 |
| 404 |
| 383 def main(): | 405 def main(): |
| 384 parser = argparse.ArgumentParser( | 406 parser = argparse.ArgumentParser( |
| 385 description='Download, build and install an instrumented package.') | 407 description='Download, build and install an instrumented package.') |
| 386 | 408 |
| 387 parser.add_argument('-j', '--jobs', type=int, default=1) | 409 parser.add_argument('-j', '--jobs', type=int, default=1) |
| 388 parser.add_argument('-p', '--package', required=True) | 410 parser.add_argument('-p', '--package', required=True) |
| 389 parser.add_argument( | 411 parser.add_argument( |
| 390 '-i', '--product-dir', default='.', | 412 '-i', '--product-dir', default='.', |
| 391 help='Relative path to the directory with chrome binaries') | 413 help='Relative path to the directory with chrome binaries') |
| 392 parser.add_argument( | 414 parser.add_argument( |
| (...skipping 27 matching lines...) Expand all Loading... |
| 420 (os.environ.get('INSTRUMENTED_LIBRARIES_NO_CLOBBER', '') != '1') | 442 (os.environ.get('INSTRUMENTED_LIBRARIES_NO_CLOBBER', '') != '1') |
| 421 | 443 |
| 422 if args.build_method == 'destdir': | 444 if args.build_method == 'destdir': |
| 423 builder = InstrumentedPackageBuilder(args, clobber) | 445 builder = InstrumentedPackageBuilder(args, clobber) |
| 424 elif args.build_method == 'custom_nss': | 446 elif args.build_method == 'custom_nss': |
| 425 builder = NSSBuilder(args, clobber) | 447 builder = NSSBuilder(args, clobber) |
| 426 elif args.build_method == 'custom_libcap': | 448 elif args.build_method == 'custom_libcap': |
| 427 builder = LibcapBuilder(args, clobber) | 449 builder = LibcapBuilder(args, clobber) |
| 428 elif args.build_method == 'custom_libpci3': | 450 elif args.build_method == 'custom_libpci3': |
| 429 builder = Libpci3Builder(args, clobber) | 451 builder = Libpci3Builder(args, clobber) |
| 452 elif args.build_method == 'stub': |
| 453 builder = StubBuilder(args, clobber) |
| 430 else: | 454 else: |
| 431 raise Exception('Unrecognized build method: %s' % args.build_method) | 455 raise Exception('Unrecognized build method: %s' % args.build_method) |
| 432 | 456 |
| 433 builder.download_build_install() | 457 builder.download_build_install() |
| 434 | 458 |
| 435 if __name__ == '__main__': | 459 if __name__ == '__main__': |
| 436 main() | 460 main() |
| OLD | NEW |