blob: 15fa3f6c99efd31a8386b719e51ad18741c06f9b [file] [log] [blame]
sbc22d1b582015-08-12 06:01:19 +09001# Copyright (c) 2012 The Chromium Authors. All rights reserved.
scottmg@chromium.org1e50b9e2013-05-29 01:02:13 +09002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
sbc22d1b582015-08-12 06:01:19 +09005"""This script is wrapper for Chromium that adds some support for how GYP
6is invoked by Chromium beyond what can be done in the gclient hooks.
7"""
scottmg@chromium.org1e50b9e2013-05-29 01:02:13 +09008
sbc22d1b582015-08-12 06:01:19 +09009import argparse
10import gc
11import glob
12import gyp_environment
scottmg@chromium.org1e50b9e2013-05-29 01:02:13 +090013import os
sbc22d1b582015-08-12 06:01:19 +090014import re
15import shlex
16import subprocess
17import string
18import sys
19import vs_toolchain
scottmg@chromium.org1e50b9e2013-05-29 01:02:13 +090020
sbc22d1b582015-08-12 06:01:19 +090021script_dir = os.path.dirname(os.path.realpath(__file__))
22chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
23
24sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
25import gyp
26
27# Assume this file is in a one-level-deep subdirectory of the source root.
28SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
29
30# Add paths so that pymod_do_main(...) can import files.
31sys.path.insert(1, os.path.join(chrome_src, 'android_webview', 'tools'))
32sys.path.insert(1, os.path.join(chrome_src, 'build', 'android', 'gyp'))
33sys.path.insert(1, os.path.join(chrome_src, 'chrome', 'tools', 'build'))
34sys.path.insert(1, os.path.join(chrome_src, 'chromecast', 'tools', 'build'))
35sys.path.insert(1, os.path.join(chrome_src, 'ios', 'chrome', 'tools', 'build'))
36sys.path.insert(1, os.path.join(chrome_src, 'native_client', 'build'))
37sys.path.insert(1, os.path.join(chrome_src, 'native_client_sdk', 'src',
38 'build_tools'))
39sys.path.insert(1, os.path.join(chrome_src, 'remoting', 'tools', 'build'))
40sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'liblouis'))
41sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'WebKit',
42 'Source', 'build', 'scripts'))
bcwhited1d977e2015-10-30 00:08:05 +090043sys.path.insert(1, os.path.join(chrome_src, 'build'))
sbc22d1b582015-08-12 06:01:19 +090044sys.path.insert(1, os.path.join(chrome_src, 'tools'))
45sys.path.insert(1, os.path.join(chrome_src, 'tools', 'generate_shim_headers'))
46sys.path.insert(1, os.path.join(chrome_src, 'tools', 'grit'))
47
48# On Windows, Psyco shortens warm runs of build/gyp_chromium by about
49# 20 seconds on a z600 machine with 12 GB of RAM, from 90 down to 70
50# seconds. Conversely, memory usage of build/gyp_chromium with Psyco
51# maxes out at about 158 MB vs. 132 MB without it.
52#
53# Psyco uses native libraries, so we need to load a different
54# installation depending on which OS we are running under. It has not
55# been tested whether using Psyco on our Mac and Linux builds is worth
56# it (the GYP running time is a lot shorter, so the JIT startup cost
57# may not be worth it).
58if sys.platform == 'win32':
59 try:
60 sys.path.insert(0, os.path.join(chrome_src, 'third_party', 'psyco_win32'))
61 import psyco
62 except:
63 psyco = None
64else:
65 psyco = None
66
67
68def GetSupplementalFiles():
69 """Returns a list of the supplemental files that are included in all GYP
70 sources."""
71 return glob.glob(os.path.join(chrome_src, '*', 'supplement.gypi'))
72
73
74def ProcessGypDefinesItems(items):
75 """Converts a list of strings to a list of key-value pairs."""
76 result = []
77 for item in items:
78 tokens = item.split('=', 1)
79 # Some GYP variables have hyphens, which we don't support.
80 if len(tokens) == 2:
81 result += [(tokens[0], tokens[1])]
82 else:
83 # No value supplied, treat it as a boolean and set it. Note that we
84 # use the string '1' here so we have a consistent definition whether
85 # you do 'foo=1' or 'foo'.
86 result += [(tokens[0], '1')]
87 return result
88
89
90def GetGypVars(supplemental_files):
91 """Returns a dictionary of all GYP vars."""
92 # Find the .gyp directory in the user's home directory.
93 home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None)
94 if home_dot_gyp:
95 home_dot_gyp = os.path.expanduser(home_dot_gyp)
96 if not home_dot_gyp:
97 home_vars = ['HOME']
98 if sys.platform in ('cygwin', 'win32'):
99 home_vars.append('USERPROFILE')
100 for home_var in home_vars:
101 home = os.getenv(home_var)
102 if home != None:
103 home_dot_gyp = os.path.join(home, '.gyp')
104 if not os.path.exists(home_dot_gyp):
105 home_dot_gyp = None
106 else:
107 break
108
109 if home_dot_gyp:
110 include_gypi = os.path.join(home_dot_gyp, "include.gypi")
111 if os.path.exists(include_gypi):
112 supplemental_files += [include_gypi]
113
114 # GYP defines from the supplemental.gypi files.
115 supp_items = []
116 for supplement in supplemental_files:
117 with open(supplement, 'r') as f:
118 try:
119 file_data = eval(f.read(), {'__builtins__': None}, None)
120 except SyntaxError, e:
121 e.filename = os.path.abspath(supplement)
122 raise
123 variables = file_data.get('variables', [])
124 for v in variables:
125 supp_items += [(v, str(variables[v]))]
126
127 # GYP defines from the environment.
128 env_items = ProcessGypDefinesItems(
129 shlex.split(os.environ.get('GYP_DEFINES', '')))
130
131 # GYP defines from the command line.
132 parser = argparse.ArgumentParser()
133 parser.add_argument('-D', dest='defines', action='append', default=[])
134 cmdline_input_items = parser.parse_known_args()[0].defines
135 cmdline_items = ProcessGypDefinesItems(cmdline_input_items)
136
137 vars_dict = dict(supp_items + env_items + cmdline_items)
138 return vars_dict
139
140
141def GetOutputDirectory():
142 """Returns the output directory that GYP will use."""
143
144 # Handle command line generator flags.
145 parser = argparse.ArgumentParser()
146 parser.add_argument('-G', dest='genflags', default=[], action='append')
147 genflags = parser.parse_known_args()[0].genflags
148
149 # Handle generator flags from the environment.
150 genflags += shlex.split(os.environ.get('GYP_GENERATOR_FLAGS', ''))
151
152 needle = 'output_dir='
153 for item in genflags:
154 if item.startswith(needle):
155 return item[len(needle):]
156
157 return 'out'
158
159
160def additional_include_files(supplemental_files, args=[]):
161 """
162 Returns a list of additional (.gypi) files to include, without duplicating
163 ones that are already specified on the command line. The list of supplemental
164 include files is passed in as an argument.
165 """
166 # Determine the include files specified on the command line.
167 # This doesn't cover all the different option formats you can use,
168 # but it's mainly intended to avoid duplicating flags on the automatic
169 # makefile regeneration which only uses this format.
170 specified_includes = set()
171 for arg in args:
172 if arg.startswith('-I') and len(arg) > 2:
173 specified_includes.add(os.path.realpath(arg[2:]))
174
175 result = []
176 def AddInclude(path):
177 if os.path.realpath(path) not in specified_includes:
178 result.append(path)
179
180 if os.environ.get('GYP_INCLUDE_FIRST') != None:
181 AddInclude(os.path.join(chrome_src, os.environ.get('GYP_INCLUDE_FIRST')))
182
183 # Always include common.gypi.
184 AddInclude(os.path.join(script_dir, 'common.gypi'))
185
186 # Optionally add supplemental .gypi files if present.
187 for supplement in supplemental_files:
188 AddInclude(supplement)
189
190 if os.environ.get('GYP_INCLUDE_LAST') != None:
191 AddInclude(os.path.join(chrome_src, os.environ.get('GYP_INCLUDE_LAST')))
192
193 return result
194
195
196def main():
197 # Disabling garbage collection saves about 1 second out of 16 on a Linux
198 # z620 workstation. Since this is a short-lived process it's not a problem to
199 # leak a few cyclyc references in order to spare the CPU cycles for
200 # scanning the heap.
201 gc.disable()
202
203 args = sys.argv[1:]
204
205 use_analyzer = len(args) and args[0] == '--analyzer'
206 if use_analyzer:
207 args.pop(0)
208 os.environ['GYP_GENERATORS'] = 'analyzer'
209 args.append('-Gconfig_path=' + args.pop(0))
210 args.append('-Ganalyzer_output_path=' + args.pop(0))
211
212 if int(os.environ.get('GYP_CHROMIUM_NO_ACTION', 0)):
213 print 'Skipping gyp_chromium due to GYP_CHROMIUM_NO_ACTION env var.'
214 sys.exit(0)
215
216 # Use the Psyco JIT if available.
217 if psyco:
218 psyco.profile()
219 print "Enabled Psyco JIT."
220
221 # Fall back on hermetic python if we happen to get run under cygwin.
222 # TODO(bradnelson): take this out once this issue is fixed:
223 # http://code.google.com/p/gyp/issues/detail?id=177
224 if sys.platform == 'cygwin':
225 import find_depot_tools
226 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
227 python_dir = sorted(glob.glob(os.path.join(depot_tools_path,
228 'python2*_bin')))[-1]
229 env = os.environ.copy()
230 env['PATH'] = python_dir + os.pathsep + env.get('PATH', '')
231 cmd = [os.path.join(python_dir, 'python.exe')] + sys.argv
232 sys.exit(subprocess.call(cmd, env=env))
233
234 # This could give false positives since it doesn't actually do real option
235 # parsing. Oh well.
236 gyp_file_specified = any(arg.endswith('.gyp') for arg in args)
237
238 gyp_environment.SetEnvironment()
239
240 # If we didn't get a file, check an env var, and then fall back to
241 # assuming 'all.gyp' from the same directory as the script.
242 if not gyp_file_specified:
243 gyp_file = os.environ.get('CHROMIUM_GYP_FILE')
244 if gyp_file:
245 # Note that CHROMIUM_GYP_FILE values can't have backslashes as
246 # path separators even on Windows due to the use of shlex.split().
247 args.extend(shlex.split(gyp_file))
248 else:
249 args.append(os.path.join(script_dir, 'all.gyp'))
250
251 supplemental_includes = GetSupplementalFiles()
252 gyp_vars_dict = GetGypVars(supplemental_includes)
253 # There shouldn't be a circular dependency relationship between .gyp files,
254 # but in Chromium's .gyp files, on non-Mac platforms, circular relationships
255 # currently exist. The check for circular dependencies is currently
256 # bypassed on other platforms, but is left enabled on iOS, where a violation
257 # of the rule causes Xcode to misbehave badly.
258 # TODO(mark): Find and kill remaining circular dependencies, and remove this
259 # option. http://crbug.com/35878.
260 # TODO(tc): Fix circular dependencies in ChromiumOS then add linux2 to the
261 # list.
262 if gyp_vars_dict.get('OS') != 'ios':
263 args.append('--no-circular-check')
264
265 # libtool on Mac warns about duplicate basenames in static libraries, so
266 # they're disallowed in general by gyp. We are lax on this point, so disable
267 # this check other than on Mac. GN does not use static libraries as heavily,
268 # so over time this restriction will mostly go away anyway, even on Mac.
269 # https://code.google.com/p/gyp/issues/detail?id=384
270 if sys.platform != 'darwin':
271 args.append('--no-duplicate-basename-check')
272
273 # We explicitly don't support the make gyp generator (crbug.com/348686). Be
274 # nice and fail here, rather than choking in gyp.
275 if re.search(r'(^|,|\s)make($|,|\s)', os.environ.get('GYP_GENERATORS', '')):
276 print 'Error: make gyp generator not supported (check GYP_GENERATORS).'
277 sys.exit(1)
278
279 # We explicitly don't support the native msvs gyp generator. Be nice and
280 # fail here, rather than generating broken projects.
281 if re.search(r'(^|,|\s)msvs($|,|\s)', os.environ.get('GYP_GENERATORS', '')):
282 print 'Error: msvs gyp generator not supported (check GYP_GENERATORS).'
283 print 'Did you mean to use the `msvs-ninja` generator?'
284 sys.exit(1)
285
286 # If CHROMIUM_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check
287 # to enfore syntax checking.
288 syntax_check = os.environ.get('CHROMIUM_GYP_SYNTAX_CHECK')
289 if syntax_check and int(syntax_check):
290 args.append('--check')
291
292 # TODO(dmikurube): Remove these checks and messages after a while.
293 if ('linux_use_tcmalloc' in gyp_vars_dict or
294 'android_use_tcmalloc' in gyp_vars_dict):
295 print '*****************************************************************'
296 print '"linux_use_tcmalloc" and "android_use_tcmalloc" are deprecated!'
297 print '-----------------------------------------------------------------'
298 print 'You specify "linux_use_tcmalloc" or "android_use_tcmalloc" in'
299 print 'your GYP_DEFINES. Please switch them into "use_allocator" now.'
300 print 'See http://crbug.com/345554 for the details.'
301 print '*****************************************************************'
302
303 # Automatically turn on crosscompile support for platforms that need it.
304 # (The Chrome OS build sets CC_host / CC_target which implicitly enables
305 # this mode.)
306 if all(('ninja' in os.environ.get('GYP_GENERATORS', ''),
307 gyp_vars_dict.get('OS') in ['android', 'ios'],
308 'GYP_CROSSCOMPILE' not in os.environ)):
309 os.environ['GYP_CROSSCOMPILE'] = '1'
310 if gyp_vars_dict.get('OS') == 'android':
311 args.append('--check')
312
313 args.extend(
314 ['-I' + i for i in additional_include_files(supplemental_includes, args)])
315
316 args.extend(['-D', 'gyp_output_dir=' + GetOutputDirectory()])
317
318 if not use_analyzer:
319 print 'Updating projects from gyp files...'
320 sys.stdout.flush()
321
322 # Off we go...
323 gyp_rc = gyp.main(args)
324
325 if not use_analyzer:
326 vs2013_runtime_dll_dirs = vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs()
327 if vs2013_runtime_dll_dirs:
328 x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
329 vs_toolchain.CopyVsRuntimeDlls(
330 os.path.join(chrome_src, GetOutputDirectory()),
331 (x86_runtime, x64_runtime))
332
333 sys.exit(gyp_rc)
334
335if __name__ == '__main__':
336 sys.exit(main())