| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 Google Inc. All rights reserved. | 2 # Copyright (c) 2012 Google Inc. 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 """Utility functions to perform Xcode-style build steps. | 6 """Utility functions to perform Xcode-style build steps. |
| 7 | 7 |
| 8 These functions are executed via gyp-mac-tool when using the Makefile generator. | 8 These functions are executed via gyp-mac-tool when using the Makefile generator. |
| 9 """ | 9 """ |
| 10 | 10 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 if len(args) < 1: | 38 if len(args) < 1: |
| 39 raise Exception("Not enough arguments") | 39 raise Exception("Not enough arguments") |
| 40 | 40 |
| 41 method = "Exec%s" % self._CommandifyName(args[0]) | 41 method = "Exec%s" % self._CommandifyName(args[0]) |
| 42 return getattr(self, method)(*args[1:]) | 42 return getattr(self, method)(*args[1:]) |
| 43 | 43 |
| 44 def _CommandifyName(self, name_string): | 44 def _CommandifyName(self, name_string): |
| 45 """Transforms a tool name like copy-info-plist to CopyInfoPlist""" | 45 """Transforms a tool name like copy-info-plist to CopyInfoPlist""" |
| 46 return name_string.title().replace('-', '') | 46 return name_string.title().replace('-', '') |
| 47 | 47 |
| 48 def ExecCopyBundleResource(self, source, dest): | 48 def ExecCopyBundleResource(self, source, dest, convert_to_binary): |
| 49 """Copies a resource file to the bundle/Resources directory, performing any | 49 """Copies a resource file to the bundle/Resources directory, performing any |
| 50 necessary compilation on each resource.""" | 50 necessary compilation on each resource.""" |
| 51 extension = os.path.splitext(source)[1].lower() | 51 extension = os.path.splitext(source)[1].lower() |
| 52 if os.path.isdir(source): | 52 if os.path.isdir(source): |
| 53 # Copy tree. | 53 # Copy tree. |
| 54 # TODO(thakis): This copies file attributes like mtime, while the | 54 # TODO(thakis): This copies file attributes like mtime, while the |
| 55 # single-file branch below doesn't. This should probably be changed to | 55 # single-file branch below doesn't. This should probably be changed to |
| 56 # be consistent with the single-file branch. | 56 # be consistent with the single-file branch. |
| 57 if os.path.exists(dest): | 57 if os.path.exists(dest): |
| 58 shutil.rmtree(dest) | 58 shutil.rmtree(dest) |
| 59 shutil.copytree(source, dest) | 59 shutil.copytree(source, dest) |
| 60 elif extension == '.xib': | 60 elif extension == '.xib': |
| 61 return self._CopyXIBFile(source, dest) | 61 return self._CopyXIBFile(source, dest) |
| 62 elif extension == '.storyboard': | 62 elif extension == '.storyboard': |
| 63 return self._CopyXIBFile(source, dest) | 63 return self._CopyXIBFile(source, dest) |
| 64 elif extension == '.strings': | 64 elif extension == '.strings': |
| 65 self._CopyStringsFile(source, dest) | 65 self._CopyStringsFile(source, dest, convert_to_binary) |
| 66 else: | 66 else: |
| 67 shutil.copy(source, dest) | 67 shutil.copy(source, dest) |
| 68 | 68 |
| 69 def _CopyXIBFile(self, source, dest): | 69 def _CopyXIBFile(self, source, dest): |
| 70 """Compiles a XIB file with ibtool into a binary plist in the bundle.""" | 70 """Compiles a XIB file with ibtool into a binary plist in the bundle.""" |
| 71 | 71 |
| 72 # ibtool sometimes crashes with relative paths. See crbug.com/314728. | 72 # ibtool sometimes crashes with relative paths. See crbug.com/314728. |
| 73 base = os.path.dirname(os.path.realpath(__file__)) | 73 base = os.path.dirname(os.path.realpath(__file__)) |
| 74 if os.path.relpath(source): | 74 if os.path.relpath(source): |
| 75 source = os.path.join(base, source) | 75 source = os.path.join(base, source) |
| 76 if os.path.relpath(dest): | 76 if os.path.relpath(dest): |
| 77 dest = os.path.join(base, dest) | 77 dest = os.path.join(base, dest) |
| 78 | 78 |
| 79 args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', | 79 args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', |
| 80 '--output-format', 'human-readable-text', '--compile', dest, source] | 80 '--output-format', 'human-readable-text', '--compile', dest, source] |
| 81 ibtool_section_re = re.compile(r'/\*.*\*/') | 81 ibtool_section_re = re.compile(r'/\*.*\*/') |
| 82 ibtool_re = re.compile(r'.*note:.*is clipping its content') | 82 ibtool_re = re.compile(r'.*note:.*is clipping its content') |
| 83 ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) | 83 ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) |
| 84 current_section_header = None | 84 current_section_header = None |
| 85 for line in ibtoolout.stdout: | 85 for line in ibtoolout.stdout: |
| 86 if ibtool_section_re.match(line): | 86 if ibtool_section_re.match(line): |
| 87 current_section_header = line | 87 current_section_header = line |
| 88 elif not ibtool_re.match(line): | 88 elif not ibtool_re.match(line): |
| 89 if current_section_header: | 89 if current_section_header: |
| 90 sys.stdout.write(current_section_header) | 90 sys.stdout.write(current_section_header) |
| 91 current_section_header = None | 91 current_section_header = None |
| 92 sys.stdout.write(line) | 92 sys.stdout.write(line) |
| 93 return ibtoolout.returncode | 93 return ibtoolout.returncode |
| 94 | 94 |
| 95 def _CopyStringsFile(self, source, dest): | 95 def _ConvertToBinary(self, dest): |
| 96 subprocess.check_call([ |
| 97 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest]) |
| 98 |
| 99 def _CopyStringsFile(self, source, dest, convert_to_binary): |
| 96 """Copies a .strings file using iconv to reconvert the input into UTF-16.""" | 100 """Copies a .strings file using iconv to reconvert the input into UTF-16.""" |
| 97 input_code = self._DetectInputEncoding(source) or "UTF-8" | 101 input_code = self._DetectInputEncoding(source) or "UTF-8" |
| 98 | 102 |
| 99 # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call | 103 # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call |
| 100 # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints | 104 # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints |
| 101 # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing | 105 # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing |
| 102 # semicolon in dictionary. | 106 # semicolon in dictionary. |
| 103 # on invalid files. Do the same kind of validation. | 107 # on invalid files. Do the same kind of validation. |
| 104 import CoreFoundation | 108 import CoreFoundation |
| 105 s = open(source, 'rb').read() | 109 s = open(source, 'rb').read() |
| 106 d = CoreFoundation.CFDataCreate(None, s, len(s)) | 110 d = CoreFoundation.CFDataCreate(None, s, len(s)) |
| 107 _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) | 111 _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) |
| 108 if error: | 112 if error: |
| 109 return | 113 return |
| 110 | 114 |
| 111 fp = open(dest, 'wb') | 115 fp = open(dest, 'wb') |
| 112 fp.write(s.decode(input_code).encode('UTF-16')) | 116 fp.write(s.decode(input_code).encode('UTF-16')) |
| 113 fp.close() | 117 fp.close() |
| 114 | 118 |
| 119 if convert_to_binary == 'True': |
| 120 self._ConvertToBinary(dest) |
| 121 |
| 115 def _DetectInputEncoding(self, file_name): | 122 def _DetectInputEncoding(self, file_name): |
| 116 """Reads the first few bytes from file_name and tries to guess the text | 123 """Reads the first few bytes from file_name and tries to guess the text |
| 117 encoding. Returns None as a guess if it can't detect it.""" | 124 encoding. Returns None as a guess if it can't detect it.""" |
| 118 fp = open(file_name, 'rb') | 125 fp = open(file_name, 'rb') |
| 119 try: | 126 try: |
| 120 header = fp.read(3) | 127 header = fp.read(3) |
| 121 except e: | 128 except e: |
| 122 fp.close() | 129 fp.close() |
| 123 return None | 130 return None |
| 124 fp.close() | 131 fp.close() |
| 125 if header.startswith("\xFE\xFF"): | 132 if header.startswith("\xFE\xFF"): |
| 126 return "UTF-16" | 133 return "UTF-16" |
| 127 elif header.startswith("\xFF\xFE"): | 134 elif header.startswith("\xFF\xFE"): |
| 128 return "UTF-16" | 135 return "UTF-16" |
| 129 elif header.startswith("\xEF\xBB\xBF"): | 136 elif header.startswith("\xEF\xBB\xBF"): |
| 130 return "UTF-8" | 137 return "UTF-8" |
| 131 else: | 138 else: |
| 132 return None | 139 return None |
| 133 | 140 |
| 134 def ExecCopyInfoPlist(self, source, dest, *keys): | 141 def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): |
| 135 """Copies the |source| Info.plist to the destination directory |dest|.""" | 142 """Copies the |source| Info.plist to the destination directory |dest|.""" |
| 136 # Read the source Info.plist into memory. | 143 # Read the source Info.plist into memory. |
| 137 fd = open(source, 'r') | 144 fd = open(source, 'r') |
| 138 lines = fd.read() | 145 lines = fd.read() |
| 139 fd.close() | 146 fd.close() |
| 140 | 147 |
| 141 # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). | 148 # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). |
| 142 plist = plistlib.readPlistFromString(lines) | 149 plist = plistlib.readPlistFromString(lines) |
| 143 if keys: | 150 if keys: |
| 144 plist = dict(plist.items() + json.loads(keys[0]).items()) | 151 plist = dict(plist.items() + json.loads(keys[0]).items()) |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 | 185 |
| 179 # Write out the file with variables replaced. | 186 # Write out the file with variables replaced. |
| 180 fd = open(dest, 'w') | 187 fd = open(dest, 'w') |
| 181 fd.write(lines) | 188 fd.write(lines) |
| 182 fd.close() | 189 fd.close() |
| 183 | 190 |
| 184 # Now write out PkgInfo file now that the Info.plist file has been | 191 # Now write out PkgInfo file now that the Info.plist file has been |
| 185 # "compiled". | 192 # "compiled". |
| 186 self._WritePkgInfo(dest) | 193 self._WritePkgInfo(dest) |
| 187 | 194 |
| 195 if convert_to_binary == 'True': |
| 196 self._ConvertToBinary(dest) |
| 197 |
| 188 def _WritePkgInfo(self, info_plist): | 198 def _WritePkgInfo(self, info_plist): |
| 189 """This writes the PkgInfo file from the data stored in Info.plist.""" | 199 """This writes the PkgInfo file from the data stored in Info.plist.""" |
| 190 plist = plistlib.readPlist(info_plist) | 200 plist = plistlib.readPlist(info_plist) |
| 191 if not plist: | 201 if not plist: |
| 192 return | 202 return |
| 193 | 203 |
| 194 # Only create PkgInfo for executable types. | 204 # Only create PkgInfo for executable types. |
| 195 package_type = plist['CFBundlePackageType'] | 205 package_type = plist['CFBundlePackageType'] |
| 196 if package_type != 'APPL': | 206 if package_type != 'APPL': |
| 197 return | 207 return |
| (...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 data = data.replace('$(%s)' % key, value) | 601 data = data.replace('$(%s)' % key, value) |
| 592 return data | 602 return data |
| 593 if isinstance(data, list): | 603 if isinstance(data, list): |
| 594 return [self._ExpandVariables(v, substitutions) for v in data] | 604 return [self._ExpandVariables(v, substitutions) for v in data] |
| 595 if isinstance(data, dict): | 605 if isinstance(data, dict): |
| 596 return {k: self._ExpandVariables(data[k], substitutions) for k in data} | 606 return {k: self._ExpandVariables(data[k], substitutions) for k in data} |
| 597 return data | 607 return data |
| 598 | 608 |
| 599 if __name__ == '__main__': | 609 if __name__ == '__main__': |
| 600 sys.exit(main(sys.argv[1:])) | 610 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |