• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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