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

Side by Side Diff: test/lib/TestGyp.py

Issue 1131213003: Remove the Android generator. (Closed) Base URL: https://chromium.googlesource.com/external/gyp.git@master
Patch Set: 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 unified diff | Download patch
« no previous file with comments | « test/hello/gyptest-regyp-output.py ('k') | test/library/gyptest-shared.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 Google Inc. All rights reserved. 1 # Copyright (c) 2012 Google Inc. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """ 5 """
6 TestGyp.py: a testing framework for GYP integration tests. 6 TestGyp.py: a testing framework for GYP integration tests.
7 """ 7 """
8 8
9 import collections 9 import collections
10 from contextlib import contextmanager 10 from contextlib import contextmanager
11 import itertools 11 import itertools
12 import os 12 import os
13 import random
14 import re 13 import re
15 import shutil 14 import shutil
16 import stat
17 import subprocess 15 import subprocess
18 import sys 16 import sys
19 import tempfile 17 import tempfile
20 import time
21 18
22 import TestCmd 19 import TestCmd
23 import TestCommon 20 import TestCommon
24 from TestCommon import __all__ 21 from TestCommon import __all__
25 22
26 __all__.extend([ 23 __all__.extend([
27 'TestGyp', 24 'TestGyp',
28 ]) 25 ])
29 26
30 27
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 class TestGypCustom(TestGypBase): 401 class TestGypCustom(TestGypBase):
405 """ 402 """
406 Subclass for testing the GYP with custom generator 403 Subclass for testing the GYP with custom generator
407 """ 404 """
408 405
409 def __init__(self, gyp=None, *args, **kw): 406 def __init__(self, gyp=None, *args, **kw):
410 self.format = kw.pop("format") 407 self.format = kw.pop("format")
411 super(TestGypCustom, self).__init__(*args, **kw) 408 super(TestGypCustom, self).__init__(*args, **kw)
412 409
413 410
414 class TestGypAndroid(TestGypBase):
415 """
416 Subclass for testing the GYP Android makefile generator. Note that
417 build/envsetup.sh and lunch must have been run before running tests.
418 """
419 format = 'android'
420
421 # Note that we can't use mmm as the build tool because ...
422 # - it builds all targets, whereas we need to pass a target
423 # - it is a function, whereas the test runner assumes the build tool is a file
424 # Instead we use make and duplicate the logic from mmm.
425 build_tool_list = ['make']
426
427 # We use our custom target 'gyp_all_modules', as opposed to the 'all_modules'
428 # target used by mmm, to build only those targets which are part of the gyp
429 # target 'all'.
430 ALL = 'gyp_all_modules'
431
432 def __init__(self, gyp=None, *args, **kw):
433 # Android requires build and test output to be inside its source tree.
434 # We use the following working directory for the test's source, but the
435 # test's build output still goes to $ANDROID_PRODUCT_OUT.
436 # Note that some tests explicitly set format='gypd' to invoke the gypd
437 # backend. This writes to the source tree, but there's no way around this.
438 kw['workdir'] = os.path.join('/tmp', 'gyptest',
439 kw.get('workdir', 'testworkarea'))
440 # We need to remove all gyp outputs from out/. Ths is because some tests
441 # don't have rules to regenerate output, so they will simply re-use stale
442 # output if present. Since the test working directory gets regenerated for
443 # each test run, this can confuse things.
444 # We don't have a list of build outputs because we don't know which
445 # dependent targets were built. Instead we delete all gyp-generated output.
446 # This may be excessive, but should be safe.
447 out_dir = os.environ['ANDROID_PRODUCT_OUT']
448 obj_dir = os.path.join(out_dir, 'obj')
449 shutil.rmtree(os.path.join(obj_dir, 'GYP'), ignore_errors = True)
450 for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']:
451 for d in os.listdir(os.path.join(obj_dir, x)):
452 if d.endswith('_gyp_intermediates'):
453 shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True)
454 for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]:
455 for d in os.listdir(os.path.join(out_dir, x)):
456 if d.endswith('_gyp.so'):
457 os.remove(os.path.join(out_dir, x, d))
458
459 super(TestGypAndroid, self).__init__(*args, **kw)
460 self._adb_path = os.path.join(os.environ['ANDROID_HOST_OUT'], 'bin', 'adb')
461 self._device_serial = None
462 adb_devices_out = self._call_adb(['devices'])
463 devices = [l.split()[0] for l in adb_devices_out.splitlines()[1:-1]
464 if l.split()[1] == 'device']
465 if len(devices) == 0:
466 self._device_serial = None
467 else:
468 if len(devices) > 1:
469 self._device_serial = random.choice(devices)
470 else:
471 self._device_serial = devices[0]
472 self._call_adb(['root'])
473 self._to_install = set()
474
475 def target_name(self, target):
476 if target == self.ALL:
477 return self.ALL
478 # The default target is 'droid'. However, we want to use our special target
479 # to build only the gyp target 'all'.
480 if target in (None, self.DEFAULT):
481 return self.ALL
482 return target
483
484 _INSTALLABLE_PREFIX = 'Install: '
485
486 def build(self, gyp_file, target=None, **kw):
487 """
488 Runs a build using the Android makefiles generated from the specified
489 gyp_file. This logic is taken from Android's mmm.
490 """
491 arguments = kw.get('arguments', [])[:]
492 arguments.append(self.target_name(target))
493 arguments.append('-C')
494 arguments.append(os.environ['ANDROID_BUILD_TOP'])
495 kw['arguments'] = arguments
496 chdir = kw.get('chdir', '')
497 makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk')
498 os.environ['ONE_SHOT_MAKEFILE'] = makefile
499 result = self.run(program=self.build_tool, **kw)
500 for l in self.stdout().splitlines():
501 if l.startswith(TestGypAndroid._INSTALLABLE_PREFIX):
502 self._to_install.add(os.path.abspath(os.path.join(
503 os.environ['ANDROID_BUILD_TOP'],
504 l[len(TestGypAndroid._INSTALLABLE_PREFIX):])))
505 del os.environ['ONE_SHOT_MAKEFILE']
506 return result
507
508 def android_module(self, group, name, subdir):
509 if subdir:
510 name = '%s_%s' % (subdir, name)
511 if group == 'SHARED_LIBRARIES':
512 name = 'lib_%s' % name
513 return '%s_gyp' % name
514
515 def intermediates_dir(self, group, module_name):
516 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group,
517 '%s_intermediates' % module_name)
518
519 def built_file_path(self, name, type=None, **kw):
520 """
521 Returns a path to the specified file name, of the specified type,
522 as built by Android. Note that we don't support the configuration
523 parameter.
524 """
525 # Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from
526 # the Android build system.
527 if type == None or type == self.EXECUTABLE:
528 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP',
529 'shared_intermediates', name)
530 subdir = kw.get('subdir')
531 if type == self.STATIC_LIB:
532 group = 'STATIC_LIBRARIES'
533 module_name = self.android_module(group, name, subdir)
534 return os.path.join(self.intermediates_dir(group, module_name),
535 '%s.a' % module_name)
536 if type == self.SHARED_LIB:
537 group = 'SHARED_LIBRARIES'
538 module_name = self.android_module(group, name, subdir)
539 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED',
540 '%s.so' % module_name)
541 assert False, 'Unhandled type'
542
543 def _adb_failure(self, command, msg, stdout, stderr):
544 """ Reports a failed adb command and fails the containing test.
545
546 Args:
547 command: The adb command that failed.
548 msg: The error description.
549 stdout: The standard output.
550 stderr: The standard error.
551 """
552 print '%s failed%s' % (' '.join(command), ': %s' % msg if msg else '')
553 print self.banner('STDOUT ')
554 stdout.seek(0)
555 print stdout.read()
556 print self.banner('STDERR ')
557 stderr.seek(0)
558 print stderr.read()
559 self.fail_test()
560
561 def _call_adb(self, command, timeout=15, retry=3):
562 """ Calls the provided adb command.
563
564 If the command fails, the test fails.
565
566 Args:
567 command: The adb command to call.
568 Returns:
569 The command's output.
570 """
571 with tempfile.TemporaryFile(bufsize=0) as adb_out:
572 with tempfile.TemporaryFile(bufsize=0) as adb_err:
573 adb_command = [self._adb_path]
574 if self._device_serial:
575 adb_command += ['-s', self._device_serial]
576 is_shell = (command[0] == 'shell')
577 if is_shell:
578 command = [command[0], '%s; echo "\n$?";' % ' '.join(command[1:])]
579 adb_command += command
580
581 for attempt in xrange(1, retry + 1):
582 adb_out.seek(0)
583 adb_out.truncate(0)
584 adb_err.seek(0)
585 adb_err.truncate(0)
586 proc = subprocess.Popen(adb_command, stdout=adb_out, stderr=adb_err)
587 deadline = time.time() + timeout
588 timed_out = False
589 while proc.poll() is None and not timed_out:
590 time.sleep(1)
591 timed_out = time.time() > deadline
592 if timed_out:
593 print 'Timeout for command %s (attempt %d of %s)' % (
594 adb_command, attempt, retry)
595 try:
596 proc.kill()
597 except:
598 pass
599 else:
600 break
601
602 if proc.returncode != 0: # returncode is None in the case of a timeout.
603 self._adb_failure(
604 adb_command, 'retcode=%s' % proc.returncode, adb_out, adb_err)
605 return
606
607 adb_out.seek(0)
608 output = adb_out.read()
609 if is_shell:
610 output = output.splitlines(True)
611 try:
612 output[-2] = output[-2].rstrip('\r\n')
613 output, rc = (''.join(output[:-1]), int(output[-1]))
614 except ValueError:
615 self._adb_failure(adb_command, 'unexpected output format',
616 adb_out, adb_err)
617 if rc != 0:
618 self._adb_failure(adb_command, 'exited with %d' % rc, adb_out,
619 adb_err)
620 return output
621
622 def run_built_executable(self, name, *args, **kw):
623 """
624 Runs an executable program built from a gyp-generated configuration.
625 """
626 match = kw.pop('match', self.match)
627
628 executable_file = self.built_file_path(name, type=self.EXECUTABLE, **kw)
629 if executable_file not in self._to_install:
630 self.fail_test()
631
632 if not self._device_serial:
633 self.skip_test(message='No devices attached.\n')
634
635 storage = self._call_adb(['shell', 'echo', '$ANDROID_DATA']).strip()
636 if not len(storage):
637 self.fail_test()
638
639 installed = set()
640 try:
641 for i in self._to_install:
642 a = os.path.abspath(
643 os.path.join(os.environ['ANDROID_BUILD_TOP'], i))
644 dest = '%s/%s' % (storage, os.path.basename(a))
645 self._call_adb(['push', os.path.abspath(a), dest])
646 installed.add(dest)
647 if i == executable_file:
648 device_executable = dest
649 self._call_adb(['shell', 'chmod', '755', device_executable])
650
651 out = self._call_adb(
652 ['shell', 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%s' % storage,
653 device_executable],
654 timeout=60,
655 retry=1)
656 out = out.replace('\r\n', '\n')
657 self._complete(out, kw.pop('stdout', None), None, None, None, match)
658 finally:
659 if len(installed):
660 self._call_adb(['shell', 'rm'] + list(installed))
661
662 def match_single_line(self, lines = None, expected_line = None):
663 """
664 Checks that specified line appears in the text.
665 """
666 for line in lines.split('\n'):
667 if line == expected_line:
668 return 1
669 return
670
671 def up_to_date(self, gyp_file, target=None, **kw):
672 """
673 Verifies that a build of the specified target is up to date.
674 """
675 kw['stdout'] = ("make: Nothing to be done for `%s'." %
676 self.target_name(target))
677
678 # We need to supply a custom matcher, since we don't want to depend on the
679 # exact stdout string.
680 kw['match'] = self.match_single_line
681 return self.build(gyp_file, target, **kw)
682
683
684 class TestGypCMake(TestGypBase): 411 class TestGypCMake(TestGypBase):
685 """ 412 """
686 Subclass for testing the GYP CMake generator, using cmake's ninja backend. 413 Subclass for testing the GYP CMake generator, using cmake's ninja backend.
687 """ 414 """
688 format = 'cmake' 415 format = 'cmake'
689 build_tool_list = ['cmake'] 416 build_tool_list = ['cmake']
690 ALL = 'all' 417 ALL = 'all'
691 418
692 def cmake_build(self, gyp_file, target=None, **kw): 419 def cmake_build(self, gyp_file, target=None, **kw):
693 arguments = kw.get('arguments', [])[:] 420 arguments = kw.get('arguments', [])[:]
(...skipping 698 matching lines...) Expand 10 before | Expand all | Expand 10 after
1392 """ 1119 """
1393 configuration = self.configuration_dirname() 1120 configuration = self.configuration_dirname()
1394 os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration) 1121 os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration)
1395 # Enclosing the name in a list avoids prepending the original dir. 1122 # Enclosing the name in a list avoids prepending the original dir.
1396 program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)] 1123 program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
1397 return self.run(program=program, *args, **kw) 1124 return self.run(program=program, *args, **kw)
1398 1125
1399 1126
1400 format_class_list = [ 1127 format_class_list = [
1401 TestGypGypd, 1128 TestGypGypd,
1402 TestGypAndroid,
1403 TestGypCMake, 1129 TestGypCMake,
1404 TestGypMake, 1130 TestGypMake,
1405 TestGypMSVS, 1131 TestGypMSVS,
1406 TestGypMSVSNinja, 1132 TestGypMSVSNinja,
1407 TestGypNinja, 1133 TestGypNinja,
1408 TestGypXcode, 1134 TestGypXcode,
1409 TestGypXcodeNinja, 1135 TestGypXcodeNinja,
1410 ] 1136 ]
1411 1137
1412 def TestGyp(*args, **kw): 1138 def TestGyp(*args, **kw):
1413 """ 1139 """
1414 Returns an appropriate TestGyp* instance for a specified GYP format. 1140 Returns an appropriate TestGyp* instance for a specified GYP format.
1415 """ 1141 """
1416 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) 1142 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT'))
1417 for format_class in format_class_list: 1143 for format_class in format_class_list:
1418 if format == format_class.format: 1144 if format == format_class.format:
1419 return format_class(*args, **kw) 1145 return format_class(*args, **kw)
1420 raise Exception, "unknown format %r" % format 1146 raise Exception, "unknown format %r" % format
OLDNEW
« no previous file with comments | « test/hello/gyptest-regyp-output.py ('k') | test/library/gyptest-shared.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698