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

Side by Side Diff: build/config/mac/gen_plist.py

Issue 2480433002: Extend tweak_info_plist template to support multiple plist file as input. (Closed)
Patch Set: Created 4 years, 1 month 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
« no previous file with comments | « build/config/mac/base_rules.gni ('k') | build/config/mac/plist_util.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 InterpolateList(values, substitutions):
38 """Interpolates variable references into |value| using |substitutions|.
39
40 Inputs:
41 values: a list of values
42 substitutions: a mapping of variable names to values
43
44 Returns:
45 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
47 subsitution.
48 """
49 result = []
50 for value in values:
51 interpolated = InterpolateValue(value, substitutions)
52 if interpolated is None:
53 return None
54 result.append(interpolated)
55 return result
56
57
58 def InterpolateString(value, substitutions):
59 """Interpolates variable references into |value| using |substitutions|.
60
61 Inputs:
62 value: a string
63 substitutions: a mapping of variable names to values
64
65 Returns:
66 A new string with all variables references ${VARIABLES} replaced by their
67 value in |substitutions| or None if any of the variable has no substitution.
68 """
69 result = value
70 for match in reversed(list(SUBST_RE.finditer(value))):
71 variable = match.group('id')
72 if variable not in substitutions:
73 return None
74 # Some values need to be identifier and thus the variables references may
75 # contains :modifier attributes to indicate how they should be converted
76 # to identifiers ("identifier" replaces all invalid characters by '_' and
77 # "rfc1034identifier" replaces them by "-" to make valid URI too).
78 modifier = match.group('modifier')
79 if modifier == ':identifier':
80 interpolated = IDENT_RE.sub('_', substitutions[variable])
81 elif modifier == ':rfc1034identifier':
82 interpolated = IDENT_RE.sub('-', substitutions[variable])
83 else:
84 interpolated = substitutions[variable]
85 result = result[:match.start()] + interpolated + result[match.end():]
86 return result
87
88
89 def InterpolateValue(value, substitutions):
90 """Interpolates variable references into |value| using |substitutions|.
91
92 Inputs:
93 value: a value, can be a dictionary, list, string or other
94 substitutions: a mapping of variable names to values
95
96 Returns:
97 A new value with all variables references ${VARIABLES} replaced by their
98 value in |substitutions| or None if any of the variable has no substitution.
99 """
100 if isinstance(value, dict):
101 return Interpolate(value, substitutions)
102 if isinstance(value, list):
103 return InterpolateList(value, substitutions)
104 if isinstance(value, str):
105 return InterpolateString(value, substitutions)
106 return value
107
108
109 def Interpolate(plist, substitutions):
110 """Interpolates variable references into |value| using |substitutions|.
111
112 Inputs:
113 plist: a dictionary representing a Property List (.plist) file
114 substitutions: a mapping of variable names to values
115
116 Returns:
117 A new plist with all variables references ${VARIABLES} replaced by their
118 value in |substitutions|. All values that contains references with no
119 substitutions will be removed and the corresponding key will be cleared
120 from the plist (not recursively).
121 """
122 result = {}
123 for key in plist:
124 value = InterpolateValue(plist[key], substitutions)
125 if value is not None:
126 result[key] = value
127 return result
128
129
130 def LoadPList(path):
131 """Loads Plist at |path| and returns it as a dictionary."""
132 fd, name = tempfile.mkstemp()
133 try:
134 subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path])
135 with os.fdopen(fd, 'r') as f:
136 return plistlib.readPlist(f)
137 finally:
138 os.unlink(name)
139
140
141 def SavePList(path, format, data):
142 """Saves |data| as a Plist to |path| in the specified |format|."""
143 fd, name = tempfile.mkstemp()
144 try:
145 with os.fdopen(fd, 'w') as f:
146 plistlib.writePlist(data, f)
147 subprocess.check_call(['plutil', '-convert', format, '-o', path, name])
148 finally:
149 os.unlink(name)
150
151
152 def MergePList(plist1, plist2):
153 """Merges |plist1| with |plist2| recursively.
154
155 Creates a new dictionary representing a Property List (.plist) files by
156 merging the two dictionary |plist1| and |plist2| recursively (only for
157 dictionary values).
158
159 Args:
160 plist1: a dictionary representing a Property List (.plist) file
161 plist2: a dictionary representing a Property List (.plist) file
162
163 Returns:
164 A new dictionary representing a Property List (.plist) file by merging
165 |plist1| with |plist2|. If any value is a dictionary, they are merged
166 recursively, otherwise |plist2| value is used.
167 """
168 if not isinstance(plist1, dict) or not isinstance(plist2, dict):
169 if plist2 is not None:
170 return plist2
171 else:
172 return plist1
173 result = {}
174 for key in set(plist1) | set(plist2):
175 if key in plist2:
176 value = plist2[key]
177 else:
178 value = plist1[key]
179 if isinstance(value, dict):
180 value = MergePList(plist1.get(key, None), plist2.get(key, None))
181 result[key] = value
182 return result
183
184
185 def main():
186 parser = ArgumentParser(
187 description='A script to generate iOS application Info.plist.',
188 fromfile_prefix_chars='@')
189 parser.add_argument('-o', '--output', required=True,
190 help='Path to output plist file.')
191 parser.add_argument('-s', '--subst', action='append', default=[],
192 help='Substitution rule in the format "key=value".')
193 parser.add_argument('-f', '--format', required=True,
194 help='Plist format (e.g. binary1, xml1) to output.')
195 parser.add_argument('path', nargs="+", help='Path to input plist files.')
196 args = parser.parse_args()
197 substitutions = {}
198 for subst in args.subst:
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
208 if __name__ == '__main__':
209 sys.exit(main())
OLDNEW
« no previous file with comments | « build/config/mac/base_rules.gni ('k') | build/config/mac/plist_util.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698