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 Crosswalk that adds some support for how GYP
8 # is invoked by Chromium beyond what can be done in the gclient hooks.
18 xwalk_dir = os.path.dirname(os.path.realpath(__file__))
19 chrome_src = os.path.abspath(os.path.join(xwalk_dir, os.pardir))
21 sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
24 sys.path.insert(0, os.path.join(chrome_src, 'build'))
28 # Assume this file is in a one-level-deep subdirectory of the source root.
29 SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
31 # Add paths so that pymod_do_main(...) can import files.
32 sys.path.insert(1, os.path.join(chrome_src, 'tools'))
33 sys.path.insert(1, os.path.join(chrome_src, 'tools', 'generate_shim_headers'))
34 sys.path.insert(1, os.path.join(chrome_src, 'tools', 'grit'))
35 sys.path.insert(1, os.path.join(chrome_src, 'chrome', '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',
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 # FIXME(rakuco,joone): This is a workaround to allow the Tizen build to
45 # proceed, as we use make there and GN isn't involved in this case (otherwise
46 # we run into problems like GN always looking for GTK+ dependencies).
47 if os.environ.get('GYP_GENERATORS') != 'make':
48 import find_depot_tools
50 # On Windows, Psyco shortens warm runs of build/gyp_chromium by about
51 # 20 seconds on a z600 machine with 12 GB of RAM, from 90 down to 70
52 # seconds. Conversely, memory usage of build/gyp_chromium with Psyco
53 # maxes out at about 158 MB vs. 132 MB without it.
55 # Psyco uses native libraries, so we need to load a different
56 # installation depending on which OS we are running under. It has not
57 # been tested whether using Psyco on our Mac and Linux builds is worth
58 # it (the GYP running time is a lot shorter, so the JIT startup cost
59 # may not be worth it).
60 if sys.platform == 'win32':
62 sys.path.insert(0, os.path.join(chrome_src, 'third_party', 'psyco_win32'))
70 def GetSupplementalFiles():
71 """Returns a list of the supplemental files that are included in all GYP
73 return glob.glob(os.path.join(chrome_src, '*', 'supplement.gypi'))
76 def FormatKeyForGN(key):
77 """Returns the given GYP key reformatted for GN.
79 GYP dictionary keys can be almost anything, but in GN they are identifiers
80 and must follow the same rules. This reformats such keys to be valid GN
82 return ''.join([c if c in string.ascii_letters else '_' for c in key])
85 def EscapeStringForGN(s):
86 """Converts a string to a GN string literal."""
87 # Escape $ characters which have special meaning to GN.
88 return '"' + s.replace('$', '\\$').replace('"', '\\"') + '"'
91 def ProcessGypDefinesItems(items):
92 """Converts a list of strings to a list of key-value pairs."""
95 tokens = item.split('=', 1)
96 # Some GYP variables have hyphens, which we don't support.
97 key = FormatKeyForGN(tokens[0])
99 result += [(key, tokens[1])]
101 # No value supplied, treat it as a boolean and set it. Note that we
102 # use the string '1' here so we have a consistent definition whether
103 # you do 'foo=1' or 'foo'.
104 result += [(key, '1')]
107 def GetGypVarsForGN(supplemental_files):
108 """Returns a dictionary of all GYP vars that we will be passing to GN."""
110 # GYP defines from the supplemental.gypi files.
112 for supplement in supplemental_files:
113 with open(supplement, 'r') as f:
115 file_data = eval(f.read(), {'__builtins__': None}, None)
116 except SyntaxError, e:
117 e.filename = os.path.abspath(supplement)
119 variables = file_data.get('variables', [])
121 supp_items += [(FormatKeyForGN(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 return dict(supp_items + env_items + cmdline_items)
140 def GetOutputDirectory():
141 """Returns the output directory that GYP will use."""
142 # GYP generator flags from the command line. We can't use optparse since we
143 # want to ignore all arguments other than "-G".
144 needle = '-Goutput_dir='
145 cmdline_input_items = []
146 for item in sys.argv[1:]:
147 if item.startswith(needle):
148 return item[len(needle):]
150 env_items = shlex.split(os.environ.get('GYP_GENERATOR_FLAGS', ''))
151 needle = 'output_dir='
152 for item in env_items:
153 if item.startswith(needle):
154 return item[len(needle):]
158 def GetArgsStringForGN(supplemental_files):
159 """Returns the args to pass to GN.
160 Based on a subset of the GYP variables that have been rewritten a bit."""
162 # Find the .gyp directory in the user's home directory.
163 home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None)
165 home_dot_gyp = os.path.expanduser(home_dot_gyp)
168 if sys.platform in ('cygwin', 'win32'):
169 home_vars.append('USERPROFILE')
170 for home_var in home_vars:
171 home = os.getenv(home_var)
173 home_dot_gyp = os.path.join(home, '.gyp')
174 if not os.path.exists(home_dot_gyp):
180 include_gypi = os.path.join(home_dot_gyp, "include.gypi")
181 if os.path.exists(include_gypi):
182 supplemental_files += [include_gypi]
184 vars_dict = GetGypVarsForGN(supplemental_files)
187 # Note: These are the additional flags passed to various builds by builders
188 # on the main waterfall. We'll probably need to add these at some point:
189 # mac_strip_release=1 http://crbug.com/330301
190 # linux_dump_symbols=0 http://crbug.com/330300
191 # host_os=linux Probably can skip, GN knows the host OS.
192 # order_text_section=<path> http://crbug.com/330299
193 # chromium_win_pch=0 http://crbug.com/297678
194 # chromium_ios_signing=0 http://crbug.com/330302
195 # linux_use_tcmalloc=0 http://crbug.com/330303
196 # release_extra_flags=... http://crbug.com/330305
198 # These tuples of (key, value, gn_arg_string) use the gn_arg_string for
199 # gn when the key is set to the given value in the GYP arguments.
201 ('android_webview_build', '1', 'is_android_webview_build=true'),
202 ('branding', 'Chrome', 'is_chrome_branded=true'),
203 ('build_for_tool', 'drmemory', 'disable_iterator_debugging=true'),
204 ('build_for_tool', 'tsan', 'disable_iterator_debugging=true'),
205 ('buildtype', 'Official', 'is_official_build=true'),
206 ('component', 'shared_library', 'is_component_build=true'),
207 ('clang', '1', 'is_clang=true'),
208 ('clang_use_chrome_plugins', '0', 'clang_use_chrome_plugins=false'),
209 ('disable_glibcxx_debug', '1', 'disable_iterator_debugging=true'),
210 ('target_arch', 'ia32', 'cpu_arch="x86"'),
211 ('target_arch', 'x64', 'cpu_arch="x64" force_win64=true'),
212 ('target_arch', 'arm', 'cpu_arch="arm"'),
213 ('target_arch', 'mipsel', 'cpu_arch="mipsel"'),
214 ('fastbuild', '0', 'symbol_level=2'),
215 ('fastbuild', '1', 'symbol_level=1'),
216 ('fastbuild', '2', 'symbol_level=0'),
217 ('OS', 'ios', 'os="ios"'),
218 ('OS', 'android', 'os="android"'),
219 ('chromeos', '1', 'os="chromeos"'),
220 ('use_aura', '1', 'use_aura=true'),
221 ('use_goma', '1', 'use_goma=true'),
222 ('asan', '1', 'is_asan=true'),
223 ('lsan', '1', 'is_lsan=true'),
224 ('msan', '1', 'is_msan=true'),
225 ('tsan', '1', 'is_tsan=true'),
227 for i in remap_cases:
228 if i[0] in vars_dict and vars_dict[i[0]] == i[1]:
229 gn_args += ' ' + i[2]
231 # These string arguments get passed directly as GN strings.
232 for v in ['android_src', 'windows_sdk_path', 'arm_float_abi']:
234 gn_args += ' ' + v + '=' + EscapeStringForGN(vars_dict[v])
236 # gomadir is renamed goma_dir in the GN build.
237 if 'gomadir' in vars_dict:
238 gn_args += ' goma_dir=%s' % EscapeStringForGN(vars_dict['gomadir'])
240 # These arguments get passed directly as integers (avoiding the quoting and
241 # escaping of the string ones above).
242 for v in ['arm_version']:
244 gn_args += ' %s=%s' % (v, vars_dict[v])
246 # Some other flags come from GYP environment variables.
247 gyp_msvs_version = os.environ.get('GYP_MSVS_VERSION', '')
249 gn_args += ' visual_studio_version=' + EscapeStringForGN(gyp_msvs_version)
250 gyp_msvs_override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH', '')
251 if gyp_msvs_override_path:
252 gn_args += ' visual_studio_path=' + \
253 EscapeStringForGN(gyp_msvs_override_path)
255 # Set the GYP flag so BUILD files know they're being invoked in GYP mode.
256 gn_args += ' is_gyp=true'
258 gyp_outdir = GetOutputDirectory()
259 gn_args += ' gyp_output_dir=\"%s\"' % gyp_outdir
261 return gn_args.strip()
264 def additional_include_files(supplemental_files, args=[]):
266 Returns a list of additional (.gypi) files to include, without duplicating
267 ones that are already specified on the command line. The list of supplemental
268 include files is passed in as an argument.
270 # Determine the include files specified on the command line.
271 # This doesn't cover all the different option formats you can use,
272 # but it's mainly intended to avoid duplicating flags on the automatic
273 # makefile regeneration which only uses this format.
274 specified_includes = set()
276 if arg.startswith('-I') and len(arg) > 2:
277 specified_includes.add(os.path.realpath(arg[2:]))
280 def AddInclude(path):
281 if os.path.realpath(path) not in specified_includes:
284 # Include xwalk common.gypi to effect chromium source tree.
285 AddInclude(os.path.join(xwalk_dir, 'build', 'common.gypi'))
287 # Always include common.gypi.
288 AddInclude(os.path.join(chrome_src, 'build', 'common.gypi'))
290 # Optionally add supplemental .gypi files if present.
291 for supplement in supplemental_files:
292 AddInclude(supplement)
297 def RunGN(supplemental_includes):
298 """Runs GN, returning True if it succeeded, printing an error and returning
301 # The binaries in platform-specific subdirectories in src/tools/gn/bin.
302 gnpath = SRC_DIR + '/tools/gn/bin/'
303 if sys.platform in ('cygwin', 'win32'):
304 gnpath += 'win/gn.exe'
305 elif sys.platform.startswith('linux'):
306 # On Linux we have 32-bit and 64-bit versions.
307 if subprocess.check_output(["getconf", "LONG_BIT"]).find("64") >= 0:
310 gnpath += 'linux/gn32'
311 elif sys.platform == 'darwin':
314 print 'Unknown platform for GN: ', sys.platform
317 print 'Generating gyp files from GN...'
319 # Need to pass both the source root (the bots don't run this command from
320 # within the source tree) as well as set the is_gyp value so the BUILD files
321 # to know they're being run under GYP.
322 args = [gnpath, 'gyp', '-q',
323 '--root=' + chrome_src,
324 '--args=' + GetArgsStringForGN(supplemental_includes),
325 '--output=//' + GetOutputDirectory() + '/gn_build/']
326 return subprocess.call(args) == 0
329 if __name__ == '__main__':
332 if int(os.environ.get('GYP_CHROMIUM_NO_ACTION', 0)):
333 print 'Skipping gyp_chromium due to GYP_CHROMIUM_NO_ACTION env var.'
336 # Support external media types capability such as MP4/MP3.
337 args = list(set(args))
339 ip_media_codecs = False # Default: no third-party codecs be build in.
341 if arg.startswith('-Dproprietary_codecs') or arg.startswith('-Dffmpeg_branding'):
343 elif arg == '-Dmediacodecs_EULA=1':
344 ip_media_codecs = True # Exception: mediacodecs_EULA be enabled.
349 args.append('-Dproprietary_codecs=1')
351 # Triggering media playback dynamically with third-party codecs by owner.
352 if ip_media_codecs == True:
353 args.append('-Dffmpeg_branding=Chrome')
355 # Enable Web Audio by default on Android x86
356 if landmine_utils.platform() == 'android':
357 args.append('-Duse_openmax_dl_fft=1')
359 # Enable Aura by default on all platforms except Android
360 if landmine_utils.platform() != 'android' and landmine_utils.platform() != 'mac':
361 args.append('-Duse_aura=1')
363 if landmine_utils.platform() == 'android':
364 args.append('-Dnotifications=1')
365 args.append('-Drelease_unwind_tables=0')
367 # Use the Psyco JIT if available.
370 print "Enabled Psyco JIT."
372 # Fall back on hermetic python if we happen to get run under cygwin.
373 # TODO(bradnelson): take this out once this issue is fixed:
374 # http://code.google.com/p/gyp/issues/detail?id=177
375 if sys.platform == 'cygwin':
376 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
377 python_dir = sorted(glob.glob(os.path.join(depot_tools_path,
378 'python2*_bin')))[-1]
379 env = os.environ.copy()
380 env['PATH'] = python_dir + os.pathsep + env.get('PATH', '')
381 p = subprocess.Popen(
382 [os.path.join(python_dir, 'python.exe')] + sys.argv,
383 env=env, shell=False)
385 sys.exit(p.returncode)
387 gyp_helper.apply_chromium_gyp_env()
389 # This could give false positives since it doesn't actually do real option
391 gyp_file_specified = False
393 if arg.endswith('.gyp'):
394 gyp_file_specified = True
397 # If we didn't get a file, check an env var, and then fall back to
398 # assuming 'all.gyp' from the same directory as the script.
399 if not gyp_file_specified:
400 gyp_file = os.environ.get('CHROMIUM_GYP_FILE')
402 # Note that CHROMIUM_GYP_FILE values can't have backslashes as
403 # path separators even on Windows due to the use of shlex.split().
404 args.extend(shlex.split(gyp_file))
406 args.append(os.path.join(xwalk_dir, 'xwalk.gyp'))
408 # There shouldn't be a circular dependency relationship between .gyp files,
409 # but in Chromium's .gyp files, on non-Mac platforms, circular relationships
410 # currently exist. The check for circular dependencies is currently
411 # bypassed on other platforms, but is left enabled on the Mac, where a
412 # violation of the rule causes Xcode to misbehave badly.
413 # TODO(mark): Find and kill remaining circular dependencies, and remove this
414 # option. http://crbug.com/35878.
415 # TODO(tc): Fix circular dependencies in ChromiumOS then add linux2 to the
417 # TODO(tmpsantos): Make runtime a proper module and enable the circular check
419 args.append('--no-circular-check')
421 # Default to ninja on linux and windows, but only if no generator has
422 # explicitly been set.
423 # Also default to ninja on mac, but only when not building chrome/ios.
424 # . -f / --format has precedence over the env var, no need to check for it
425 # . set the env var only if it hasn't been set yet
426 # . chromium.gyp_env has been applied to os.environ at this point already
427 if sys.platform.startswith('linux') and not os.environ.get('GYP_GENERATORS'):
428 os.environ['GYP_GENERATORS'] = 'ninja'
429 if sys.platform.startswith('win') and not os.environ.get('GYP_GENERATORS'):
430 os.environ['GYP_GENERATORS'] = 'ninja'
431 elif sys.platform == 'darwin' and not os.environ.get('GYP_GENERATORS') and \
432 not 'OS=ios' in os.environ.get('GYP_DEFINES', []):
433 os.environ['GYP_GENERATORS'] = 'ninja'
435 # If using ninja on windows, and the automatic toolchain has been installed
436 # by depot_tools, then use it.
437 if (sys.platform in ('win32', 'cygwin') and
438 os.environ.get('GYP_GENERATORS') == 'ninja'):
439 depot_tools_path = find_depot_tools.add_depot_tools_to_path()
440 toolchain = os.path.normpath(os.path.join(
441 depot_tools_path, 'win_toolchain', 'vs2013_files'))
442 if os.path.isdir(toolchain):
443 os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
444 os.environ['GYP_MSVS_VERSION'] = '2013'
445 # We need to make sure windows_sdk_path is set to the automated
446 # toolchain values in GYP_DEFINES, but don't want to override any other
448 gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
449 win8sdk = os.path.join(toolchain, 'win8sdk')
450 gyp_defines_dict['windows_sdk_path'] = win8sdk
451 os.environ['WINDOWSSDKDIR'] = win8sdk
452 os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
453 for k, v in gyp_defines_dict.iteritems())
454 # Include the VS runtime in the PATH in case it's not machine-installed.
455 runtime_path = ';'.join(os.path.normpath(os.path.join(toolchain, s))
456 for s in ('sys64', 'sys32'))
457 os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
458 print('Using automatic toolchain in %s.' % toolchain)
460 # If CHROMIUM_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check
461 # to enfore syntax checking.
462 syntax_check = os.environ.get('CHROMIUM_GYP_SYNTAX_CHECK')
463 if syntax_check and int(syntax_check):
464 args.append('--check')
466 supplemental_includes = GetSupplementalFiles()
468 # FIXME(rakuco,joone): This is a workaround to allow the Tizen build to
469 # proceed, as we use make there and GN isn't involved in this case (otherwise
470 # we run into problems like GN always looking for GTK+ dependencies).
471 if os.environ.get('GYP_GENERATORS') != 'make':
472 if not RunGN(supplemental_includes):
476 ['-I' + i for i in additional_include_files(supplemental_includes, args)])
478 args.extend(['-D', 'gyp_output_dir=' + GetOutputDirectory()])
480 print 'Updating projects from gyp files...'
484 sys.exit(gyp.main(args))