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

Side by Side Diff: build/config/ios/ios_gen_plist.py

Issue 1611363003: Add support for iOS application bundle to GN. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gn-bundles
Patch Set: Filter .xcassets from the copy_bundle_data step Created 4 years, 11 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
(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 # Xcode substitutes variables like ${PRODUCT_NAME} when compiling Info.plist.
16 # It also supports supports modifiers like :identifier or :rfc1034identifier.
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"
19 # value (used when applying the modifier).
20 SUBST_RE = re.compile(r'\$\{(?P<id>[^}]*?)(?P<modifier>:[^}]*)?\}')
21 IDENT_RE = re.compile(r'[/\s]')
22
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 Merge(plist1, plist2):
38 """Recursively merges |plist2| into |plist1|."""
39 if not plist1:
40 return plist2
41 for key in plist2:
42 value = plist2[key]
43 if isinstance(value, dict):
44 plist1[key] = Merge(plist1.get(key, {}), value)
45 else:
46 plist1[key] = value
47 return plist1
48
49
50 def Interpolate(plist, substitutions):
51 """Interpolates ${variable} into |plist| using mapping in |substitutions|."""
52 result = []
53 groups = SUBST_RE.split(plistlib.writePlistToString(plist))
54 # SUBST_RE defines two patterns, so given a string, SUBST_RE.split will
55 # return a list whose position correspond to an unmatched fragment, a
56 # match identifier or a match modifier (may be None as it is optional).
57 # Enumerate over the value and check the index modulo 3 to figure out
58 # the category of the value.
59 for i, value in enumerate(groups):
60 if i % 3 == 0:
61 result.append(value)
62 elif i % 3 == 1:
63 if value in substitutions:
64 result.append(substitutions[value])
65 else:
66 result.append('${%s}' % value)
67 elif i % 3 == 2:
68 value, modifier = result[-1], value
69 # Apply the modifier if defined (otherwise the value will be None). At
70 # the beginning Xcode only supported :identifier that replaced all the
71 # characters that are invalid by an hyphen, however, this is an invalid
72 # character in a bundle identifier which is a reversed domain name and
73 # so :rfc1034identifier was created that instead uses an underscore.
74 if modifier == 'identifier':
75 value = IDENT_RE.sub('-', value)
76 elif modifier == 'rfc1034identifier':
77 value = IDENT_RE.sub('_', value)
78 result[-1] = value
79 return CleanPlist(plistlib.readPlistFromString(''.join(result)))
80
81
82 def CleanPlist(plist):
83 """Removes all key of |plist| with unexpanded variables substitution."""
84 key_to_remove = []
85 for key in plist:
86 value = plist[key]
87 data = plistlib.writePlistToString(value)
88 if SUBST_RE.search(data):
89 key_to_remove.append(key)
90 for key in key_to_remove:
91 del plist[key]
92 return plist
93
94
95 def LoadPList(path):
96 """Loads Plist at |path| and returns it as a dictionary."""
97 fd, name = tempfile.mkstemp()
98 try:
99 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path])
100 with os.fdopen(fd, 'r') as f:
101 return plistlib.readPlist(f)
102 finally:
103 os.unlink(name)
104
105
106 def SavePList(path, data):
107 """Saves |data| as a Plist to |path| in binary1 format."""
108 fd, name = tempfile.mkstemp()
109 try:
110 with os.fdopen(fd, 'w') as f:
111 plistlib.writePlist(data, f)
112 subprocess.check_call(['plutil', '-convert', 'binary1', '-o', path, name])
113 finally:
114 os.unlink(name)
115
116
117 def main():
118 parser = ArgumentParser(
119 description='A script to generate iOS application Info.plist.',
120 fromfile_prefix_chars='@')
121 parser.add_argument('-o', '--output', required=True,
122 help='Path to output plist file.')
123 parser.add_argument('-s', '--subst', action='append', default=[],
124 help='Substitution rule in the format "key=value".')
125 parser.add_argument('path', nargs="+", help='Path to input plist files.')
126 args = parser.parse_args()
127
128 substitutions = {}
129 for subst in args.subst:
130 key, value = subst.split('=', 1)
131 substitutions[key] = value
132
133 data = {}
134 for path in args.path:
135 data = Merge(data, LoadPList(path))
136
137 data = Interpolate(data, substitutions)
138 SavePList(args.output, data)
139 return 0
140
141
142 if __name__ == '__main__':
143 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698