| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 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 """MB - the Meta-Build wrapper around GYP and GN | 6 """MB - the Meta-Build wrapper around GYP and GN |
| 7 | 7 |
| 8 MB is a wrapper script for GYP and GN that can be used to generate build files | 8 MB is a wrapper script for GYP and GN that can be used to generate build files |
| 9 for sets of canned configurations and analyze them. | 9 for sets of canned configurations and analyze them. |
| 10 """ | 10 """ |
| 11 | 11 |
| 12 from __future__ import print_function | 12 from __future__ import print_function |
| 13 | 13 |
| 14 import argparse | 14 import argparse |
| 15 import ast | 15 import ast |
| 16 import json | 16 import json |
| 17 import os | 17 import os |
| 18 import pipes | 18 import pipes |
| 19 import shlex | 19 import shlex |
| 20 import shutil | 20 import shutil |
| 21 import sys | 21 import sys |
| 22 import subprocess | 22 import subprocess |
| 23 import tempfile |
| 23 | 24 |
| 24 | 25 |
| 25 def main(args): | 26 def main(args): |
| 26 mbw = MetaBuildWrapper() | 27 mbw = MetaBuildWrapper() |
| 27 mbw.ParseArgs(args) | 28 mbw.ParseArgs(args) |
| 28 return mbw.args.func() | 29 return mbw.args.func() |
| 29 | 30 |
| 30 | 31 |
| 31 class MetaBuildWrapper(object): | 32 class MetaBuildWrapper(object): |
| 32 def __init__(self): | 33 def __init__(self): |
| (...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 408 self.Print() | 409 self.Print() |
| 409 | 410 |
| 410 output_path = self.args.output_path[0] | 411 output_path = self.args.output_path[0] |
| 411 | 412 |
| 412 # Bail out early if a GN file was modified, since 'gn refs' won't know | 413 # Bail out early if a GN file was modified, since 'gn refs' won't know |
| 413 # what to do about it. | 414 # what to do about it. |
| 414 if any(f.endswith('.gn') or f.endswith('.gni') for f in inp['files']): | 415 if any(f.endswith('.gn') or f.endswith('.gni') for f in inp['files']): |
| 415 self.WriteJSON({'status': 'Found dependency (all)'}, output_path) | 416 self.WriteJSON({'status': 'Found dependency (all)'}, output_path) |
| 416 return 0 | 417 return 0 |
| 417 | 418 |
| 418 # TODO: Because of the --type=executable filter below, we don't detect | 419 # Bail out early if 'all' was asked for, since 'gn refs' won't recognize it. |
| 419 # when files will cause 'all' or 'gn_all' or similar targets to be | 420 if 'all' in inp['targets']: |
| 420 # dirty. We need to figure out how to handle that properly, but for | |
| 421 # now we can just bail out early. | |
| 422 if 'gn_all' in inp['targets'] or 'all' in inp['targets']: | |
| 423 self.WriteJSON({'status': 'Found dependency (all)'}, output_path) | 421 self.WriteJSON({'status': 'Found dependency (all)'}, output_path) |
| 424 return 0 | 422 return 0 |
| 425 | 423 |
| 426 all_needed_targets = set() | |
| 427 ret = 0 | 424 ret = 0 |
| 428 for f in inp['files']: | 425 response_file = self.TempFile() |
| 426 response_file.write('\n'.join(inp['files']) + '\n') |
| 427 response_file.close() |
| 428 |
| 429 matching_targets = [] |
| 430 try: |
| 429 cmd = self.GNCmd('refs', self.args.path[0]) + [ | 431 cmd = self.GNCmd('refs', self.args.path[0]) + [ |
| 430 '//' + f, '--type=executable', '--all', '--as=output'] | 432 '@%s' % response_file.name, |
| 433 '--type=executable', '--all', '--as=output' |
| 434 ] |
| 431 ret, out, _ = self.Run(cmd) | 435 ret, out, _ = self.Run(cmd) |
| 432 if ret and not 'The input matches no targets' in out: | 436 if ret and not 'The input matches no targets' in out: |
| 433 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), | 437 self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), |
| 434 output_path) | 438 output_path) |
| 439 build_dir = self.ToSrcRelPath(self.args.path[0]) + os.sep |
| 440 for output in out.splitlines(): |
| 441 build_output = output.replace(build_dir, '') |
| 442 if build_output in inp['targets']: |
| 443 matching_targets.append(build_output) |
| 444 finally: |
| 445 self.RemoveFile(response_file.name) |
| 435 | 446 |
| 436 rpath = self.ToSrcRelPath(self.args.path[0]) + os.sep | 447 if matching_targets: |
| 437 needed_targets = [t.replace(rpath, '') for t in out.splitlines()] | |
| 438 needed_targets = [nt for nt in needed_targets if nt in inp['targets']] | |
| 439 all_needed_targets.update(set(needed_targets)) | |
| 440 | |
| 441 if all_needed_targets: | |
| 442 # TODO: it could be that a target X might depend on a target Y | 448 # TODO: it could be that a target X might depend on a target Y |
| 443 # and both would be listed in the input, but we would only need | 449 # and both would be listed in the input, but we would only need |
| 444 # to specify target X as a build_target (whereas both X and Y are | 450 # to specify target X as a build_target (whereas both X and Y are |
| 445 # targets). I'm not sure if that optimization is generally worth it. | 451 # targets). I'm not sure if that optimization is generally worth it. |
| 446 self.WriteJSON({'targets': sorted(all_needed_targets), | 452 self.WriteJSON({'targets': sorted(matching_targets), |
| 447 'build_targets': sorted(all_needed_targets), | 453 'build_targets': sorted(matching_targets), |
| 448 'status': 'Found dependency'}, output_path) | 454 'status': 'Found dependency'}, output_path) |
| 449 else: | 455 else: |
| 450 self.WriteJSON({'targets': [], | 456 self.WriteJSON({'targets': [], |
| 451 'build_targets': [], | 457 'build_targets': [], |
| 452 'status': 'No dependency'}, output_path) | 458 'status': 'No dependency'}, output_path) |
| 453 | 459 |
| 454 if not ret and self.args.verbose: | 460 if not ret and self.args.verbose: |
| 455 outp = json.loads(self.ReadFile(output_path)) | 461 outp = json.loads(self.ReadFile(output_path)) |
| 456 self.Print() | 462 self.Print() |
| 457 self.Print('analyze output:') | 463 self.Print('analyze output:') |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 529 | 535 |
| 530 def Exists(self, path): | 536 def Exists(self, path): |
| 531 # This function largely exists so it can be overridden for testing. | 537 # This function largely exists so it can be overridden for testing. |
| 532 return os.path.exists(path) | 538 return os.path.exists(path) |
| 533 | 539 |
| 534 def ReadFile(self, path): | 540 def ReadFile(self, path): |
| 535 # This function largely exists so it can be overriden for testing. | 541 # This function largely exists so it can be overriden for testing. |
| 536 with open(path) as fp: | 542 with open(path) as fp: |
| 537 return fp.read() | 543 return fp.read() |
| 538 | 544 |
| 545 def RemoveFile(self, path): |
| 546 # This function largely exists so it can be overriden for testing. |
| 547 os.remove(path) |
| 548 |
| 549 def TempFile(self, mode='w'): |
| 550 # This function largely exists so it can be overriden for testing. |
| 551 return tempfile.NamedTemporaryFile(mode=mode, delete=False) |
| 552 |
| 539 def WriteFile(self, path, contents): | 553 def WriteFile(self, path, contents): |
| 540 # This function largely exists so it can be overriden for testing. | 554 # This function largely exists so it can be overriden for testing. |
| 541 with open(path, 'w') as fp: | 555 with open(path, 'w') as fp: |
| 542 return fp.write(contents) | 556 return fp.write(contents) |
| 543 | 557 |
| 558 |
| 544 class MBErr(Exception): | 559 class MBErr(Exception): |
| 545 pass | 560 pass |
| 546 | 561 |
| 547 | 562 |
| 548 if __name__ == '__main__': | 563 if __name__ == '__main__': |
| 549 try: | 564 try: |
| 550 sys.exit(main(sys.argv[1:])) | 565 sys.exit(main(sys.argv[1:])) |
| 551 except MBErr as e: | 566 except MBErr as e: |
| 552 print(e) | 567 print(e) |
| 553 sys.exit(1) | 568 sys.exit(1) |
| 554 except KeyboardInterrupt: | 569 except KeyboardInterrupt: |
| 555 print("interrupted, exiting", stream=sys.stderr) | 570 print("interrupted, exiting", stream=sys.stderr) |
| 556 sys.exit(130) | 571 sys.exit(130) |
| OLD | NEW |