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