| 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]') |
| 16 SUBST_RE = re.compile(r'\$\{(?P<id>[^}]*?)(?P<modifier>:[^}]*)?\}') |
| 17 |
| 18 |
| 19 class ArgumentParser(argparse.ArgumentParser): |
| 20 |
| 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 |