| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import argparse | 5 import argparse |
| 6 import plistlib | 6 import plistlib |
| 7 import os | 7 import os |
| 8 import re | 8 import re |
| 9 import subprocess | 9 import subprocess |
| 10 import sys | 10 import sys |
| 11 import tempfile | 11 import tempfile |
| 12 import shlex | 12 import shlex |
| 13 | 13 |
| 14 | 14 |
| 15 # Xcode substitutes variables like ${PRODUCT_NAME} when compiling Info.plist. | 15 # Xcode substitutes variables like ${PRODUCT_NAME} when compiling Info.plist. |
| 16 # It also supports supports modifiers like :identifier or :rfc1034identifier. | 16 # It also supports supports modifiers like :identifier or :rfc1034identifier. |
| 17 # SUBST_RE matches a variable substitution pattern with an optional modifier, | 17 # SUBST_RE matches a variable substitution pattern with an optional modifier, |
| 18 # while IDENT_RE matches all characters that are not valid in an "identifier" | 18 # while IDENT_RE matches all characters that are not valid in an "identifier" |
| 19 # value (used when applying the modifier). | 19 # value (used when applying the modifier). |
| 20 SUBST_RE = re.compile(r'\$\{(?P<id>[^}]*?)(?P<modifier>:[^}]*)?\}') | 20 SUBST_RE = re.compile(r'\$\{(?P<id>[^}]*?)(?P<modifier>:[^}]*)?\}') |
| 21 IDENT_RE = re.compile(r'[_/\s]') | 21 IDENT_RE = re.compile(r'[_/\s]') |
| 22 | 22 |
| 23 | 23 |
| 24 class ArgumentParser(argparse.ArgumentParser): | |
| 25 """Subclass of argparse.ArgumentParser to work with GN response files. | |
| 26 | |
| 27 GN response file writes all the arguments on a single line and assumes | |
| 28 that the python script uses shlext.split() to extract them. Since the | |
| 29 default ArgumentParser expects a single argument per line, we need to | |
| 30 provide a subclass to have the correct support for @{{response_file_name}}. | |
| 31 """ | |
| 32 | |
| 33 def convert_arg_line_to_args(self, arg_line): | |
| 34 return shlex.split(arg_line) | |
| 35 | |
| 36 | |
| 37 def InterpolateList(values, substitutions): | 24 def InterpolateList(values, substitutions): |
| 38 """Interpolates variable references into |value| using |substitutions|. | 25 """Interpolates variable references into |value| using |substitutions|. |
| 39 | 26 |
| 40 Inputs: | 27 Inputs: |
| 41 values: a list of values | 28 values: a list of values |
| 42 substitutions: a mapping of variable names to values | 29 substitutions: a mapping of variable names to values |
| 43 | 30 |
| 44 Returns: | 31 Returns: |
| 45 A new list of values with all variables references ${VARIABLE} replaced | 32 A new list of values with all variables references ${VARIABLE} replaced |
| 46 by their value in |substitutions| or None if any of the variable has no | 33 by their value in |substitutions| or None if any of the variable has no |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 if key in plist2: | 162 if key in plist2: |
| 176 value = plist2[key] | 163 value = plist2[key] |
| 177 else: | 164 else: |
| 178 value = plist1[key] | 165 value = plist1[key] |
| 179 if isinstance(value, dict): | 166 if isinstance(value, dict): |
| 180 value = MergePList(plist1.get(key, None), plist2.get(key, None)) | 167 value = MergePList(plist1.get(key, None), plist2.get(key, None)) |
| 181 result[key] = value | 168 result[key] = value |
| 182 return result | 169 return result |
| 183 | 170 |
| 184 | 171 |
| 185 def main(): | 172 class Action(object): |
| 186 parser = ArgumentParser( | 173 """Class implementing one action supported by the script.""" |
| 187 description='A script to generate iOS application Info.plist.', | 174 |
| 188 fromfile_prefix_chars='@') | 175 @classmethod |
| 189 parser.add_argument('-o', '--output', required=True, | 176 def Register(cls, subparsers): |
| 190 help='Path to output plist file.') | 177 parser = subparsers.add_parser(cls.name, help=cls.help) |
| 191 parser.add_argument('-s', '--subst', action='append', default=[], | 178 parser.set_defaults(func=cls._Execute) |
| 192 help='Substitution rule in the format "key=value".') | 179 cls._Register(parser) |
| 193 parser.add_argument('-f', '--format', required=True, | 180 |
| 194 help='Plist format (e.g. binary1, xml1) to output.') | 181 |
| 195 parser.add_argument('path', nargs="+", help='Path to input plist files.') | 182 class MergeAction(Action): |
| 183 """Class to merge multiple plist files.""" |
| 184 |
| 185 name = 'merge' |
| 186 help = 'merge multiple plist files' |
| 187 |
| 188 @staticmethod |
| 189 def _Register(parser): |
| 190 parser.add_argument( |
| 191 '-o', '--output', required=True, |
| 192 help='path to the output plist file') |
| 193 parser.add_argument( |
| 194 '-f', '--format', required=True, choices=('xml1', 'binary1', 'json'), |
| 195 help='format of the plist file to generate') |
| 196 parser.add_argument( |
| 197 'path', nargs="+", |
| 198 help='path to plist files to merge') |
| 199 |
| 200 @staticmethod |
| 201 def _Execute(args): |
| 202 data = {} |
| 203 for filename in args.path: |
| 204 data = MergePList(data, LoadPList(filename)) |
| 205 SavePList(args.output, args.format, data) |
| 206 |
| 207 |
| 208 class SubstituteAction(Action): |
| 209 """Class implementing the variable substitution in a plist file.""" |
| 210 |
| 211 name = 'substitute' |
| 212 help = 'perform pattern substitution in a plist file' |
| 213 |
| 214 @staticmethod |
| 215 def _Register(parser): |
| 216 parser.add_argument( |
| 217 '-o', '--output', required=True, |
| 218 help='path to the output plist file') |
| 219 parser.add_argument( |
| 220 '-t', '--template', required=True, |
| 221 help='path to the template file') |
| 222 parser.add_argument( |
| 223 '-s', '--substitution', action='append', default=[], |
| 224 help='substitution rule in the format key=value') |
| 225 parser.add_argument( |
| 226 '-f', '--format', required=True, choices=('xml1', 'binary1', 'json'), |
| 227 help='format of the plist file to generate') |
| 228 |
| 229 @staticmethod |
| 230 def _Execute(args): |
| 231 substitutions = {} |
| 232 for substitution in args.substitution: |
| 233 key, value = substitution.split('=', 1) |
| 234 substitutions[key] = value |
| 235 data = Interpolate(LoadPList(args.template), substitutions) |
| 236 SavePList(args.output, args.format, data) |
| 237 |
| 238 |
| 239 def Main(): |
| 240 parser = argparse.ArgumentParser(description='manipulate plist files') |
| 241 subparsers = parser.add_subparsers() |
| 242 |
| 243 for action in [MergeAction, SubstituteAction]: |
| 244 action.Register(subparsers) |
| 245 |
| 196 args = parser.parse_args() | 246 args = parser.parse_args() |
| 197 substitutions = {} | 247 args.func(args) |
| 198 for subst in args.subst: | 248 |
| 199 key, value = subst.split('=', 1) | |
| 200 substitutions[key] = value | |
| 201 data = {} | |
| 202 for filename in args.path: | |
| 203 data = MergePList(data, LoadPList(filename)) | |
| 204 data = Interpolate(data, substitutions) | |
| 205 SavePList(args.output, args.format, data) | |
| 206 return 0 | |
| 207 | 249 |
| 208 if __name__ == '__main__': | 250 if __name__ == '__main__': |
| 209 sys.exit(main()) | 251 sys.exit(Main()) |
| OLD | NEW |