• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import json
6import os
7import pipes
8import shutil
9import subprocess
10import sys
11
12
13script_dir = os.path.dirname(os.path.realpath(__file__))
14chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir))
15SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16sys.path.insert(1, os.path.join(chrome_src, 'tools'))
17sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib'))
18json_data_file = os.path.join(script_dir, 'win_toolchain.json')
19
20
21import gyp
22
23
24def SetEnvironmentAndGetRuntimeDllDirs():
25  """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
26  returns the location of the VS runtime DLLs so they can be copied into
27  the output directory after gyp generation.
28  """
29  vs2013_runtime_dll_dirs = None
30  depot_tools_win_toolchain = \
31      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
32  if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
33    if not os.path.exists(json_data_file):
34      Update()
35    with open(json_data_file, 'r') as tempf:
36      toolchain_data = json.load(tempf)
37
38    toolchain = toolchain_data['path']
39    version = toolchain_data['version']
40    version_is_pro = version[-1] != 'e'
41    win8sdk = toolchain_data['win8sdk']
42    wdk = toolchain_data['wdk']
43    # TODO(scottmg): The order unfortunately matters in these. They should be
44    # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
45    # below). http://crbug.com/345992
46    vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
47
48    os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
49    os.environ['GYP_MSVS_VERSION'] = version
50    # We need to make sure windows_sdk_path is set to the automated
51    # toolchain values in GYP_DEFINES, but don't want to override any
52    # otheroptions.express
53    # values there.
54    gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
55    gyp_defines_dict['windows_sdk_path'] = win8sdk
56    os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
57        for k, v in gyp_defines_dict.iteritems())
58    os.environ['WINDOWSSDKDIR'] = win8sdk
59    os.environ['WDK_DIR'] = wdk
60    # Include the VS runtime in the PATH in case it's not machine-installed.
61    runtime_path = ';'.join(vs2013_runtime_dll_dirs)
62    os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
63  return vs2013_runtime_dll_dirs
64
65
66def CopyVsRuntimeDlls(output_dir, runtime_dirs):
67  """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
68  directory so that even if not system-installed, built binaries are likely to
69  be able to run.
70
71  This needs to be run after gyp has been run so that the expected target
72  output directories are already created.
73  """
74  assert sys.platform.startswith(('win32', 'cygwin'))
75
76  def copy_runtime_impl(target, source):
77    """Copy |source| to |target| if it doesn't already exist or if it need to be
78    updated.
79    """
80    if (os.path.isdir(os.path.dirname(target)) and
81        (not os.path.isfile(target) or
82          os.stat(target).st_mtime != os.stat(source).st_mtime)):
83      print 'Copying %s to %s...' % (source, target)
84      if os.path.exists(target):
85        os.unlink(target)
86      shutil.copy2(source, target)
87
88  def copy_runtime(target_dir, source_dir, dll_pattern):
89    """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
90    exist, but the target directory does exist."""
91    for which in ('p', 'r'):
92      dll = dll_pattern % which
93      target = os.path.join(target_dir, dll)
94      source = os.path.join(source_dir, dll)
95      copy_runtime_impl(target, source)
96
97  x86, x64 = runtime_dirs
98  out_debug = os.path.join(output_dir, 'Debug')
99  out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
100  out_release = os.path.join(output_dir, 'Release')
101  out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
102  out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
103  out_release_x64 = os.path.join(output_dir, 'Release_x64')
104
105  if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
106    os.makedirs(out_debug_nacl64)
107  if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
108    os.makedirs(out_release_nacl64)
109  copy_runtime(out_debug,          x86, 'msvc%s120d.dll')
110  copy_runtime(out_release,        x86, 'msvc%s120.dll')
111  copy_runtime(out_debug_x64,      x64, 'msvc%s120d.dll')
112  copy_runtime(out_release_x64,    x64, 'msvc%s120.dll')
113  copy_runtime(out_debug_nacl64,   x64, 'msvc%s120d.dll')
114  copy_runtime(out_release_nacl64, x64, 'msvc%s120.dll')
115
116  # Copy the PGO runtime library to the release directories.
117  if os.environ.get('GYP_MSVS_OVERRIDE_PATH'):
118    pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'),
119                                       'VC', 'bin')
120    pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64')
121    pgo_runtime_dll = 'pgort120.dll'
122    source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll)
123    if os.path.exists(source_x86):
124      copy_runtime_impl(os.path.join(out_release, pgo_runtime_dll), source_x86)
125    source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll)
126    if os.path.exists(source_x64):
127      copy_runtime_impl(os.path.join(out_release_x64, pgo_runtime_dll),
128                        source_x64)
129
130
131def _GetDesiredVsToolchainHashes():
132  """Load a list of SHA1s corresponding to the toolchains that we want installed
133  to build with."""
134  sha1path = os.path.join(script_dir,
135                          '..', 'buildtools', 'toolchain_vs2013.hash')
136  with open(sha1path, 'rb') as f:
137    return f.read().strip().splitlines()
138
139
140def Update():
141  """Requests an update of the toolchain to the specific hashes we have at
142  this revision. The update outputs a .json of the various configuration
143  information required to pass to gyp which we use in |GetToolchainDir()|.
144  """
145  depot_tools_win_toolchain = \
146      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
147  if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
148    import find_depot_tools
149    depot_tools_path = find_depot_tools.add_depot_tools_to_path()
150    json_data_file = os.path.join(script_dir, 'win_toolchain.json')
151    get_toolchain_args = [
152        sys.executable,
153        os.path.join(depot_tools_path,
154                    'win_toolchain',
155                    'get_toolchain_if_necessary.py'),
156        '--output-json', json_data_file,
157      ] + _GetDesiredVsToolchainHashes()
158    subprocess.check_call(get_toolchain_args)
159
160  return 0
161
162
163def GetToolchainDir():
164  """Gets location information about the current toolchain (must have been
165  previously updated by 'update'). This is used for the GN build."""
166  SetEnvironmentAndGetRuntimeDllDirs()
167  print '''vs_path = "%s"
168sdk_path = "%s"
169vs_version = "%s"
170wdk_dir = "%s"
171''' % (
172      os.environ['GYP_MSVS_OVERRIDE_PATH'],
173      os.environ['WINDOWSSDKDIR'],
174      os.environ['GYP_MSVS_VERSION'],
175      os.environ.get('WDK_DIR', ''))
176
177
178def main():
179  if not sys.platform.startswith(('win32', 'cygwin')):
180    return 0
181  commands = {
182      'update': Update,
183      'get_toolchain_dir': GetToolchainDir,
184      # TODO(scottmg): Add copy_dlls for GN builds (gyp_chromium calls
185      # CopyVsRuntimeDlls via import, currently).
186  }
187  if len(sys.argv) < 2 or sys.argv[1] not in commands:
188    print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
189    return 1
190  return commands[sys.argv[1]]()
191
192
193if __name__ == '__main__':
194  sys.exit(main())
195