Home | History | Annotate | Download | only in toolchain
      1 # Copyright 2013 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 
      5 # Extracts a Windows VS2013 toolchain from various downloadable pieces.
      6 
      7 
      8 from toolchain import *
      9 
     10 
     11 def GetIsoUrl(pro):
     12   """Get the .iso path."""
     13   prefix = 'http://download.microsoft.com/download/'
     14   if pro:
     15     return (prefix +
     16         'A/F/1/AF128362-A6A8-4DB3-A39A-C348086472CC/VS2013_RTM_PRO_ENU.iso')
     17   else:
     18     return (prefix +
     19         '7/2/E/72E0F986-D247-4289-B9DC-C4FB07374894/VS2013_RTM_DskExp_ENU.iso')
     20 
     21 
     22 def DownloadMainIso(url):
     23   temp_dir = TempDir()
     24   target_path = os.path.join(temp_dir, os.path.basename(url))
     25   Download(url, target_path)
     26   return target_path
     27 
     28 
     29 def GetSourceImage(local_dir, pro):
     30   url = GetIsoUrl(pro)
     31   if local_dir:
     32     return os.path.join(local_dir, os.path.basename(url))
     33   else:
     34     return DownloadMainIso(url)
     35 
     36 
     37 def ExtractMsiList(iso_dir, packages):
     38   results = []
     39   for (package, skippable) in packages:
     40     path_to_package = os.path.join(iso_dir, 'packages', package)
     41     if not os.path.exists(path_to_package) and skippable:
     42       sys.stdout.write('Pro-only %s skipped.\n' % package)
     43       continue
     44     results.append(ExtractMsi(path_to_package))
     45   return results
     46 
     47 
     48 def ExtractComponents(image):
     49   extracted_iso = ExtractIso(image)
     50   results = ExtractMsiList(extracted_iso, [
     51       (r'vcRuntimeAdditional_amd64\vc_runtimeAdditional_x64.msi', False),
     52       (r'vcRuntimeAdditional_x86\vc_runtimeAdditional_x86.msi', False),
     53       (r'vcRuntimeDebug_amd64\vc_runtimeDebug_x64.msi', False),
     54       (r'vcRuntimeDebug_x86\vc_runtimeDebug_x86.msi', False),
     55       (r'vcRuntimeMinimum_amd64\vc_runtimeMinimum_x64.msi', False),
     56       (r'vcRuntimeMinimum_x86\vc_runtimeMinimum_x86.msi', False),
     57       (r'vc_compilerCore86\vc_compilerCore86.msi', False),
     58       (r'vc_compilerCore86res\vc_compilerCore86res.msi', False),
     59       (r'vc_compilerx64nat\vc_compilerx64nat.msi', True),
     60       (r'vc_compilerx64natres\vc_compilerx64natres.msi', True),
     61       (r'vc_compilerx64x86\vc_compilerx64x86.msi', True),
     62       (r'vc_compilerx64x86res\vc_compilerx64x86res.msi', True),
     63       (r'vc_librarycore86\vc_librarycore86.msi', False),
     64       (r'vc_libraryDesktop\x64\vc_LibraryDesktopX64.msi', False),
     65       (r'vc_libraryDesktop\x86\vc_LibraryDesktopX86.msi', False),
     66       (r'vc_libraryextended\vc_libraryextended.msi', True),
     67       (r'Windows_SDK\Windows Software Development Kit-x86_en-us.msi', False),
     68       ('Windows_SDK\\'
     69        r'Windows Software Development Kit for Metro style Apps-x86_en-us.msi',
     70           False),
     71     ])
     72   return results
     73 
     74 
     75 def CopyToFinalLocation(extracted_dirs, target_dir):
     76   sys.stdout.write('Copying to final location...\n')
     77   mappings = {
     78       'Program Files\\Microsoft Visual Studio 12.0\\': '.\\',
     79       'Windows Kits\\8.0\\': 'win8sdk\\',
     80       'System64\\': 'sys64\\',
     81       'System\\': 'sys32\\',
     82   }
     83   matches = []
     84   for extracted_dir in extracted_dirs:
     85     for root, dirnames, filenames in os.walk(extracted_dir):
     86       for filename in filenames:
     87         matches.append((extracted_dir, os.path.join(root, filename)))
     88 
     89   copies = []
     90   for prefix, full_path in matches:
     91     partial_path = full_path[len(prefix) + 1:]  # +1 for trailing \
     92     #print 'partial_path', partial_path
     93     for map_from, map_to in mappings.iteritems():
     94       #print 'map_from:', map_from, ', map_to:', map_to
     95       if partial_path.startswith(map_from):
     96         target_path = os.path.join(map_to, partial_path[len(map_from):])
     97         copies.append((full_path, os.path.join(target_dir, target_path)))
     98 
     99   for full_source, full_target in copies:
    100     target_dir = os.path.dirname(full_target)
    101     if not os.path.isdir(target_dir):
    102       os.makedirs(target_dir)
    103     shutil.copy2(full_source, full_target)
    104 
    105 
    106 def GenerateSetEnvCmd(target_dir, pro):
    107   """Generate a batch file that gyp expects to exist to set up the compiler
    108   environment. This is normally generated by a full install of the SDK, but we
    109   do it here manually since we do not do a full install."""
    110   with open(os.path.join(
    111         target_dir, r'win8sdk\bin\SetEnv.cmd'), 'w') as file:
    112     file.write('@echo off\n')
    113     file.write(':: Generated by tools\\win\\toolchain\\toolchain2013.py.\n')
    114     # Common to x86 and x64
    115     file.write('set PATH=%~dp0..\\..\\Common7\\IDE;%PATH%\n')
    116     file.write('set INCLUDE=%~dp0..\\..\\win8sdk\\Include\\um;'
    117                '%~dp0..\\..\\win8sdk\\Include\\shared;'
    118                '%~dp0..\\..\\VC\\include;'
    119                '%~dp0..\\..\\VC\\atlmfc\\include\n')
    120     file.write('if "%1"=="/x64" goto x64\n')
    121 
    122     # x86. If we're Pro, then use the amd64_x86 cross (we don't support x86
    123     # host at all).
    124     if pro:
    125       file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x86;'
    126                 '%~dp0..\\..\\VC\\bin\\amd64_x86;'
    127                 '%~dp0..\\..\\VC\\bin\\amd64;'  # Needed for mspdb120.dll.
    128                 '%PATH%\n')
    129     else:
    130       file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x86;'
    131                 '%~dp0..\\..\\VC\\bin;%PATH%\n')
    132     file.write('set LIB=%~dp0..\\..\\VC\\lib;'
    133                '%~dp0..\\..\\win8sdk\\Lib\\win8\\um\\x86;'
    134                '%~dp0..\\..\\VC\\atlmfc\\lib\n')
    135     file.write('goto done\n')
    136 
    137     # Express does not include a native 64 bit compiler, so we have to use
    138     # the x86->x64 cross.
    139     if not pro:
    140       # x86->x64 cross.
    141       file.write(':x64\n')
    142       file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x64;'
    143                  '%~dp0..\\..\\VC\\bin\\x86_amd64;'
    144                  '%PATH%\n')
    145     else:
    146       # x64 native.
    147       file.write(':x64\n')
    148       file.write('set PATH=%~dp0..\\..\\win8sdk\\bin\\x64;'
    149                  '%~dp0..\\..\\VC\\bin\\amd64;'
    150                  '%PATH%\n')
    151     file.write('set LIB=%~dp0..\\..\\VC\\lib\\amd64;'
    152                '%~dp0..\\..\\win8sdk\\Lib\\win8\\um\\x64;'
    153                '%~dp0..\\..\\VC\\atlmfc\\lib\\amd64\n')
    154     file.write(':done\n')
    155 
    156 
    157 def GenerateTopLevelEnv(target_dir, pro):
    158   """Generate a batch file that sets up various environment variables that let
    159   the Chromium build files and gyp find SDKs and tools."""
    160   with open(os.path.join(target_dir, r'env.bat'), 'w') as file:
    161     file.write('@echo off\n')
    162     file.write(':: Generated by tools\\win\\toolchain\\toolchain2013.py.\n')
    163     file.write('set GYP_DEFINES=windows_sdk_path="%~dp0win8sdk" '
    164                'component=shared_library\n')
    165     file.write('set GYP_MSVS_VERSION=2013%s\n' % '' if pro else 'e')
    166     file.write('set GYP_MSVS_OVERRIDE_PATH=%~dp0\n')
    167     file.write('set GYP_GENERATORS=ninja\n')
    168     file.write('set WindowsSDKDir=%~dp0win8sdk\n')
    169     paths = [
    170         r'Debug_NonRedist\x64\Microsoft.VC120.DebugCRT',
    171         r'Debug_NonRedist\x86\Microsoft.VC120.DebugCRT',
    172         r'x64\Microsoft.VC120.CRT',
    173         r'x86\Microsoft.VC120.CRT',
    174       ]
    175     additions = ';'.join(('%~dp0' + x) for x in paths)
    176     file.write('set PATH=%s;%%PATH%%\n' % additions)
    177     file.write('echo Environment set for toolchain in %~dp0.\n')
    178     file.write('cd /d %~dp0..\n')
    179 
    180 
    181 def main():
    182   parser = OptionParser()
    183   parser.add_option('--targetdir', metavar='DIR',
    184                     help='put toolchain into DIR',
    185                     default=os.path.abspath('win_toolchain_2013'))
    186   parser.add_option('--noclean', action='store_false', dest='clean',
    187                     help='do not remove temp files',
    188                     default=True)
    189   parser.add_option('--local', metavar='DIR',
    190                     help='use downloaded files from DIR')
    191   parser.add_option('--express', metavar='EXPRESS',
    192                     help='use VS Express instead of Pro', action='store_true')
    193   options, args = parser.parse_args()
    194   try:
    195     target_dir = os.path.abspath(options.targetdir)
    196     if os.path.exists(target_dir):
    197       sys.stderr.write('%s already exists. Please [re]move it or use '
    198                        '--targetdir to select a different target.\n' %
    199                        target_dir)
    200       return 1
    201     pro = not options.express
    202     # Set the working directory to 7z subdirectory. 7-zip doesn't find its
    203     # codec dll very well, so this is the simplest way to make sure it runs
    204     # correctly, as we don't otherwise care about working directory.
    205     os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '7z'))
    206     image = GetSourceImage(options.local, pro)
    207     extracted = ExtractComponents(image)
    208     CopyToFinalLocation(extracted, target_dir)
    209 
    210     GenerateSetEnvCmd(target_dir, pro)
    211     GenerateTopLevelEnv(target_dir, pro)
    212   finally:
    213     if options.clean:
    214       DeleteAllTempDirs()
    215 
    216   sys.stdout.write(
    217       '\nIn a (clean) cmd shell, you can now run\n\n'
    218       '  %s\\env.bat\n\n'
    219       'then\n\n'
    220       "  gclient runhooks (or gclient sync if you haven't pulled deps yet)\n"
    221       '  ninja -C out\Debug chrome\n\n'
    222       'Note that this script intentionally does not modify any global\n'
    223       'settings like the registry, or system environment variables, so you\n'
    224       'will need to run the above env.bat whenever you start a new\n'
    225       'shell.\n\n' % target_dir)
    226 
    227 
    228 if __name__ == '__main__':
    229   main()
    230