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