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

Side by Side Diff: pylib/gyp/mac_tool.py

Issue 1745173002: Add support for iOS Frameworks with header maps. (Closed) Base URL: https://chromium.googlesource.com/external/gyp.git@master
Patch Set: Spacing nits Created 4 years, 9 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
OLDNEW
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
11 import fcntl 11 import fcntl
12 import fnmatch 12 import fnmatch
13 import glob 13 import glob
14 import json 14 import json
15 import os 15 import os
16 import plistlib 16 import plistlib
17 import re 17 import re
18 import shutil 18 import shutil
19 import string 19 import string
20 import struct
20 import subprocess 21 import subprocess
21 import sys 22 import sys
22 import tempfile 23 import tempfile
23 24
24 25
25 def main(args): 26 def main(args):
26 executor = MacTool() 27 executor = MacTool()
27 exit_code = executor.Dispatch(args) 28 exit_code = executor.Dispatch(args)
28 if exit_code is not None: 29 if exit_code is not None:
29 sys.exit(exit_code) 30 sys.exit(exit_code)
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 print >>sys.stderr, line 266 print >>sys.stderr, line
266 # Unconditionally touch the output .a file on the command line if present 267 # Unconditionally touch the output .a file on the command line if present
267 # and the command succeeded. A bit hacky. 268 # and the command succeeded. A bit hacky.
268 if not libtoolout.returncode: 269 if not libtoolout.returncode:
269 for i in range(len(cmd_list) - 1): 270 for i in range(len(cmd_list) - 1):
270 if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): 271 if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'):
271 os.utime(cmd_list[i+1], None) 272 os.utime(cmd_list[i+1], None)
272 break 273 break
273 return libtoolout.returncode 274 return libtoolout.returncode
274 275
276 def ExecPackageIosFramework(self, framework):
277 # Find the name of the binary based on the part before the ".framework".
278 binary = os.path.basename(framework).split('.')[0]
279 module_path = os.path.join(framework, 'Modules');
280 if not os.path.exists(module_path):
281 os.mkdir(module_path)
282 module_template = 'framework module %s {\n' \
283 ' umbrella header "%s.h"\n' \
284 '\n' \
285 ' export *\n' \
286 ' module * { export * }\n' \
287 '}\n' % (binary, binary)
288
289 module_file = open(os.path.join(module_path, 'module.modulemap'), "w")
290 module_file.write(module_template)
291 module_file.close()
292
275 def ExecPackageFramework(self, framework, version): 293 def ExecPackageFramework(self, framework, version):
276 """Takes a path to Something.framework and the Current version of that and 294 """Takes a path to Something.framework and the Current version of that and
277 sets up all the symlinks.""" 295 sets up all the symlinks."""
278 # Find the name of the binary based on the part before the ".framework". 296 # Find the name of the binary based on the part before the ".framework".
279 binary = os.path.basename(framework).split('.')[0] 297 binary = os.path.basename(framework).split('.')[0]
280 298
281 CURRENT = 'Current' 299 CURRENT = 'Current'
282 RESOURCES = 'Resources' 300 RESOURCES = 'Resources'
283 VERSIONS = 'Versions' 301 VERSIONS = 'Versions'
284 302
(...skipping 16 matching lines...) Expand all
301 # Back to where we were before! 319 # Back to where we were before!
302 os.chdir(pwd) 320 os.chdir(pwd)
303 321
304 def _Relink(self, dest, link): 322 def _Relink(self, dest, link):
305 """Creates a symlink to |dest| named |link|. If |link| already exists, 323 """Creates a symlink to |dest| named |link|. If |link| already exists,
306 it is overwritten.""" 324 it is overwritten."""
307 if os.path.lexists(link): 325 if os.path.lexists(link):
308 os.remove(link) 326 os.remove(link)
309 os.symlink(dest, link) 327 os.symlink(dest, link)
310 328
329 def ExecCompileIosFrameworkHeaderMap(self, out, framework, *all_headers):
330 framework_name = os.path.basename(framework).split('.')[0]
331 all_headers = map(os.path.abspath, all_headers)
332 filelist = {}
333 for header in all_headers:
334 filename = os.path.basename(header)
335 filelist[filename] = header
336 filelist[os.path.join(framework_name, filename)] = header
337 WriteHmap(out, filelist)
338
339 def ExecCopyIosFrameworkHeaders(self, framework, *copy_headers):
340 header_path = os.path.join(framework, 'Headers');
341 if not os.path.exists(header_path):
342 os.makedirs(header_path)
343 for header in copy_headers:
344 shutil.copy(header, os.path.join(header_path, os.path.basename(header)))
345
311 def ExecCompileXcassets(self, keys, *inputs): 346 def ExecCompileXcassets(self, keys, *inputs):
312 """Compiles multiple .xcassets files into a single .car file. 347 """Compiles multiple .xcassets files into a single .car file.
313 348
314 This invokes 'actool' to compile all the inputs .xcassets files. The 349 This invokes 'actool' to compile all the inputs .xcassets files. The
315 |keys| arguments is a json-encoded dictionary of extra arguments to 350 |keys| arguments is a json-encoded dictionary of extra arguments to
316 pass to 'actool' when the asset catalogs contains an application icon 351 pass to 'actool' when the asset catalogs contains an application icon
317 or a launch image. 352 or a launch image.
318 353
319 Note that 'actool' does not create the Assets.car file if the asset 354 Note that 'actool' does not create the Assets.car file if the asset
320 catalogs does not contains imageset. 355 catalogs does not contains imageset.
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
594 if isinstance(data, str): 629 if isinstance(data, str):
595 for key, value in substitutions.iteritems(): 630 for key, value in substitutions.iteritems():
596 data = data.replace('$(%s)' % key, value) 631 data = data.replace('$(%s)' % key, value)
597 return data 632 return data
598 if isinstance(data, list): 633 if isinstance(data, list):
599 return [self._ExpandVariables(v, substitutions) for v in data] 634 return [self._ExpandVariables(v, substitutions) for v in data]
600 if isinstance(data, dict): 635 if isinstance(data, dict):
601 return {k: self._ExpandVariables(data[k], substitutions) for k in data} 636 return {k: self._ExpandVariables(data[k], substitutions) for k in data}
602 return data 637 return data
603 638
639 def NextGreaterPowerOf2(x):
640 return 2**(x-1).bit_length()
641
642 def WriteHmap(output_name, filelist):
643 """Generates a header map based on |filelist|.
644
645 Per Mark Mentovai:
646 A header map is structured essentially as a hash table, keyed by names used
647 in #includes, and providing pathnames to the actual files.
648
649 The implementation below and the comment above comes from inspecting:
650 http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/includ e_server/headermap.py?txt
651 while also looking at the implementation in clang in:
652 https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp
653 """
654 magic = 1751998832
655 version = 1
656 _reserved = 0
657 count = len(filelist)
658 capacity = NextGreaterPowerOf2(count)
659 strings_offset = 24 + (12 * capacity)
660 max_value_length = len(max(filelist.items(), key=lambda (k,v):len(v))[1])
661
662 out = open(output_name, "wb")
663 out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset,
664 count, capacity, max_value_length))
665
666 # Create empty hashmap buckets.
667 buckets = [None] * capacity
668 for file, path in filelist.items():
669 key = 0
670 for c in file:
671 key += ord(c.lower()) * 13
672
673 # Fill next empty bucket.
674 while buckets[key & capacity - 1] is not None:
675 key = key + 1
676 buckets[key & capacity - 1] = (file, path)
677
678 next_offset = 1
679 for bucket in buckets:
680 if bucket is None:
681 out.write(struct.pack('<LLL', 0, 0, 0))
682 else:
683 (file, path) = bucket
684 key_offset = next_offset
685 prefix_offset = key_offset + len(file) + 1
686 suffix_offset = prefix_offset + len(os.path.dirname(path) + os.sep) + 1
687 next_offset = suffix_offset + len(os.path.basename(path)) + 1
688 out.write(struct.pack('<LLL', key_offset, prefix_offset, suffix_offset))
689
690 # Pad byte since next offset starts at 1.
691 out.write(struct.pack('<x'))
692
693 for bucket in buckets:
694 if bucket is not None:
695 (file, path) = bucket
696 out.write(struct.pack('<%ds' % len(file), file))
697 out.write(struct.pack('<s', '\0'))
698 base = os.path.dirname(path) + os.sep
699 out.write(struct.pack('<%ds' % len(base), base))
700 out.write(struct.pack('<s', '\0'))
701 path = os.path.basename(path)
702 out.write(struct.pack('<%ds' % len(path), path))
703 out.write(struct.pack('<s', '\0'))
704
604 if __name__ == '__main__': 705 if __name__ == '__main__':
605 sys.exit(main(sys.argv[1:])) 706 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698