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

Side by Side Diff: ios/build/tools/setup-gn.py

Issue 2343853002: Upstream helper script to build Chromium on iOS and update instructions. (Closed)
Patch Set: Fix comment. Created 4 years, 3 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
« no previous file with comments | « ios/build/tools/setup-gn.config ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import argparse
7 import convert_gn_xcodeproj
8 import errno
9 import os
10 import re
11 import shutil
12 import subprocess
13 import sys
14 import tempfile
15 import ConfigParser
16
17 try:
18 import cStringIO as StringIO
19 except ImportError:
20 import StringIO
21
22
23 SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator')
24 SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official')
25
26
27 class ConfigParserWithStringInterpolation(ConfigParser.SafeConfigParser):
28
29 '''A .ini file parser that supports strings and environment variables.'''
30
31 ENV_VAR_PATTERN = re.compile('\$([A-Za-z0-9_]+)')
32
33 def values(self, section):
34 return map(
35 lambda (k, v): self._UnquoteString(self._ExpandEnvVar(v)),
36 ConfigParser.SafeConfigParser.items(self, section))
37
38 def getstring(self, section, option):
39 return self._UnquoteString(self._ExpandEnvVar(self.get(section, option)))
40
41 def _UnquoteString(self, string):
42 if not string or string[0] != '"' or string[-1] != '"':
43 return string
44 return string[1:-1]
45
46 def _ExpandEnvVar(self, value):
47 match = self.ENV_VAR_PATTERN.search(value)
48 if not match:
49 return value
50 name, (begin, end) = match.group(1), match.span(0)
51 prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:])
52 return prefix + os.environ.get(name, '') + suffix
53
54 class GnGenerator(object):
55
56 '''Holds configuration for a build and method to generate gn default files.'''
57
58 FAT_BUILD_DEFAULT_ARCH = '64-bit'
59
60 TARGET_CPU_VALUES = {
61 'iphoneos': {
62 '32-bit': '"arm"',
63 '64-bit': '"arm64"',
64 },
65 'iphonesimulator': {
66 '32-bit': '"x86"',
67 '64-bit': '"x64"',
68 }
69 }
70
71 def __init__(self, settings, config, target):
72 assert target in SUPPORTED_TARGETS
73 assert config in SUPPORTED_CONFIGS
74 self._settings = settings
75 self._config = config
76 self._target = target
77
78 def _GetGnArgs(self):
79 """Build the list of arguments to pass to gn.
80
81 Returns:
82 A list of tuple containing gn variable names and variable values (it
83 is not a dictionary as the order needs to be preserved).
84 """
85 args = []
86
87 if self._settings.getboolean('goma', 'enabled'):
88 args.append(('use_goma', True))
89 goma_dir = self._settings.getstring('goma', 'install')
90 if goma_dir:
91 args.append(('goma_dir', '"%s"' % os.path.expanduser(goma_dir)))
92
93 args.append(('is_debug', self._config == 'Debug'))
94 args.append(('enable_dsyms', self._config in ('Profile', 'Official')))
95 args.append(('enable_stripping', 'enable_dsyms'))
96 args.append(('is_official_build', self._config == 'Official'))
97 args.append(('is_chrome_branded', 'is_official_build'))
98 args.append(('use_xcode_clang', 'is_official_build'))
99
100 cpu_values = self.TARGET_CPU_VALUES[self._target]
101 build_arch = self._settings.getstring('build', 'arch')
102 if build_arch == 'fat':
103 target_cpu = cpu_values[self.FAT_BUILD_DEFAULT_ARCH]
104 args.append(('target_cpu', target_cpu))
105 args.append(('additional_target_cpus',
106 [cpu for cpu in cpu_values.itervalues() if cpu != target_cpu]))
107 else:
108 args.append(('target_cpu', cpu_values[build_arch]))
109
110 # Add user overrides after the other configurations so that they can
111 # refer to them and override them.
112 args.extend(self._settings.items('gn_args'))
113 return args
114
115
116 def Generate(self, gn_path, root_path, out_path):
117 buf = StringIO.StringIO()
118 self.WriteArgsGn(buf)
119 WriteToFileIfChanged(
120 os.path.join(out_path, 'args.gn'),
121 buf.getvalue(),
122 overwrite=True)
123
124 subprocess.check_call(
125 self.GetGnCommand(gn_path, root_path, out_path, True))
126
127 def CreateGnRules(self, gn_path, root_path, out_path):
128 buf = StringIO.StringIO()
129 self.WriteArgsGn(buf)
130 WriteToFileIfChanged(
131 os.path.join(out_path, 'args.gn'),
132 buf.getvalue(),
133 overwrite=True)
134
135 buf = StringIO.StringIO()
136 gn_command = self.GetGnCommand(gn_path, root_path, out_path, False)
137 self.WriteBuildNinja(buf, gn_command)
138 WriteToFileIfChanged(
139 os.path.join(out_path, 'build.ninja'),
140 buf.getvalue(),
141 overwrite=False)
142
143 buf = StringIO.StringIO()
144 self.WriteBuildNinjaDeps(buf)
145 WriteToFileIfChanged(
146 os.path.join(out_path, 'build.ninja.d'),
147 buf.getvalue(),
148 overwrite=False)
149
150 def WriteArgsGn(self, stream):
151 stream.write('# This file was generated by setup-gn.py. Do not edit\n')
152 stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
153 stream.write('# to configure settings.\n')
154 stream.write('\n')
155
156 if self._settings.has_section('$imports$'):
157 for import_rule in self._settings.values('$imports$'):
158 stream.write('import("%s")\n' % import_rule)
159 stream.write('\n')
160
161 gn_args = self._GetGnArgs()
162 for name, value in gn_args:
163 if isinstance(value, bool):
164 stream.write('%s = %s\n' % (name, str(value).lower()))
165 elif isinstance(value, list):
166 stream.write('%s = [%s' % (name, '\n' if len(value) else ''))
167 if len(value) == 1:
168 prefix = ' '
169 suffix = ' '
170 else:
171 prefix = ' '
172 suffix = ',\n'
173 for item in value:
174 if isinstance(item, bool):
175 stream.write('%s%s%s' % (prefix, str(item).lower(), suffix))
176 else:
177 stream.write('%s%s%s' % (prefix, item, suffix))
178 stream.write(']\n')
179 else:
180 stream.write('%s = %s\n' % (name, value))
181
182 def WriteBuildNinja(self, stream, gn_command):
183 stream.write('rule gn\n')
184 stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command))
185 stream.write(' description = Regenerating ninja files\n')
186 stream.write('\n')
187 stream.write('build build.ninja: gn\n')
188 stream.write(' generator = 1\n')
189 stream.write(' depfile = build.ninja.d\n')
190
191 def WriteBuildNinjaDeps(self, stream):
192 stream.write('build.ninja: nonexistant_file.gn\n')
193
194 def GetGnCommand(self, gn_path, src_path, out_path, generate_xcode_project):
195 gn_command = [ gn_path, '--root=%s' % os.path.realpath(src_path), '-q' ]
196 if generate_xcode_project:
197 gn_command.append('--ide=xcode')
198 gn_command.append('--root-target=gn_all')
199 if self._settings.getboolean('goma', 'enabled'):
200 ninja_jobs = self._settings.getint('xcode', 'jobs') or 200
201 gn_command.append('--ninja-extra-args=-j%s' % ninja_jobs)
202 if self._settings.has_section('filters'):
203 target_filters = self._settings.values('filters')
204 if target_filters:
205 gn_command.append('--filters=%s' % ';'.join(target_filters))
206 else:
207 gn_command.append('--check')
208 gn_command.append('gen')
209 gn_command.append('//%s' %
210 os.path.relpath(os.path.abspath(out_path), os.path.abspath(src_path)))
211 return gn_command
212
213
214 def WriteToFileIfChanged(filename, content, overwrite):
215 '''Write |content| to |filename| if different. If |overwrite| is False
216 and the file already exists it is left untouched.'''
217 if os.path.exists(filename):
218 if not overwrite:
219 return
220 with open(filename) as file:
221 if file.read() == content:
222 return
223 if not os.path.isdir(os.path.dirname(filename)):
224 os.makedirs(os.path.dirname(filename))
225 with open(filename, 'w') as file:
226 file.write(content)
227
228
229 def NinjaNeedEscape(arg):
230 '''Returns True if |arg| need to be escaped when writen to .ninja file.'''
231 return ':' in arg or '*' in arg or ';' in arg
232
233
234 def NinjaEscapeCommand(command):
235 '''Escapes |command| in order to write it to .ninja file.'''
236 result = []
237 for arg in command:
238 if NinjaNeedEscape(arg):
239 arg = arg.replace(':', '$:')
240 arg = arg.replace(';', '\\;')
241 arg = arg.replace('*', '\\*')
242 else:
243 result.append(arg)
244 return ' '.join(result)
245
246
247 def FindGn():
248 '''Returns absolute path to gn binary looking at the PATH env variable.'''
249 for path in os.environ['PATH'].split(os.path.pathsep):
250 gn_path = os.path.join(path, 'gn')
251 if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK):
252 return gn_path
253 return None
254
255
256 def GenerateXcodeProject(gn_path, root_dir, out_dir, settings):
257 '''Convert GN generated Xcode project into multi-configuration Xcode
258 project.'''
259
260 temp_path = tempfile.mkdtemp(prefix=os.path.abspath(
261 os.path.join(out_dir, '_temp')))
262 try:
263 generator = GnGenerator(settings, 'Debug', 'iphonesimulator')
264 generator.Generate(gn_path, root_dir, temp_path)
265 convert_gn_xcodeproj.ConvertGnXcodeProject(
266 os.path.join(temp_path),
267 os.path.join(out_dir, 'build'),
268 SUPPORTED_CONFIGS)
269 finally:
270 if os.path.exists(temp_path):
271 shutil.rmtree(temp_path)
272
273
274 def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
275 '''Generates all template configurations for gn.'''
276 for config in SUPPORTED_CONFIGS:
277 for target in SUPPORTED_TARGETS:
278 build_dir = os.path.join(out_dir, '%s-%s' % (config, target))
279 generator = GnGenerator(settings, config, target)
280 generator.CreateGnRules(gn_path, root_dir, build_dir)
281
282
283 def Main(args):
284 default_root = os.path.normpath(os.path.join(
285 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
286
287 parser = argparse.ArgumentParser(
288 description='Generate build directories for use with gn.')
289 parser.add_argument(
290 'root', default=default_root, nargs='?',
291 help='root directory where to generate multiple out configurations')
292 parser.add_argument(
293 '--import', action='append', dest='import_rules', default=[],
294 help='path to file defining default gn variables')
295 args = parser.parse_args(args)
296
297 # Load configuration (first global and then any user overrides).
298 settings = ConfigParserWithStringInterpolation()
299 settings.read([
300 os.path.splitext(__file__)[0] + '.config',
301 os.path.expanduser('~/.setup-gn'),
302 ])
303
304 # Add private sections corresponding to --import argument.
305 if args.import_rules:
306 settings.add_section('$imports$')
307 for i, import_rule in enumerate(args.import_rules):
308 if not import_rule.startswith('//'):
309 import_rule = '//%s' % os.path.relpath(
310 os.path.abspath(import_rule), os.path.abspath(args.root))
311 settings.set('$imports$', '$rule%d$' % i, import_rule)
312
313 # Validate settings.
314 if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'):
315 sys.stderr.write('ERROR: invalid value for build.arch: %s\n' %
316 settings.getstring('build', 'arch'))
317 sys.exit(1)
318
319 if settings.getboolean('goma', 'enabled'):
320 if settings.getint('xcode', 'jobs') < 0:
321 sys.stderr.write('ERROR: invalid value for xcode.jobs: %s\n' %
322 settings.get('xcode', 'jobs'))
323 sys.exit(1)
324 goma_install = os.path.expanduser(settings.getstring('goma', 'install'))
325 if not os.path.isdir(goma_install):
326 sys.stderr.write('WARNING: goma.install directory not found: %s\n' %
327 settings.get('goma', 'install'))
328 sys.stderr.write('WARNING: disabling goma\n')
329 settings.set('goma', 'enabled', 'false')
330
331 # Find gn binary in PATH.
332 gn_path = FindGn()
333 if gn_path is None:
334 sys.stderr.write('ERROR: cannot find gn in PATH\n')
335 sys.exit(1)
336
337 out_dir = os.path.join(args.root, 'out')
338 if not os.path.isdir(out_dir):
339 os.makedirs(out_dir)
340
341 GenerateXcodeProject(gn_path, args.root, out_dir, settings)
342 GenerateGnBuildRules(gn_path, args.root, out_dir, settings)
343
344
345 if __name__ == '__main__':
346 sys.exit(Main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « ios/build/tools/setup-gn.config ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698