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

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: Fix comment nit 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
293
275 def ExecPackageFramework(self, framework, version): 294 def ExecPackageFramework(self, framework, version):
276 """Takes a path to Something.framework and the Current version of that and 295 """Takes a path to Something.framework and the Current version of that and
277 sets up all the symlinks.""" 296 sets up all the symlinks."""
278 # Find the name of the binary based on the part before the ".framework". 297 # Find the name of the binary based on the part before the ".framework".
279 binary = os.path.basename(framework).split('.')[0] 298 binary = os.path.basename(framework).split('.')[0]
280 299
281 CURRENT = 'Current' 300 CURRENT = 'Current'
282 RESOURCES = 'Resources' 301 RESOURCES = 'Resources'
283 VERSIONS = 'Versions' 302 VERSIONS = 'Versions'
284 303
(...skipping 16 matching lines...) Expand all
301 # Back to where we were before! 320 # Back to where we were before!
302 os.chdir(pwd) 321 os.chdir(pwd)
303 322
304 def _Relink(self, dest, link): 323 def _Relink(self, dest, link):
305 """Creates a symlink to |dest| named |link|. If |link| already exists, 324 """Creates a symlink to |dest| named |link|. If |link| already exists,
306 it is overwritten.""" 325 it is overwritten."""
307 if os.path.lexists(link): 326 if os.path.lexists(link):
308 os.remove(link) 327 os.remove(link)
309 os.symlink(dest, link) 328 os.symlink(dest, link)
310 329
330 def ExecCompileIosFrameworkHeaderMap(self, out, framework, *all_headers):
331 framework_name = os.path.basename(framework).split('.')[0]
332 all_headers = map(os.path.abspath, all_headers)
333 filelist = {}
334 for header in all_headers:
335 filename = os.path.basename(header)
336 filelist[filename] = header
337 filelist[os.path.join(framework_name, filename)] = header
338 WriteHmap(out, filelist)
339
340 def ExecCopyIosFrameworkHeaders(self, framework, *copy_headers):
341 header_path = os.path.join(framework, 'Headers');
342 if not os.path.exists(header_path):
343 os.makedirs(header_path)
344 for header in copy_headers:
345 shutil.copy2(header, os.path.join(header_path, os.path.basename(header)))
sdefresne 2016/03/18 15:59:00 nit: why do you need copy2? Is it really important
justincohen 2016/03/19 03:57:00 Done.
346
311 def ExecCompileXcassets(self, keys, *inputs): 347 def ExecCompileXcassets(self, keys, *inputs):
312 """Compiles multiple .xcassets files into a single .car file. 348 """Compiles multiple .xcassets files into a single .car file.
313 349
314 This invokes 'actool' to compile all the inputs .xcassets files. The 350 This invokes 'actool' to compile all the inputs .xcassets files. The
315 |keys| arguments is a json-encoded dictionary of extra arguments to 351 |keys| arguments is a json-encoded dictionary of extra arguments to
316 pass to 'actool' when the asset catalogs contains an application icon 352 pass to 'actool' when the asset catalogs contains an application icon
317 or a launch image. 353 or a launch image.
318 354
319 Note that 'actool' does not create the Assets.car file if the asset 355 Note that 'actool' does not create the Assets.car file if the asset
320 catalogs does not contains imageset. 356 catalogs does not contains imageset.
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
594 if isinstance(data, str): 630 if isinstance(data, str):
595 for key, value in substitutions.iteritems(): 631 for key, value in substitutions.iteritems():
596 data = data.replace('$(%s)' % key, value) 632 data = data.replace('$(%s)' % key, value)
597 return data 633 return data
598 if isinstance(data, list): 634 if isinstance(data, list):
599 return [self._ExpandVariables(v, substitutions) for v in data] 635 return [self._ExpandVariables(v, substitutions) for v in data]
600 if isinstance(data, dict): 636 if isinstance(data, dict):
601 return {k: self._ExpandVariables(data[k], substitutions) for k in data} 637 return {k: self._ExpandVariables(data[k], substitutions) for k in data}
602 return data 638 return data
603 639
640 def NextGreaterPowerOf2(x):
641 return 2**(x-1).bit_length()
642
643 def WriteHmap(output_name, filelist):
644 print "hi"
sdefresne 2016/03/18 15:59:00 Debug?
justincohen 2016/03/19 03:57:00 oooof.
645 """Generates a header map based on |filelist|.
646
647 Per Mark Mentovai:
648 A header map is structured essentially as a hash table, keyed by names used
649 in #includes, and providing pathnames to the actual files.
650
651 The implementation below and the comment above comes from inspecting:
652 http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/includ e_server/headermap.py?txt
653 while also looking at the implementation in clang in:
654 https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp
655 """
656 magic = 1751998832
657 version = 1
658 _reserved = 0
659 count = len(filelist)
660 capacity = NextGreaterPowerOf2(count)
661 strings_offset = 24 + (12 * capacity)
662 max_value_length = len(max(filelist.items(), key=lambda (k,v):len(v))[1])
663
664 out = open(output_name, "wb")
665 out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset,
666 count, capacity, max_value_length))
667
668 # Create empty hashmap buckets.
669 buckets = [None] * capacity
670 for file, path in filelist.items():
671 key = 0
672 for c in file:
673 key += ord(c.lower()) * 13
674
675 # Fill next empty bucket.
676 while buckets[key & capacity - 1] is not None:
677 key = key + 1
678 buckets[key & capacity - 1] = (file, path)
679
680 next_offset = 1
681 for bucket in buckets:
682 if bucket is None:
683 out.write(struct.pack('<LLL', 0, 0, 0))
684 else:
685 (file, path) = bucket
686 key_offset = next_offset
687 prefix_offset = key_offset + len(file) + 1
688 suffix_offset = prefix_offset + len(os.path.dirname(path) + os.sep) + 1
689 next_offset = suffix_offset + len(os.path.basename(path)) + 1
690 out.write(struct.pack('<LLL', key_offset, prefix_offset, suffix_offset))
691
692 # Pad byte since next offset starts at 1.
693 out.write(struct.pack('<x'))
694
695 for bucket in buckets:
696 if bucket is not None:
697 (file, path) = bucket
698 out.write(struct.pack('<%ds' % len(file), file))
699 out.write(struct.pack('<s', '\0'))
700 base = os.path.dirname(path) + os.sep
701 out.write(struct.pack('<%ds' % len(base), base))
702 out.write(struct.pack('<s', '\0'))
703 path = os.path.basename(path)
704 out.write(struct.pack('<%ds' % len(path), path))
705 out.write(struct.pack('<s', '\0'))
706
604 if __name__ == '__main__': 707 if __name__ == '__main__':
605 sys.exit(main(sys.argv[1:])) 708 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698