361
408
super(TestGypCustom, self).__init__(*args, **kw)
364
class TestGypAndroid(TestGypBase):
366
Subclass for testing the GYP Android makefile generator. Note that
367
build/envsetup.sh and lunch must have been run before running tests.
369
TODO: This is currently an incomplete implementation. We do not support
370
run_built_executable(), so we pass only tests which do not use this. As a
371
result, support for host targets is not properly tested.
375
# Note that we can't use mmm as the build tool because ...
376
# - it builds all targets, whereas we need to pass a target
377
# - it is a function, whereas the test runner assumes the build tool is a file
378
# Instead we use make and duplicate the logic from mmm.
379
build_tool_list = ['make']
381
# We use our custom target 'gyp_all_modules', as opposed to the 'all_modules'
382
# target used by mmm, to build only those targets which are part of the gyp
384
ALL = 'gyp_all_modules'
386
def __init__(self, gyp=None, *args, **kw):
387
# Android requires build and test output to be inside its source tree.
388
# We use the following working directory for the test's source, but the
389
# test's build output still goes to $ANDROID_PRODUCT_OUT.
390
# Note that some tests explicitly set format='gypd' to invoke the gypd
391
# backend. This writes to the source tree, but there's no way around this.
392
kw['workdir'] = os.path.join('/tmp', 'gyptest',
393
kw.get('workdir', 'testworkarea'))
394
# We need to remove all gyp outputs from out/. Ths is because some tests
395
# don't have rules to regenerate output, so they will simply re-use stale
396
# output if present. Since the test working directory gets regenerated for
397
# each test run, this can confuse things.
398
# We don't have a list of build outputs because we don't know which
399
# dependent targets were built. Instead we delete all gyp-generated output.
400
# This may be excessive, but should be safe.
401
out_dir = os.environ['ANDROID_PRODUCT_OUT']
402
obj_dir = os.path.join(out_dir, 'obj')
403
shutil.rmtree(os.path.join(obj_dir, 'GYP'), ignore_errors = True)
404
for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']:
405
for d in os.listdir(os.path.join(obj_dir, x)):
406
if d.endswith('_gyp_intermediates'):
407
shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True)
408
for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]:
409
for d in os.listdir(os.path.join(out_dir, x)):
410
if d.endswith('_gyp.so'):
411
os.remove(os.path.join(out_dir, x, d))
413
super(TestGypAndroid, self).__init__(*args, **kw)
415
def target_name(self, target):
416
if target == self.ALL:
418
# The default target is 'droid'. However, we want to use our special target
419
# to build only the gyp target 'all'.
420
if target in (None, self.DEFAULT):
424
def build(self, gyp_file, target=None, **kw):
426
Runs a build using the Android makefiles generated from the specified
427
gyp_file. This logic is taken from Android's mmm.
429
arguments = kw.get('arguments', [])[:]
430
arguments.append(self.target_name(target))
411
class TestGypCMake(TestGypBase):
413
Subclass for testing the GYP CMake generator, using cmake's ninja backend.
416
build_tool_list = ['cmake']
419
def cmake_build(self, gyp_file, target=None, **kw):
420
arguments = kw.get('arguments', [])[:]
422
self.build_tool_list = ['cmake']
423
self.initialize_build_tool()
425
chdir = os.path.join(kw.get('chdir', '.'),
427
self.configuration_dirname())
430
arguments.append('-G')
431
arguments.append('Ninja')
433
kw['arguments'] = arguments
435
stderr = kw.get('stderr', None)
437
kw['stderr'] = stderr.split('$$$')[0]
439
self.run(program=self.build_tool, **kw)
441
def ninja_build(self, gyp_file, target=None, **kw):
442
arguments = kw.get('arguments', [])[:]
444
self.build_tool_list = ['ninja']
445
self.initialize_build_tool()
447
# Add a -C output/path to the command line.
431
448
arguments.append('-C')
432
arguments.append(os.environ['ANDROID_BUILD_TOP'])
449
arguments.append(os.path.join('out', self.configuration_dirname()))
451
if target not in (None, self.DEFAULT):
452
arguments.append(target)
433
454
kw['arguments'] = arguments
434
chdir = kw.get('chdir', '')
435
makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk')
436
os.environ['ONE_SHOT_MAKEFILE'] = makefile
437
result = self.run(program=self.build_tool, **kw)
438
del os.environ['ONE_SHOT_MAKEFILE']
441
def android_module(self, group, name, subdir):
443
name = '%s_%s' % (subdir, name)
444
if group == 'SHARED_LIBRARIES':
445
name = 'lib_%s' % name
446
return '%s_gyp' % name
448
def intermediates_dir(self, group, module_name):
449
return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group,
450
'%s_intermediates' % module_name)
456
stderr = kw.get('stderr', None)
458
stderrs = stderr.split('$$$')
459
kw['stderr'] = stderrs[1] if len(stderrs) > 1 else ''
461
return self.run(program=self.build_tool, **kw)
463
def build(self, gyp_file, target=None, status=0, **kw):
464
# Two tools must be run to build, cmake and the ninja.
465
# Allow cmake to succeed when the overall expectation is to fail.
469
if not isinstance(status, collections.Iterable): status = (status,)
470
kw['status'] = list(itertools.chain((0,), status))
471
self.cmake_build(gyp_file, target, **kw)
472
kw['status'] = status
473
self.ninja_build(gyp_file, target, **kw)
475
def run_built_executable(self, name, *args, **kw):
476
# Enclosing the name in a list avoids prepending the original dir.
477
program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
478
if sys.platform == 'darwin':
479
configuration = self.configuration_dirname()
480
os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration)
481
return self.run(program=program, *args, **kw)
452
483
def built_file_path(self, name, type=None, **kw):
454
Returns a path to the specified file name, of the specified type,
455
as built by Android. Note that we don't support the configuration
458
# Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from
459
# the Android build system.
461
return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP',
462
'shared_intermediates', name)
485
chdir = kw.get('chdir')
489
result.append(self.configuration_dirname())
490
if type == self.STATIC_LIB:
491
if sys.platform != 'darwin':
492
result.append('obj.target')
493
elif type == self.SHARED_LIB:
494
if sys.platform != 'darwin' and sys.platform != 'win32':
495
result.append('lib.target')
463
496
subdir = kw.get('subdir')
464
if type == self.EXECUTABLE:
465
# We don't install executables
466
group = 'EXECUTABLES'
467
module_name = self.android_module(group, name, subdir)
468
return os.path.join(self.intermediates_dir(group, module_name), name)
469
if type == self.STATIC_LIB:
470
group = 'STATIC_LIBRARIES'
471
module_name = self.android_module(group, name, subdir)
472
return os.path.join(self.intermediates_dir(group, module_name),
473
'%s.a' % module_name)
474
if type == self.SHARED_LIB:
475
group = 'SHARED_LIBRARIES'
476
module_name = self.android_module(group, name, subdir)
477
return os.path.join(self.intermediates_dir(group, module_name), 'LINKED',
478
'%s.so' % module_name)
479
assert False, 'Unhandled type'
481
def run_built_executable(self, name, *args, **kw):
483
Runs an executable program built from a gyp-generated configuration.
485
This is not correctly implemented for Android. For now, we simply check
486
that the executable file exists.
488
# Running executables requires a device. Even if we build for target x86,
489
# the binary is not built with the correct toolchain options to actually
492
# Copied from TestCommon.run()
493
match = kw.pop('match', self.match)
495
if os.path.exists(self.built_file_path(name)):
497
self._complete(None, None, None, None, status, match)
499
def match_single_line(self, lines = None, expected_line = None):
501
Checks that specified line appears in the text.
503
for line in lines.split('\n'):
504
if line == expected_line:
497
if subdir and type != self.SHARED_LIB:
498
result.append(subdir)
499
result.append(self.built_file_basename(name, type, **kw))
500
return self.workpath(*result)
508
502
def up_to_date(self, gyp_file, target=None, **kw):
510
Verifies that a build of the specified target is up to date.
512
kw['stdout'] = ("make: Nothing to be done for `%s'." %
513
self.target_name(target))
503
result = self.ninja_build(gyp_file, target, **kw)
505
stdout = self.stdout()
506
if 'ninja: no work to do' not in stdout:
507
self.report_not_up_to_date()
515
# We need to supply a custom matcher, since we don't want to depend on the
516
# exact stdout string.
517
kw['match'] = self.match_single_line
518
return self.build(gyp_file, target, **kw)
520
512
class TestGypMake(TestGypBase):
866
896
return self.workpath(*result)
899
class TestGypMSVSNinja(TestGypNinja):
901
Subclass for testing the GYP Visual Studio Ninja generator.
903
format = 'msvs-ninja'
905
def initialize_build_tool(self):
906
super(TestGypMSVSNinja, self).initialize_build_tool()
907
# When using '--build', make sure ninja is first in the format list.
908
self.formats.insert(0, 'ninja')
910
def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw):
912
Runs a Visual Studio build using the configuration generated
913
from the specified gyp_file.
915
arguments = kw.get('arguments', [])[:]
916
if target in (None, self.ALL, self.DEFAULT):
917
# Note: the Visual Studio generator doesn't add an explicit 'all' target.
918
# This will build each project. This will work if projects are hermetic,
919
# but may fail if they are not (a project may run more than once).
920
# It would be nice to supply an all.metaproj for MSBuild.
921
arguments.extend([gyp_file.replace('.gyp', '.sln')])
923
# MSBuild documentation claims that one can specify a sln but then build a
924
# project target like 'msbuild a.sln /t:proj:target' but this format only
925
# supports 'Clean', 'Rebuild', and 'Publish' (with none meaning Default).
926
# This limitation is due to the .sln -> .sln.metaproj conversion.
927
# The ':' is not special, 'proj:target' is a target in the metaproj.
928
arguments.extend([target+'.vcxproj'])
936
arguments.extend(['/target:'+build])
937
configuration = self.configuration_buildname()
938
config = configuration.split('|')
939
arguments.extend(['/property:Configuration='+config[0]])
941
arguments.extend(['/property:Platform='+config[1]])
942
arguments.extend(['/property:BuildInParallel=false'])
943
arguments.extend(['/verbosity:minimal'])
945
kw['arguments'] = arguments
946
return self.run(program=self.msbuild_path, **kw)
869
949
class TestGypXcode(TestGypBase):
871
951
Subclass for testing the GYP Xcode generator.
975
1056
return self.workpath(*result)
1059
class TestGypXcodeNinja(TestGypXcode):
1061
Subclass for testing the GYP Xcode Ninja generator.
1063
format = 'xcode-ninja'
1065
def initialize_build_tool(self):
1066
super(TestGypXcodeNinja, self).initialize_build_tool()
1067
# When using '--build', make sure ninja is first in the format list.
1068
self.formats.insert(0, 'ninja')
1070
def build(self, gyp_file, target=None, **kw):
1072
Runs an xcodebuild using the .xcodeproj generated from the specified
1075
build_config = self.configuration
1076
if build_config and build_config.endswith(('-iphoneos',
1077
'-iphonesimulator')):
1078
build_config, sdk = self.configuration.split('-')
1079
kw['arguments'] = kw.get('arguments', []) + ['-sdk', sdk]
1081
with self._build_configuration(build_config):
1082
return super(TestGypXcodeNinja, self).build(
1083
gyp_file.replace('.gyp', '.ninja.gyp'), target, **kw)
1086
def _build_configuration(self, build_config):
1087
config = self.configuration
1088
self.configuration = build_config
1092
self.configuration = config
1094
def built_file_path(self, name, type=None, **kw):
1096
chdir = kw.get('chdir')
1098
result.append(chdir)
1099
result.append('out')
1100
result.append(self.configuration_dirname())
1101
subdir = kw.get('subdir')
1102
if subdir and type != self.SHARED_LIB:
1103
result.append(subdir)
1104
result.append(self.built_file_basename(name, type, **kw))
1105
return self.workpath(*result)
1107
def up_to_date(self, gyp_file, target=None, **kw):
1108
result = self.build(gyp_file, target, **kw)
1110
stdout = self.stdout()
1111
if 'ninja: no work to do' not in stdout:
1112
self.report_not_up_to_date()
1116
def run_built_executable(self, name, *args, **kw):
1118
Runs an executable built by xcodebuild + ninja.
1120
configuration = self.configuration_dirname()
1121
os.environ['DYLD_LIBRARY_PATH'] = os.path.join('out', configuration)
1122
# Enclosing the name in a list avoids prepending the original dir.
1123
program = [self.built_file_path(name, type=self.EXECUTABLE, **kw)]
1124
return self.run(program=program, *args, **kw)
978
1127
format_class_list = [
987
1138
def TestGyp(*args, **kw):