Support symlinks in tools/vim/chromium.ycm_extra_conf.py
[chromium-blink-merge.git] / build / gyp_chromium
blob11125751a0e5b0e515741ee8d18cd96c608a40e0
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 glob
11 import gyp_environment
12 import os
13 import re
14 import shlex
15 import subprocess
16 import string
17 import sys
18 import vs_toolchain
20 script_dir = os.path.dirname(os.path.realpath(__file__))
21 chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
23 sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
24 import gyp
26 # Assume this file is in a one-level-deep subdirectory of the source root.
27 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
29 # Add paths so that pymod_do_main(...) can import files.
30 sys.path.insert(1, os.path.join(chrome_src, 'build', 'android', 'gyp'))
31 sys.path.insert(1, os.path.join(chrome_src, 'tools'))
32 sys.path.insert(1, os.path.join(chrome_src, 'tools', 'generate_shim_headers'))
33 sys.path.insert(1, os.path.join(chrome_src, 'tools', 'grit'))
34 sys.path.insert(1, os.path.join(chrome_src, 'chrome', 'tools', 'build'))
35 sys.path.insert(1, os.path.join(chrome_src, 'chromecast', 'tools', 'build'))
36 sys.path.insert(1, os.path.join(chrome_src, 'native_client', 'build'))
37 sys.path.insert(1, os.path.join(chrome_src, 'native_client_sdk', 'src',
38 'build_tools'))
39 sys.path.insert(1, os.path.join(chrome_src, 'remoting', 'tools', 'build'))
40 sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'liblouis'))
41 sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'WebKit',
42 'Source', 'build', 'scripts'))
44 # On Windows, Psyco shortens warm runs of build/gyp_chromium by about
45 # 20 seconds on a z600 machine with 12 GB of RAM, from 90 down to 70
46 # seconds. Conversely, memory usage of build/gyp_chromium with Psyco
47 # maxes out at about 158 MB vs. 132 MB without it.
49 # Psyco uses native libraries, so we need to load a different
50 # installation depending on which OS we are running under. It has not
51 # been tested whether using Psyco on our Mac and Linux builds is worth
52 # it (the GYP running time is a lot shorter, so the JIT startup cost
53 # may not be worth it).
54 if sys.platform == 'win32':
55 try:
56 sys.path.insert(0, os.path.join(chrome_src, 'third_party', 'psyco_win32'))
57 import psyco
58 except:
59 psyco = None
60 else:
61 psyco = None
64 def GetSupplementalFiles():
65 """Returns a list of the supplemental files that are included in all GYP
66 sources."""
67 return glob.glob(os.path.join(chrome_src, '*', 'supplement.gypi'))
70 def ProcessGypDefinesItems(items):
71 """Converts a list of strings to a list of key-value pairs."""
72 result = []
73 for item in items:
74 tokens = item.split('=', 1)
75 # Some GYP variables have hyphens, which we don't support.
76 if len(tokens) == 2:
77 result += [(tokens[0], tokens[1])]
78 else:
79 # No value supplied, treat it as a boolean and set it. Note that we
80 # use the string '1' here so we have a consistent definition whether
81 # you do 'foo=1' or 'foo'.
82 result += [(tokens[0], '1')]
83 return result
86 def GetGypVars(supplemental_files):
87 """Returns a dictionary of all GYP vars."""
88 # Find the .gyp directory in the user's home directory.
89 home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None)
90 if home_dot_gyp:
91 home_dot_gyp = os.path.expanduser(home_dot_gyp)
92 if not home_dot_gyp:
93 home_vars = ['HOME']
94 if sys.platform in ('cygwin', 'win32'):
95 home_vars.append('USERPROFILE')
96 for home_var in home_vars:
97 home = os.getenv(home_var)
98 if home != None:
99 home_dot_gyp = os.path.join(home, '.gyp')
100 if not os.path.exists(home_dot_gyp):
101 home_dot_gyp = None
102 else:
103 break
105 if home_dot_gyp:
106 include_gypi = os.path.join(home_dot_gyp, "include.gypi")
107 if os.path.exists(include_gypi):
108 supplemental_files += [include_gypi]
110 # GYP defines from the supplemental.gypi files.
111 supp_items = []
112 for supplement in supplemental_files:
113 with open(supplement, 'r') as f:
114 try:
115 file_data = eval(f.read(), {'__builtins__': None}, None)
116 except SyntaxError, e:
117 e.filename = os.path.abspath(supplement)
118 raise
119 variables = file_data.get('variables', [])
120 for v in variables:
121 supp_items += [(v, str(variables[v]))]
123 # GYP defines from the environment.
124 env_items = ProcessGypDefinesItems(
125 shlex.split(os.environ.get('GYP_DEFINES', '')))
127 # GYP defines from the command line. We can't use optparse since we want
128 # to ignore all arguments other than "-D".
129 cmdline_input_items = []
130 for i in range(len(sys.argv))[1:]:
131 if sys.argv[i].startswith('-D'):
132 if sys.argv[i] == '-D' and i + 1 < len(sys.argv):
133 cmdline_input_items += [sys.argv[i + 1]]
134 elif len(sys.argv[i]) > 2:
135 cmdline_input_items += [sys.argv[i][2:]]
136 cmdline_items = ProcessGypDefinesItems(cmdline_input_items)
138 vars_dict = dict(supp_items + env_items + cmdline_items)
139 return vars_dict
142 def GetOutputDirectory():
143 """Returns the output directory that GYP will use."""
144 # GYP generator flags from the command line. We can't use optparse since we
145 # want to ignore all arguments other than "-G".
146 needle = '-Goutput_dir='
147 cmdline_input_items = []
148 for item in sys.argv[1:]:
149 if item.startswith(needle):
150 return item[len(needle):]
152 env_items = shlex.split(os.environ.get('GYP_GENERATOR_FLAGS', ''))
153 needle = 'output_dir='
154 for item in env_items:
155 if item.startswith(needle):
156 return item[len(needle):]
158 return "out"
161 def additional_include_files(supplemental_files, args=[]):
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.
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:]))
176 result = []
177 def AddInclude(path):
178 if os.path.realpath(path) not in specified_includes:
179 result.append(path)
181 if os.environ.get('GYP_INCLUDE_FIRST') != None:
182 AddInclude(os.path.join(chrome_src, os.environ.get('GYP_INCLUDE_FIRST')))
184 # Always include common.gypi.
185 AddInclude(os.path.join(script_dir, 'common.gypi'))
187 # Optionally add supplemental .gypi files if present.
188 for supplement in supplemental_files:
189 AddInclude(supplement)
191 if os.environ.get('GYP_INCLUDE_LAST') != None:
192 AddInclude(os.path.join(chrome_src, os.environ.get('GYP_INCLUDE_LAST')))
194 return result
197 if __name__ == '__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 import gc
203 gc.disable()
205 args = sys.argv[1:]
207 use_analyzer = len(args) and args[0] == '--analyzer'
208 if use_analyzer:
209 args.pop(0)
210 os.environ['GYP_GENERATORS'] = 'analyzer'
211 args.append('-Gconfig_path=' + args.pop(0))
212 args.append('-Ganalyzer_output_path=' + args.pop(0))
214 if int(os.environ.get('GYP_CHROMIUM_NO_ACTION', 0)):
215 print 'Skipping gyp_chromium due to GYP_CHROMIUM_NO_ACTION env var.'
216 sys.exit(0)
218 # Use the Psyco JIT if available.
219 if psyco:
220 psyco.profile()
221 print "Enabled Psyco JIT."
223 # Fall back on hermetic python if we happen to get run under cygwin.
224 # TODO(bradnelson): take this out once this issue is fixed:
225 # http://code.google.com/p/gyp/issues/detail?id=177
226 if sys.platform == 'cygwin':
227 import find_depot_tools
228 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
229 python_dir = sorted(glob.glob(os.path.join(depot_tools_path,
230 'python2*_bin')))[-1]
231 env = os.environ.copy()
232 env['PATH'] = python_dir + os.pathsep + env.get('PATH', '')
233 p = subprocess.Popen(
234 [os.path.join(python_dir, 'python.exe')] + sys.argv,
235 env=env, shell=False)
236 p.communicate()
237 sys.exit(p.returncode)
239 # This could give false positives since it doesn't actually do real option
240 # parsing. Oh well.
241 gyp_file_specified = False
242 for arg in args:
243 if arg.endswith('.gyp'):
244 gyp_file_specified = True
245 break
247 gyp_environment.SetEnvironment()
249 # If we didn't get a file, check an env var, and then fall back to
250 # assuming 'all.gyp' from the same directory as the script.
251 if not gyp_file_specified:
252 gyp_file = os.environ.get('CHROMIUM_GYP_FILE')
253 if gyp_file:
254 # Note that CHROMIUM_GYP_FILE values can't have backslashes as
255 # path separators even on Windows due to the use of shlex.split().
256 args.extend(shlex.split(gyp_file))
257 else:
258 args.append(os.path.join(script_dir, 'all.gyp'))
260 # There shouldn't be a circular dependency relationship between .gyp files,
261 # but in Chromium's .gyp files, on non-Mac platforms, circular relationships
262 # currently exist. The check for circular dependencies is currently
263 # bypassed on other platforms, but is left enabled on the Mac, where a
264 # violation of the rule causes Xcode to misbehave badly.
265 # TODO(mark): Find and kill remaining circular dependencies, and remove this
266 # option. http://crbug.com/35878.
267 # TODO(tc): Fix circular dependencies in ChromiumOS then add linux2 to the
268 # list.
269 if sys.platform not in ('darwin',):
270 args.append('--no-circular-check')
272 # We explicitly don't support the make gyp generator (crbug.com/348686). Be
273 # nice and fail here, rather than choking in gyp.
274 if re.search(r'(^|,|\s)make($|,|\s)', os.environ.get('GYP_GENERATORS', '')):
275 print 'Error: make gyp generator not supported (check GYP_GENERATORS).'
276 sys.exit(1)
278 # We explicitly don't support the native msvs gyp generator. Be nice and
279 # fail here, rather than generating broken projects.
280 if re.search(r'(^|,|\s)msvs($|,|\s)', os.environ.get('GYP_GENERATORS', '')):
281 print 'Error: msvs gyp generator not supported (check GYP_GENERATORS).'
282 print 'Did you mean to use the `msvs-ninja` generator?'
283 sys.exit(1)
285 # If CHROMIUM_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check
286 # to enfore syntax checking.
287 syntax_check = os.environ.get('CHROMIUM_GYP_SYNTAX_CHECK')
288 if syntax_check and int(syntax_check):
289 args.append('--check')
291 supplemental_includes = GetSupplementalFiles()
292 gyp_vars_dict = GetGypVars(supplemental_includes)
294 # TODO(dmikurube): Remove these checks and messages after a while.
295 if ('linux_use_tcmalloc' in gyp_vars_dict or
296 'android_use_tcmalloc' in gyp_vars_dict):
297 print '*****************************************************************'
298 print '"linux_use_tcmalloc" and "android_use_tcmalloc" are deprecated!'
299 print '-----------------------------------------------------------------'
300 print 'You specify "linux_use_tcmalloc" or "android_use_tcmalloc" in'
301 print 'your GYP_DEFINES. Please switch them into "use_allocator" now.'
302 print 'See http://crbug.com/345554 for the details.'
303 print '*****************************************************************'
305 # Automatically turn on crosscompile support for platforms that need it.
306 # (The Chrome OS build sets CC_host / CC_target which implicitly enables
307 # this mode.)
308 if all(('ninja' in os.environ.get('GYP_GENERATORS', ''),
309 gyp_vars_dict.get('OS') in ['android', 'ios'],
310 'GYP_CROSSCOMPILE' not in os.environ)):
311 os.environ['GYP_CROSSCOMPILE'] = '1'
312 if gyp_vars_dict.get('OS') == 'android':
313 args.append('--check')
315 args.extend(
316 ['-I' + i for i in additional_include_files(supplemental_includes, args)])
318 args.extend(['-D', 'gyp_output_dir=' + GetOutputDirectory()])
320 if not use_analyzer:
321 print 'Updating projects from gyp files...'
322 sys.stdout.flush()
324 # Off we go...
325 gyp_rc = gyp.main(args)
327 if not use_analyzer:
328 vs2013_runtime_dll_dirs = vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs()
329 if vs2013_runtime_dll_dirs:
330 x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
331 vs_toolchain.CopyVsRuntimeDlls(
332 os.path.join(chrome_src, GetOutputDirectory()),
333 (x86_runtime, x64_runtime))
335 sys.exit(gyp_rc)