Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import argparse | |
| 6 import plistlib | |
| 7 import os | |
| 8 import re | |
| 9 import subprocess | |
| 10 import sys | |
| 11 import tempfile | |
| 12 import shlex | |
| 13 | |
| 14 | |
| 15 IDENT_RE = re.compile(r'[/\s]') | |
|
brettw
2016/01/22 19:00:12
Can you provide documentation for these about what
sdefresne
2016/01/25 14:01:22
Done.
| |
| 16 SUBST_RE = re.compile(r'\$\{(?P<id>[^}]*?)(?P<modifier>:[^}]*)?\}') | |
| 17 | |
| 18 | |
| 19 class ArgumentParser(argparse.ArgumentParser): | |
| 20 | |
|
brettw
2016/01/22 19:00:12
Delete blank line.
sdefresne
2016/01/25 14:01:22
Done.
| |
| 21 """Subclass of argparse.ArgumentParser to work with GN response files.""" | |
| 22 | |
| 23 def convert_arg_line_to_args(self, arg_line): | |
| 24 return shlex.split(arg_line) | |
| 25 | |
| 26 | |
| 27 def Merge(plist1, plist2): | |
| 28 """Recursively merges |plist2| into |plist1|.""" | |
| 29 if not plist1: | |
| 30 return plist2 | |
| 31 for key in plist2: | |
| 32 value = plist2[key] | |
| 33 if isinstance(value, dict): | |
| 34 plist1[key] = Merge(plist1.get(key, {}), value) | |
| 35 else: | |
| 36 plist1[key] = value | |
| 37 return plist1 | |
| 38 | |
| 39 | |
| 40 def Interpolate(plist, substitutions): | |
| 41 """Interpolates ${variable} into |plist| using mapping in |substitutions|.""" | |
| 42 result = [] | |
| 43 groups = SUBST_RE.split(plistlib.writePlistToString(plist)) | |
| 44 # SUBST_RE defines two patterns, so given a string, SUBST_RE.split will | |
| 45 # return a list whose position correspond to an unmatched fragment, a | |
| 46 # match identifier or a match modifier (may be None as it is optional). | |
| 47 # Enumerate over the value and check the index modulo 3 to figure out | |
| 48 # the category of the value. | |
| 49 for i, value in enumerate(groups): | |
| 50 if i % 3 == 0: | |
| 51 result.append(value) | |
| 52 elif i % 3 == 1: | |
| 53 if value in substitutions: | |
| 54 result.append(substitutions[value]) | |
| 55 else: | |
| 56 result.append('${%s}' % value) | |
| 57 elif i % 3 == 2: | |
| 58 value, modifier = result[-1], value | |
| 59 if modifier == 'identifier': | |
| 60 value = IDENT_RE.sub('-', value) | |
| 61 elif modifier == 'rfc1034identifier': | |
| 62 value = IDENT_RE.sub('_', value) | |
| 63 result[-1] = value | |
| 64 return CleanPlist(plistlib.readPlistFromString(''.join(result))) | |
| 65 | |
| 66 | |
| 67 def CleanPlist(plist): | |
| 68 """Removes all key of |plist| with unexpanded variables substitution.""" | |
| 69 key_to_remove = [] | |
| 70 for key in plist: | |
| 71 value = plist[key] | |
| 72 data = plistlib.writePlistToString(value) | |
| 73 if SUBST_RE.search(data): | |
| 74 key_to_remove.append(key) | |
| 75 for key in key_to_remove: | |
| 76 del plist[key] | |
| 77 return plist | |
| 78 | |
| 79 | |
| 80 def LoadPList(path): | |
| 81 """Loads Plist at |path| and returns it as a dictionary.""" | |
| 82 fd, name = tempfile.mkstemp() | |
| 83 try: | |
| 84 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) | |
| 85 with os.fdopen(fd, 'r') as f: | |
| 86 return plistlib.readPlist(f) | |
| 87 finally: | |
| 88 os.unlink(name) | |
| 89 | |
| 90 | |
| 91 def SavePList(path, data): | |
| 92 """Saves |data| as a Plist to |path| in binary1 format.""" | |
| 93 fd, name = tempfile.mkstemp() | |
| 94 try: | |
| 95 with os.fdopen(fd, 'w') as f: | |
| 96 plistlib.writePlist(data, f) | |
| 97 subprocess.check_call(['plutil', '-convert', 'binary1', '-o', path, name]) | |
| 98 finally: | |
| 99 os.unlink(name) | |
| 100 | |
| 101 | |
| 102 def main(): | |
| 103 parser = ArgumentParser( | |
| 104 description='A script to generate iOS application Info.plist.', | |
| 105 fromfile_prefix_chars='@') | |
| 106 parser.add_argument('-o', '--output', required=True, | |
| 107 help='Path to output plist file.') | |
| 108 parser.add_argument('-s', '--subst', action='append', default=[], | |
| 109 help='Substitution rule in the format "key=value".') | |
| 110 parser.add_argument('path', nargs="+", help='Path to input plist files.') | |
| 111 args = parser.parse_args() | |
| 112 | |
| 113 substitutions = {} | |
| 114 for subst in args.subst: | |
| 115 key, value = subst.split('=', 1) | |
| 116 substitutions[key] = value | |
| 117 | |
| 118 data = {} | |
| 119 for path in args.path: | |
| 120 data = Merge(data, LoadPList(path)) | |
| 121 | |
| 122 data = Interpolate(data, substitutions) | |
| 123 SavePList(args.output, data) | |
| 124 return 0 | |
| 125 | |
| 126 | |
| 127 if __name__ == '__main__': | |
| 128 sys.exit(main()) | |
| OLD | NEW |