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.
22 script_dir
= os
.path
.dirname(os
.path
.realpath(__file__
))
23 chrome_src
= os
.path
.abspath(os
.path
.join(script_dir
, os
.pardir
))
25 sys
.path
.insert(0, os
.path
.join(chrome_src
, 'tools', 'gyp', 'pylib'))
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 # 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':
56 sys
.path
.insert(0, os
.path
.join(chrome_src
, 'third_party', 'psyco_win32'))
64 def GetSupplementalFiles():
65 """Returns a list of the supplemental files that are included in all GYP
67 return glob
.glob(os
.path
.join(chrome_src
, '*', 'supplement.gypi'))
70 def FormatKeyForGN(key
):
71 """Returns the given GYP key reformatted for GN.
73 GYP dictionary keys can be almost anything, but in GN they are identifiers
74 and must follow the same rules. This reformats such keys to be valid GN
76 return ''.join([c
if c
in string
.ascii_letters
else '_' for c
in key
])
79 def EscapeStringForGN(s
):
80 """Converts a string to a GN string literal."""
81 for old
, new
in [('\\', '\\\\'), ('$', '\\$'), ('"', '\\"')]:
82 s
= s
.replace(old
, new
)
86 def ProcessGypDefinesItems(items
):
87 """Converts a list of strings to a list of key-value pairs."""
90 tokens
= item
.split('=', 1)
91 # Some GYP variables have hyphens, which we don't support.
92 key
= FormatKeyForGN(tokens
[0])
94 result
+= [(key
, tokens
[1])]
96 # No value supplied, treat it as a boolean and set it. Note that we
97 # use the string '1' here so we have a consistent definition whether
98 # you do 'foo=1' or 'foo'.
99 result
+= [(key
, '1')]
103 def GetGypVarsForGN(supplemental_files
):
104 """Returns a dictionary of all GYP vars that we will be passing to GN."""
105 # Find the .gyp directory in the user's home directory.
106 home_dot_gyp
= os
.environ
.get('GYP_CONFIG_DIR', None)
108 home_dot_gyp
= os
.path
.expanduser(home_dot_gyp
)
111 if sys
.platform
in ('cygwin', 'win32'):
112 home_vars
.append('USERPROFILE')
113 for home_var
in home_vars
:
114 home
= os
.getenv(home_var
)
116 home_dot_gyp
= os
.path
.join(home
, '.gyp')
117 if not os
.path
.exists(home_dot_gyp
):
123 include_gypi
= os
.path
.join(home_dot_gyp
, "include.gypi")
124 if os
.path
.exists(include_gypi
):
125 supplemental_files
+= [include_gypi
]
127 # GYP defines from the supplemental.gypi files.
129 for supplement
in supplemental_files
:
130 with
open(supplement
, 'r') as f
:
132 file_data
= eval(f
.read(), {'__builtins__': None}, None)
133 except SyntaxError, e
:
134 e
.filename
= os
.path
.abspath(supplement
)
136 variables
= file_data
.get('variables', [])
138 supp_items
+= [(FormatKeyForGN(v
), str(variables
[v
]))]
140 # GYP defines from the environment.
141 env_items
= ProcessGypDefinesItems(
142 shlex
.split(os
.environ
.get('GYP_DEFINES', '')))
144 # GYP defines from the command line. We can't use optparse since we want
145 # to ignore all arguments other than "-D".
146 cmdline_input_items
= []
147 for i
in range(len(sys
.argv
))[1:]:
148 if sys
.argv
[i
].startswith('-D'):
149 if sys
.argv
[i
] == '-D' and i
+ 1 < len(sys
.argv
):
150 cmdline_input_items
+= [sys
.argv
[i
+ 1]]
151 elif len(sys
.argv
[i
]) > 2:
152 cmdline_input_items
+= [sys
.argv
[i
][2:]]
153 cmdline_items
= ProcessGypDefinesItems(cmdline_input_items
)
155 vars_dict
= dict(supp_items
+ env_items
+ cmdline_items
)
156 # It's not possible to set a default value for cpu_arch in GN, so do it here
157 # for now (http://crbug.com/344767).
158 if vars_dict
.get('OS') == 'android' and not 'target_arch' in vars_dict
:
159 vars_dict
['target_arch'] = 'arm'
163 def GetOutputDirectory():
164 """Returns the output directory that GYP will use."""
165 # GYP generator flags from the command line. We can't use optparse since we
166 # want to ignore all arguments other than "-G".
167 needle
= '-Goutput_dir='
168 cmdline_input_items
= []
169 for item
in sys
.argv
[1:]:
170 if item
.startswith(needle
):
171 return item
[len(needle
):]
173 env_items
= shlex
.split(os
.environ
.get('GYP_GENERATOR_FLAGS', ''))
174 needle
= 'output_dir='
175 for item
in env_items
:
176 if item
.startswith(needle
):
177 return item
[len(needle
):]
182 def GetArgsStringForGN(vars_dict
):
183 """Returns the args to pass to GN.
184 Based on a subset of the GYP variables that have been rewritten a bit."""
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 # use_allocator=tcmalloc http://crbug.com/330303, 345554
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 ('enable_mdns', '0', 'enable_mdns=false'),
211 ('enable_mdns', '1', 'enable_mdns=true'),
212 ('enable_plugins', '0', 'enable_plugins=false'),
213 ('enable_plugins', '1', 'enable_plugins=true'),
214 ('target_arch', 'ia32', 'cpu_arch="x86"'),
215 ('target_arch', 'x64', 'cpu_arch="x64" force_win64=true'),
216 ('target_arch', 'arm', 'cpu_arch="arm"'),
217 ('target_arch', 'mipsel', 'cpu_arch="mipsel"'),
218 ('fastbuild', '0', 'symbol_level=2'),
219 ('fastbuild', '1', 'symbol_level=1'),
220 ('fastbuild', '2', 'symbol_level=0'),
221 ('OS', 'ios', 'os="ios"'),
222 ('OS', 'android', 'os="android"'),
223 ('chromeos', '1', 'os="chromeos"'),
224 ('use_aura', '1', 'use_aura=true'),
225 ('use_goma', '1', 'use_goma=true'),
226 ('use_openssl', '0', 'use_openssl=false'),
227 ('use_openssl', '1', 'use_openssl=true'),
228 ('asan', '1', 'is_asan=true'),
229 ('lsan', '1', 'is_lsan=true'),
230 ('msan', '1', 'is_msan=true'),
231 ('tsan', '1', 'is_tsan=true'),
233 for i
in remap_cases
:
234 if i
[0] in vars_dict
and vars_dict
[i
[0]] == i
[1]:
235 gn_args
+= ' ' + i
[2]
237 # These string arguments get passed directly as GN strings.
238 for v
in ['android_src', 'arm_float_abi', 'ios_deployment_target',
239 'ios_sdk_path', 'windows_sdk_path']:
241 gn_args
+= ' ' + v
+ '=' + EscapeStringForGN(vars_dict
[v
])
243 # gomadir is renamed goma_dir in the GN build.
244 if 'gomadir' in vars_dict
:
245 gn_args
+= ' goma_dir=%s' % EscapeStringForGN(vars_dict
['gomadir'])
247 # Set the "use_ios_simulator" flag if the ios_sdk_path is set.
248 if 'ios_sdk_path' in vars_dict
:
249 if os
.path
.basename(vars_dict
['ios_sdk_path']).lower().startswith(
251 gn_args
+= ' use_ios_simulator=true'
253 gn_args
+= ' use_ios_simulator=false'
255 # These arguments get passed directly as integers (avoiding the quoting and
256 # escaping of the string ones above).
257 for v
in ['arm_version']:
259 gn_args
+= ' %s=%s' % (v
, vars_dict
[v
])
261 # Some other flags come from GYP environment variables.
262 gyp_msvs_version
= os
.environ
.get('GYP_MSVS_VERSION', '')
264 gn_args
+= ' visual_studio_version=' + EscapeStringForGN(gyp_msvs_version
)
265 gyp_msvs_override_path
= os
.environ
.get('GYP_MSVS_OVERRIDE_PATH', '')
266 if gyp_msvs_override_path
:
267 gn_args
+= ' visual_studio_path=' + \
268 EscapeStringForGN(gyp_msvs_override_path
)
270 # Set the GYP flag so BUILD files know they're being invoked in GYP mode.
271 gn_args
+= ' is_gyp=true'
273 gyp_outdir
= GetOutputDirectory()
274 gn_args
+= ' gyp_output_dir=\"%s\"' % gyp_outdir
276 return gn_args
.strip()
279 def additional_include_files(supplemental_files
, args
=[]):
281 Returns a list of additional (.gypi) files to include, without duplicating
282 ones that are already specified on the command line. The list of supplemental
283 include files is passed in as an argument.
285 # Determine the include files specified on the command line.
286 # This doesn't cover all the different option formats you can use,
287 # but it's mainly intended to avoid duplicating flags on the automatic
288 # makefile regeneration which only uses this format.
289 specified_includes
= set()
291 if arg
.startswith('-I') and len(arg
) > 2:
292 specified_includes
.add(os
.path
.realpath(arg
[2:]))
295 def AddInclude(path
):
296 if os
.path
.realpath(path
) not in specified_includes
:
299 # Always include common.gypi.
300 AddInclude(os
.path
.join(script_dir
, 'common.gypi'))
302 # Optionally add supplemental .gypi files if present.
303 for supplement
in supplemental_files
:
304 AddInclude(supplement
)
309 def RunGN(vars_dict
):
310 """Runs GN, returning True if it succeeded, printing an error and returning
313 # The binaries in platform-specific subdirectories in src/tools/gn/bin.
314 gnpath
= SRC_DIR
+ '/tools/gn/bin/'
315 if sys
.platform
in ('cygwin', 'win32'):
316 gnpath
+= 'win/gn.exe'
317 elif sys
.platform
.startswith('linux'):
318 # On Linux we have 32-bit and 64-bit versions.
319 if subprocess
.check_output(["getconf", "LONG_BIT"]).find("64") >= 0:
322 gnpath
+= 'linux/gn32'
323 elif sys
.platform
== 'darwin':
326 print 'Unknown platform for GN: ', sys
.platform
329 print 'Generating gyp files from GN...'
331 # Need to pass both the source root (the bots don't run this command from
332 # within the source tree) as well as set the is_gyp value so the BUILD files
333 # to know they're being run under GYP.
334 args
= [gnpath
, 'gyp', '-q',
335 '--root=' + chrome_src
,
336 '--args=' + GetArgsStringForGN(vars_dict
),
337 '--output=//' + GetOutputDirectory() + '/gn_build/']
338 return subprocess
.call(args
) == 0
341 def GetDesiredVsToolchainHashes():
342 """Load a list of SHA1s corresponding to the toolchains that we want installed
344 sha1path
= os
.path
.join(script_dir
, 'toolchain_vs2013.hash')
345 with
open(sha1path
, 'rb') as f
:
346 return f
.read().strip().splitlines()
349 def DownloadVsToolChain():
350 """Download the Visual Studio toolchain on Windows.
352 If on Windows, request that depot_tools install/update the automatic
353 toolchain, and then use it (unless opted-out) and return a tuple containing
354 the x64 and x86 paths. Otherwise return None.
356 vs2013_runtime_dll_dirs
= None
357 depot_tools_win_toolchain
= \
358 bool(int(os
.environ
.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
359 if sys
.platform
in ('win32', 'cygwin') and depot_tools_win_toolchain
:
360 import find_depot_tools
361 depot_tools_path
= find_depot_tools
.add_depot_tools_to_path()
362 temp_handle
, data_file
= tempfile
.mkstemp(suffix
='.json')
363 os
.close(temp_handle
)
364 get_toolchain_args
= [
366 os
.path
.join(depot_tools_path
,
368 'get_toolchain_if_necessary.py'),
369 '--output-json', data_file
,
370 ] + GetDesiredVsToolchainHashes()
371 subprocess
.check_call(get_toolchain_args
)
373 with
open(data_file
, 'r') as tempf
:
374 toolchain_data
= json
.load(tempf
)
377 toolchain
= toolchain_data
['path']
378 version
= toolchain_data
['version']
379 version_is_pro
= version
[-1] != 'e'
380 win8sdk
= toolchain_data
['win8sdk']
381 wdk
= toolchain_data
['wdk']
382 # TODO(scottmg): The order unfortunately matters in these. They should be
383 # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
384 # below). http://crbug.com/345992
385 vs2013_runtime_dll_dirs
= toolchain_data
['runtime_dirs']
387 os
.environ
['GYP_MSVS_OVERRIDE_PATH'] = toolchain
388 os
.environ
['GYP_MSVS_VERSION'] = version
389 # We need to make sure windows_sdk_path is set to the automated
390 # toolchain values in GYP_DEFINES, but don't want to override any
391 # otheroptions.express
393 gyp_defines_dict
= gyp
.NameValueListToDict(gyp
.ShlexEnv('GYP_DEFINES'))
394 gyp_defines_dict
['windows_sdk_path'] = win8sdk
395 os
.environ
['GYP_DEFINES'] = ' '.join('%s=%s' % (k
, pipes
.quote(str(v
)))
396 for k
, v
in gyp_defines_dict
.iteritems())
397 os
.environ
['WINDOWSSDKDIR'] = win8sdk
398 os
.environ
['WDK_DIR'] = wdk
399 # Include the VS runtime in the PATH in case it's not machine-installed.
400 runtime_path
= ';'.join(vs2013_runtime_dll_dirs
)
401 os
.environ
['PATH'] = runtime_path
+ ';' + os
.environ
['PATH']
402 print('Using automatic toolchain in %s (%s edition).' % (
403 toolchain
, 'Pro' if version_is_pro
else 'Express'))
404 return vs2013_runtime_dll_dirs
407 def CopyVsRuntimeDlls(output_dir
, runtime_dirs
):
408 """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
409 directory so that even if not system-installed, built binaries are likely to
412 This needs to be run after gyp has been run so that the expected target
413 output directories are already created.
415 assert sys
.platform
.startswith(('win32', 'cygwin'))
417 def copy_runtime(target_dir
, source_dir
, dll_pattern
):
418 """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
419 exist, but the target directory does exist."""
420 for which
in ('p', 'r'):
421 dll
= dll_pattern
% which
422 target
= os
.path
.join(target_dir
, dll
)
423 source
= os
.path
.join(source_dir
, dll
)
424 # If gyp generated to that output dir, and the runtime isn't already
425 # there, then copy it over.
426 if (os
.path
.isdir(target_dir
) and
427 (not os
.path
.isfile(target
) or
428 os
.stat(target
).st_mtime
!= os
.stat(source
).st_mtime
)):
429 print 'Copying %s to %s...' % (source
, target
)
430 if os
.path
.exists(target
):
432 shutil
.copy2(source
, target
)
434 x86
, x64
= runtime_dirs
435 out_debug
= os
.path
.join(output_dir
, 'Debug')
436 out_debug_nacl64
= os
.path
.join(output_dir
, 'Debug', 'x64')
437 out_release
= os
.path
.join(output_dir
, 'Release')
438 out_release_nacl64
= os
.path
.join(output_dir
, 'Release', 'x64')
439 out_debug_x64
= os
.path
.join(output_dir
, 'Debug_x64')
440 out_release_x64
= os
.path
.join(output_dir
, 'Release_x64')
442 if os
.path
.exists(out_debug
) and not os
.path
.exists(out_debug_nacl64
):
443 os
.makedirs(out_debug_nacl64
)
444 if os
.path
.exists(out_release
) and not os
.path
.exists(out_release_nacl64
):
445 os
.makedirs(out_release_nacl64
)
446 copy_runtime(out_debug
, x86
, 'msvc%s120d.dll')
447 copy_runtime(out_release
, x86
, 'msvc%s120.dll')
448 copy_runtime(out_debug_x64
, x64
, 'msvc%s120d.dll')
449 copy_runtime(out_release_x64
, x64
, 'msvc%s120.dll')
450 copy_runtime(out_debug_nacl64
, x64
, 'msvc%s120d.dll')
451 copy_runtime(out_release_nacl64
, x64
, 'msvc%s120.dll')
454 if __name__
== '__main__':
457 if int(os
.environ
.get('GYP_CHROMIUM_NO_ACTION', 0)):
458 print 'Skipping gyp_chromium due to GYP_CHROMIUM_NO_ACTION env var.'
461 # Use the Psyco JIT if available.
464 print "Enabled Psyco JIT."
466 # Fall back on hermetic python if we happen to get run under cygwin.
467 # TODO(bradnelson): take this out once this issue is fixed:
468 # http://code.google.com/p/gyp/issues/detail?id=177
469 if sys
.platform
== 'cygwin':
470 import find_depot_tools
471 depot_tools_path
= find_depot_tools
.add_depot_tools_to_path()
472 python_dir
= sorted(glob
.glob(os
.path
.join(depot_tools_path
,
473 'python2*_bin')))[-1]
474 env
= os
.environ
.copy()
475 env
['PATH'] = python_dir
+ os
.pathsep
+ env
.get('PATH', '')
476 p
= subprocess
.Popen(
477 [os
.path
.join(python_dir
, 'python.exe')] + sys
.argv
,
478 env
=env
, shell
=False)
480 sys
.exit(p
.returncode
)
482 gyp_helper
.apply_chromium_gyp_env()
484 # This could give false positives since it doesn't actually do real option
486 gyp_file_specified
= False
488 if arg
.endswith('.gyp'):
489 gyp_file_specified
= True
492 # If we didn't get a file, check an env var, and then fall back to
493 # assuming 'all.gyp' from the same directory as the script.
494 if not gyp_file_specified
:
495 gyp_file
= os
.environ
.get('CHROMIUM_GYP_FILE')
497 # Note that CHROMIUM_GYP_FILE values can't have backslashes as
498 # path separators even on Windows due to the use of shlex.split().
499 args
.extend(shlex
.split(gyp_file
))
501 args
.append(os
.path
.join(script_dir
, 'all.gyp'))
503 # There shouldn't be a circular dependency relationship between .gyp files,
504 # but in Chromium's .gyp files, on non-Mac platforms, circular relationships
505 # currently exist. The check for circular dependencies is currently
506 # bypassed on other platforms, but is left enabled on the Mac, where a
507 # violation of the rule causes Xcode to misbehave badly.
508 # TODO(mark): Find and kill remaining circular dependencies, and remove this
509 # option. http://crbug.com/35878.
510 # TODO(tc): Fix circular dependencies in ChromiumOS then add linux2 to the
512 if sys
.platform
not in ('darwin',):
513 args
.append('--no-circular-check')
515 # We explicitly don't support the make gyp generator (crbug.com/348686). Be
516 # nice and fail here, rather than choking in gyp.
517 if 'make' in os
.environ
.get('GYP_GENERATORS', ''):
518 print 'Error: make gyp generator not supported (check GYP_GENERATORS).'
521 # Default to ninja on linux and windows, but only if no generator has
522 # explicitly been set.
523 # Also default to ninja on mac, but only when not building chrome/ios.
524 # . -f / --format has precedence over the env var, no need to check for it
525 # . set the env var only if it hasn't been set yet
526 # . chromium.gyp_env has been applied to os.environ at this point already
527 if sys
.platform
.startswith(('linux', 'win', 'freebsd')) and \
528 not os
.environ
.get('GYP_GENERATORS'):
529 os
.environ
['GYP_GENERATORS'] = 'ninja'
530 elif sys
.platform
== 'darwin' and not os
.environ
.get('GYP_GENERATORS') and \
531 not 'OS=ios' in os
.environ
.get('GYP_DEFINES', []):
532 os
.environ
['GYP_GENERATORS'] = 'ninja'
534 vs2013_runtime_dll_dirs
= DownloadVsToolChain()
536 # If CHROMIUM_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check
537 # to enfore syntax checking.
538 syntax_check
= os
.environ
.get('CHROMIUM_GYP_SYNTAX_CHECK')
539 if syntax_check
and int(syntax_check
):
540 args
.append('--check')
542 supplemental_includes
= GetSupplementalFiles()
543 gn_vars_dict
= GetGypVarsForGN(supplemental_includes
)
545 # Automatically turn on crosscompile support for platforms that need it.
546 # (The Chrome OS build sets CC_host / CC_target which implicitly enables
548 if all(('ninja' in os
.environ
.get('GYP_GENERATORS', ''),
549 gn_vars_dict
.get('OS') in ['android', 'ios'],
550 'GYP_CROSSCOMPILE' not in os
.environ
)):
551 os
.environ
['GYP_CROSSCOMPILE'] = '1'
553 # TODO(brettw) bug 350974 either turn back on GN or delete all of this code.
554 #if not RunGN(gn_vars_dict):
557 ['-I' + i
for i
in additional_include_files(supplemental_includes
, args
)])
559 args
.extend(['-D', 'gyp_output_dir=' + GetOutputDirectory()])
561 print 'Updating projects from gyp files...'
565 gyp_rc
= gyp
.main(args
)
567 # Check for landmines (reasons to clobber the build). This must be run here,
568 # rather than a separate runhooks step so that any environment modifications
569 # from above are picked up.
570 print 'Running build/landmines.py...'
571 subprocess
.check_call(
572 [sys
.executable
, os
.path
.join(script_dir
, 'landmines.py')])
574 if vs2013_runtime_dll_dirs
:
575 x64_runtime
, x86_runtime
= vs2013_runtime_dll_dirs
576 CopyVsRuntimeDlls(os
.path
.join(chrome_src
, GetOutputDirectory()),
577 (x86_runtime
, x64_runtime
))