• 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    with open(json_data_file, 'r') as tempf:
34      toolchain_data = json.load(tempf)
35
36    toolchain = toolchain_data['path']
37    version = toolchain_data['version']
38    version_is_pro = version[-1] != 'e'
39    win8sdk = toolchain_data['win8sdk']
40    wdk = toolchain_data['wdk']
41    # TODO(scottmg): The order unfortunately matters in these. They should be
42    # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call
43    # below). http://crbug.com/345992
44    vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs']
45
46    os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
47    os.environ['GYP_MSVS_VERSION'] = version
48    # We need to make sure windows_sdk_path is set to the automated
49    # toolchain values in GYP_DEFINES, but don't want to override any
50    # otheroptions.express
51    # values there.
52    gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES'))
53    gyp_defines_dict['windows_sdk_path'] = win8sdk
54    os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v)))
55        for k, v in gyp_defines_dict.iteritems())
56    os.environ['WINDOWSSDKDIR'] = win8sdk
57    os.environ['WDK_DIR'] = wdk
58    # Include the VS runtime in the PATH in case it's not machine-installed.
59    runtime_path = ';'.join(vs2013_runtime_dll_dirs)
60    os.environ['PATH'] = runtime_path + ';' + os.environ['PATH']
61  return vs2013_runtime_dll_dirs
62
63
64def CopyVsRuntimeDlls(output_dir, runtime_dirs):
65  """Copies the VS runtime DLLs from the given |runtime_dirs| to the output
66  directory so that even if not system-installed, built binaries are likely to
67  be able to run.
68
69  This needs to be run after gyp has been run so that the expected target
70  output directories are already created.
71  """
72  assert sys.platform.startswith(('win32', 'cygwin'))
73
74  def copy_runtime(target_dir, source_dir, dll_pattern):
75    """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't
76    exist, but the target directory does exist."""
77    for which in ('p', 'r'):
78      dll = dll_pattern % which
79      target = os.path.join(target_dir, dll)
80      source = os.path.join(source_dir, dll)
81      # If gyp generated to that output dir, and the runtime isn't already
82      # there, then copy it over.
83      if (os.path.isdir(target_dir) and
84          (not os.path.isfile(target) or
85            os.stat(target).st_mtime != os.stat(source).st_mtime)):
86        print 'Copying %s to %s...' % (source, target)
87        if os.path.exists(target):
88          os.unlink(target)
89        shutil.copy2(source, target)
90
91  x86, x64 = runtime_dirs
92  out_debug = os.path.join(output_dir, 'Debug')
93  out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64')
94  out_release = os.path.join(output_dir, 'Release')
95  out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64')
96  out_debug_x64 = os.path.join(output_dir, 'Debug_x64')
97  out_release_x64 = os.path.join(output_dir, 'Release_x64')
98
99  if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64):
100    os.makedirs(out_debug_nacl64)
101  if os.path.exists(out_release) and not os.path.exists(out_release_nacl64):
102    os.makedirs(out_release_nacl64)
103  copy_runtime(out_debug,          x86, 'msvc%s120d.dll')
104  copy_runtime(out_release,        x86, 'msvc%s120.dll')
105  copy_runtime(out_debug_x64,      x64, 'msvc%s120d.dll')
106  copy_runtime(out_release_x64,    x64, 'msvc%s120.dll')
107  copy_runtime(out_debug_nacl64,   x64, 'msvc%s120d.dll')
108  copy_runtime(out_release_nacl64, x64, 'msvc%s120.dll')
109
110
111def _GetDesiredVsToolchainHashes():
112  """Load a list of SHA1s corresponding to the toolchains that we want installed
113  to build with."""
114  sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash')
115  with open(sha1path, 'rb') as f:
116    return f.read().strip().splitlines()
117
118
119def Update():
120  """Requests an update of the toolchain to the specific hashes we have at
121  this revision. The update outputs a .json of the various configuration
122  information required to pass to gyp which we use in |GetToolchainDir()|.
123  """
124  depot_tools_win_toolchain = \
125      bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
126  if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain:
127    import find_depot_tools
128    depot_tools_path = find_depot_tools.add_depot_tools_to_path()
129    json_data_file = os.path.join(script_dir, 'win_toolchain.json')
130    get_toolchain_args = [
131        sys.executable,
132        os.path.join(depot_tools_path,
133                    'win_toolchain',
134                    'get_toolchain_if_necessary.py'),
135        '--output-json', json_data_file,
136      ] + _GetDesiredVsToolchainHashes()
137    subprocess.check_call(get_toolchain_args)
138
139  return 0
140
141
142def GetToolchainDir():
143  """Gets location information about the current toolchain (must have been
144  previously updated by 'update'). This is used for the GN build."""
145  SetEnvironmentAndGetRuntimeDllDirs()
146  print '''vs_path = "%s"
147sdk_path = "%s"
148vs_version = "%s"
149wdk_dir = "%s"
150''' % (
151      os.environ['GYP_MSVS_OVERRIDE_PATH'],
152      os.environ['WINDOWSSDKDIR'],
153      os.environ['GYP_MSVS_VERSION'],
154      os.environ['WDK_DIR'])
155
156
157def main():
158  if not sys.platform.startswith(('win32', 'cygwin')):
159    return 0
160  commands = {
161      'update': Update,
162      'get_toolchain_dir': GetToolchainDir,
163      # TODO(scottmg): Add copy_dlls for GN builds (gyp_chromium calls
164      # CopyVsRuntimeDlls via import, currently).
165  }
166  if len(sys.argv) < 2 or sys.argv[1] not in commands:
167    print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands)
168    return 1
169  return commands[sys.argv[1]]()
170
171
172if __name__ == '__main__':
173  sys.exit(main())
174