1#!/usr/bin/env python 2 3# Copyright (c) 2012 Google Inc. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7""" 8Verifies that device and simulator bundles are built correctly. 9""" 10 11import plistlib 12import TestGyp 13import os 14import struct 15import subprocess 16import sys 17import tempfile 18import TestMac 19 20def CheckFileType(file, expected): 21 proc = subprocess.Popen(['lipo', '-info', file], stdout=subprocess.PIPE) 22 o = proc.communicate()[0].strip() 23 assert not proc.returncode 24 if not expected in o: 25 print 'File: Expected %s, got %s' % (expected, o) 26 test.fail_test() 27 28def HasCerts(): 29 # Because the bots do not have certs, don't check them if there are no 30 # certs available. 31 proc = subprocess.Popen(['security','find-identity','-p', 'codesigning', 32 '-v'], stdout=subprocess.PIPE) 33 return "0 valid identities found" not in proc.communicate()[0].strip() 34 35def CheckSignature(file): 36 proc = subprocess.Popen(['codesign', '-v', file], stdout=subprocess.PIPE) 37 o = proc.communicate()[0].strip() 38 assert not proc.returncode 39 if "code object is not signed at all" in o: 40 print 'File %s not properly signed.' % (file) 41 test.fail_test() 42 43def CheckEntitlements(file, expected_entitlements): 44 with tempfile.NamedTemporaryFile() as temp: 45 proc = subprocess.Popen(['codesign', '--display', '--entitlements', 46 temp.name, file], stdout=subprocess.PIPE) 47 o = proc.communicate()[0].strip() 48 assert not proc.returncode 49 data = temp.read() 50 entitlements = ParseEntitlements(data) 51 if not entitlements: 52 print 'No valid entitlements found in %s.' % (file) 53 test.fail_test() 54 if entitlements != expected_entitlements: 55 print 'Unexpected entitlements found in %s.' % (file) 56 test.fail_test() 57 58def ParseEntitlements(data): 59 if len(data) < 8: 60 return None 61 magic, length = struct.unpack('>II', data[:8]) 62 if magic != 0xfade7171 or length != len(data): 63 return None 64 return data[8:] 65 66def GetXcodeVersionValue(type): 67 args = ['xcodebuild', '-version', '-sdk', 'iphoneos', type] 68 job = subprocess.Popen(args, stdout=subprocess.PIPE) 69 return job.communicate()[0].strip() 70 71def GetMachineBuild(): 72 args = ['sw_vers', '-buildVersion'] 73 job = subprocess.Popen(args, stdout=subprocess.PIPE) 74 return job.communicate()[0].strip() 75 76def CheckPlistvalue(plist, key, expected): 77 if key not in plist: 78 print '%s not set in plist' % key 79 test.fail_test() 80 return 81 actual = plist[key] 82 if actual != expected: 83 print 'File: Expected %s, got %s for %s' % (expected, actual, key) 84 test.fail_test() 85 86def CheckPlistNotSet(plist, key): 87 if key in plist: 88 print '%s should not be set in plist' % key 89 test.fail_test() 90 return 91 92def ConvertBinaryPlistToXML(path): 93 proc = subprocess.call(['plutil', '-convert', 'xml1', path], 94 stdout=subprocess.PIPE) 95 96if sys.platform == 'darwin': 97 test = TestGyp.TestGyp(formats=['ninja', 'xcode']) 98 99 test.run_gyp('test-device.gyp', chdir='app-bundle') 100 101 test_configs = ['Default-iphoneos', 'Default'] 102 for configuration in test_configs: 103 test.set_configuration(configuration) 104 test.build('test-device.gyp', 'test_app', chdir='app-bundle') 105 result_file = test.built_file_path('Test App Gyp.app/Test App Gyp', 106 chdir='app-bundle') 107 test.must_exist(result_file) 108 info_plist = test.built_file_path('Test App Gyp.app/Info.plist', 109 chdir='app-bundle') 110 plist = plistlib.readPlist(info_plist) 111 xcode_version = TestMac.Xcode.Version() 112 if xcode_version >= '0720': 113 if len(plist) != 23: 114 print 'plist should have 23 entries, but it has %s' % len(plist) 115 test.fail_test() 116 117 # Values that will hopefully never change. 118 CheckPlistvalue(plist, 'CFBundleDevelopmentRegion', 'English') 119 CheckPlistvalue(plist, 'CFBundleExecutable', 'Test App Gyp') 120 CheckPlistvalue(plist, 'CFBundleIdentifier', 'com.google.Test App Gyp') 121 CheckPlistvalue(plist, 'CFBundleInfoDictionaryVersion', '6.0') 122 CheckPlistvalue(plist, 'CFBundleName', 'Test App Gyp') 123 CheckPlistvalue(plist, 'CFBundlePackageType', 'APPL') 124 CheckPlistvalue(plist, 'CFBundleShortVersionString', '1.0') 125 CheckPlistvalue(plist, 'CFBundleSignature', 'ause') 126 CheckPlistvalue(plist, 'CFBundleVersion', '1') 127 CheckPlistvalue(plist, 'NSMainNibFile', 'MainMenu') 128 CheckPlistvalue(plist, 'NSPrincipalClass', 'NSApplication') 129 CheckPlistvalue(plist, 'UIDeviceFamily', [1, 2]) 130 131 # Values that get pulled from xcodebuild. 132 machine_build = GetMachineBuild() 133 platform_version = GetXcodeVersionValue('ProductVersion') 134 sdk_build = GetXcodeVersionValue('ProductBuildVersion') 135 xcode_build = TestMac.Xcode.Build() 136 137 # Xcode keeps changing what gets included in executable plists, and it 138 # changes between device and simuator builds. Allow the strictest tests for 139 # Xcode 7.2 and above. 140 if xcode_version >= '0720': 141 CheckPlistvalue(plist, 'BuildMachineOSBuild', machine_build) 142 CheckPlistvalue(plist, 'DTCompiler', 'com.apple.compilers.llvm.clang.1_0') 143 CheckPlistvalue(plist, 'DTPlatformVersion', platform_version) 144 CheckPlistvalue(plist, 'DTSDKBuild', sdk_build) 145 CheckPlistvalue(plist, 'DTXcode', xcode_version) 146 CheckPlistvalue(plist, 'DTXcodeBuild', xcode_build) 147 CheckPlistvalue(plist, 'MinimumOSVersion', '8.0') 148 149 150 if configuration == 'Default-iphoneos': 151 platform_name = 'iphoneos' 152 CheckFileType(result_file, 'armv7') 153 CheckPlistvalue(plist, 'CFBundleSupportedPlatforms', ['iPhoneOS']) 154 # Apple keeps changing their mind. 155 if xcode_version >= '0720': 156 CheckPlistvalue(plist, 'DTPlatformBuild', sdk_build) 157 else: 158 platform_name = 'iphonesimulator' 159 CheckFileType(result_file, 'i386') 160 CheckPlistvalue(plist, 'CFBundleSupportedPlatforms', ['iPhoneSimulator']) 161 if xcode_version >= '0720': 162 CheckPlistvalue(plist, 'DTPlatformBuild', '') 163 164 CheckPlistvalue(plist, 'DTPlatformName', platform_name) 165 CheckPlistvalue(plist, 'DTSDKName', platform_name + platform_version) 166 167 168 if HasCerts() and configuration == 'Default-iphoneos': 169 test.build('test-device.gyp', 'sig_test', chdir='app-bundle') 170 result_file = test.built_file_path('sigtest.app/sigtest', 171 chdir='app-bundle') 172 CheckSignature(result_file) 173 info_plist = test.built_file_path('sigtest.app/Info.plist', 174 chdir='app-bundle') 175 176 plist = plistlib.readPlist(info_plist) 177 CheckPlistvalue(plist, 'UIDeviceFamily', [1]) 178 179 entitlements_file = test.built_file_path('sig_test.xcent', 180 chdir='app-bundle') 181 if os.path.isfile(entitlements_file): 182 expected_entitlements = open(entitlements_file).read() 183 CheckEntitlements(result_file, expected_entitlements) 184 185 test.pass_test() 186