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 |