Chromium Code Reviews| Index: build/config/ios/ios_gen_plist.py |
| diff --git a/build/config/ios/ios_gen_plist.py b/build/config/ios/ios_gen_plist.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..77dc67c65ec1a147d69d7133e2a6e16cb5517467 |
| --- /dev/null |
| +++ b/build/config/ios/ios_gen_plist.py |
| @@ -0,0 +1,128 @@ |
| +# Copyright 2016 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +import argparse |
| +import plistlib |
| +import os |
| +import re |
| +import subprocess |
| +import sys |
| +import tempfile |
| +import shlex |
| + |
| + |
| +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.
|
| +SUBST_RE = re.compile(r'\$\{(?P<id>[^}]*?)(?P<modifier>:[^}]*)?\}') |
| + |
| + |
| +class ArgumentParser(argparse.ArgumentParser): |
| + |
|
brettw
2016/01/22 19:00:12
Delete blank line.
sdefresne
2016/01/25 14:01:22
Done.
|
| + """Subclass of argparse.ArgumentParser to work with GN response files.""" |
| + |
| + def convert_arg_line_to_args(self, arg_line): |
| + return shlex.split(arg_line) |
| + |
| + |
| +def Merge(plist1, plist2): |
| + """Recursively merges |plist2| into |plist1|.""" |
| + if not plist1: |
| + return plist2 |
| + for key in plist2: |
| + value = plist2[key] |
| + if isinstance(value, dict): |
| + plist1[key] = Merge(plist1.get(key, {}), value) |
| + else: |
| + plist1[key] = value |
| + return plist1 |
| + |
| + |
| +def Interpolate(plist, substitutions): |
| + """Interpolates ${variable} into |plist| using mapping in |substitutions|.""" |
| + result = [] |
| + groups = SUBST_RE.split(plistlib.writePlistToString(plist)) |
| + # SUBST_RE defines two patterns, so given a string, SUBST_RE.split will |
| + # return a list whose position correspond to an unmatched fragment, a |
| + # match identifier or a match modifier (may be None as it is optional). |
| + # Enumerate over the value and check the index modulo 3 to figure out |
| + # the category of the value. |
| + for i, value in enumerate(groups): |
| + if i % 3 == 0: |
| + result.append(value) |
| + elif i % 3 == 1: |
| + if value in substitutions: |
| + result.append(substitutions[value]) |
| + else: |
| + result.append('${%s}' % value) |
| + elif i % 3 == 2: |
| + value, modifier = result[-1], value |
| + if modifier == 'identifier': |
| + value = IDENT_RE.sub('-', value) |
| + elif modifier == 'rfc1034identifier': |
| + value = IDENT_RE.sub('_', value) |
| + result[-1] = value |
| + return CleanPlist(plistlib.readPlistFromString(''.join(result))) |
| + |
| + |
| +def CleanPlist(plist): |
| + """Removes all key of |plist| with unexpanded variables substitution.""" |
| + key_to_remove = [] |
| + for key in plist: |
| + value = plist[key] |
| + data = plistlib.writePlistToString(value) |
| + if SUBST_RE.search(data): |
| + key_to_remove.append(key) |
| + for key in key_to_remove: |
| + del plist[key] |
| + return plist |
| + |
| + |
| +def LoadPList(path): |
| + """Loads Plist at |path| and returns it as a dictionary.""" |
| + fd, name = tempfile.mkstemp() |
| + try: |
| + subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) |
| + with os.fdopen(fd, 'r') as f: |
| + return plistlib.readPlist(f) |
| + finally: |
| + os.unlink(name) |
| + |
| + |
| +def SavePList(path, data): |
| + """Saves |data| as a Plist to |path| in binary1 format.""" |
| + fd, name = tempfile.mkstemp() |
| + try: |
| + with os.fdopen(fd, 'w') as f: |
| + plistlib.writePlist(data, f) |
| + subprocess.check_call(['plutil', '-convert', 'binary1', '-o', path, name]) |
| + finally: |
| + os.unlink(name) |
| + |
| + |
| +def main(): |
| + parser = ArgumentParser( |
| + description='A script to generate iOS application Info.plist.', |
| + fromfile_prefix_chars='@') |
| + parser.add_argument('-o', '--output', required=True, |
| + help='Path to output plist file.') |
| + parser.add_argument('-s', '--subst', action='append', default=[], |
| + help='Substitution rule in the format "key=value".') |
| + parser.add_argument('path', nargs="+", help='Path to input plist files.') |
| + args = parser.parse_args() |
| + |
| + substitutions = {} |
| + for subst in args.subst: |
| + key, value = subst.split('=', 1) |
| + substitutions[key] = value |
| + |
| + data = {} |
| + for path in args.path: |
| + data = Merge(data, LoadPList(path)) |
| + |
| + data = Interpolate(data, substitutions) |
| + SavePList(args.output, data) |
| + return 0 |
| + |
| + |
| +if __name__ == '__main__': |
| + sys.exit(main()) |