MSVSUtil.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. # Copyright (c) 2013 Google Inc. 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. """Utility functions shared amongst the Windows generators."""
  5. import copy
  6. import os
  7. # A dictionary mapping supported target types to extensions.
  8. TARGET_TYPE_EXT = {
  9. 'executable': 'exe',
  10. 'loadable_module': 'dll',
  11. 'shared_library': 'dll',
  12. 'static_library': 'lib',
  13. }
  14. def _GetLargePdbShimCcPath():
  15. """Returns the path of the large_pdb_shim.cc file."""
  16. this_dir = os.path.abspath(os.path.dirname(__file__))
  17. src_dir = os.path.abspath(os.path.join(this_dir, '..', '..'))
  18. win_data_dir = os.path.join(src_dir, 'data', 'win')
  19. large_pdb_shim_cc = os.path.join(win_data_dir, 'large-pdb-shim.cc')
  20. return large_pdb_shim_cc
  21. def _DeepCopySomeKeys(in_dict, keys):
  22. """Performs a partial deep-copy on |in_dict|, only copying the keys in |keys|.
  23. Arguments:
  24. in_dict: The dictionary to copy.
  25. keys: The keys to be copied. If a key is in this list and doesn't exist in
  26. |in_dict| this is not an error.
  27. Returns:
  28. The partially deep-copied dictionary.
  29. """
  30. d = {}
  31. for key in keys:
  32. if key not in in_dict:
  33. continue
  34. d[key] = copy.deepcopy(in_dict[key])
  35. return d
  36. def _SuffixName(name, suffix):
  37. """Add a suffix to the end of a target.
  38. Arguments:
  39. name: name of the target (foo#target)
  40. suffix: the suffix to be added
  41. Returns:
  42. Target name with suffix added (foo_suffix#target)
  43. """
  44. parts = name.rsplit('#', 1)
  45. parts[0] = '%s_%s' % (parts[0], suffix)
  46. return '#'.join(parts)
  47. def _ShardName(name, number):
  48. """Add a shard number to the end of a target.
  49. Arguments:
  50. name: name of the target (foo#target)
  51. number: shard number
  52. Returns:
  53. Target name with shard added (foo_1#target)
  54. """
  55. return _SuffixName(name, str(number))
  56. def ShardTargets(target_list, target_dicts):
  57. """Shard some targets apart to work around the linkers limits.
  58. Arguments:
  59. target_list: List of target pairs: 'base/base.gyp:base'.
  60. target_dicts: Dict of target properties keyed on target pair.
  61. Returns:
  62. Tuple of the new sharded versions of the inputs.
  63. """
  64. # Gather the targets to shard, and how many pieces.
  65. targets_to_shard = {}
  66. for t in target_dicts:
  67. shards = int(target_dicts[t].get('msvs_shard', 0))
  68. if shards:
  69. targets_to_shard[t] = shards
  70. # Shard target_list.
  71. new_target_list = []
  72. for t in target_list:
  73. if t in targets_to_shard:
  74. for i in range(targets_to_shard[t]):
  75. new_target_list.append(_ShardName(t, i))
  76. else:
  77. new_target_list.append(t)
  78. # Shard target_dict.
  79. new_target_dicts = {}
  80. for t in target_dicts:
  81. if t in targets_to_shard:
  82. for i in range(targets_to_shard[t]):
  83. name = _ShardName(t, i)
  84. new_target_dicts[name] = copy.copy(target_dicts[t])
  85. new_target_dicts[name]['target_name'] = _ShardName(
  86. new_target_dicts[name]['target_name'], i)
  87. sources = new_target_dicts[name].get('sources', [])
  88. new_sources = []
  89. for pos in range(i, len(sources), targets_to_shard[t]):
  90. new_sources.append(sources[pos])
  91. new_target_dicts[name]['sources'] = new_sources
  92. else:
  93. new_target_dicts[t] = target_dicts[t]
  94. # Shard dependencies.
  95. for t in new_target_dicts:
  96. for deptype in ('dependencies', 'dependencies_original'):
  97. dependencies = copy.copy(new_target_dicts[t].get(deptype, []))
  98. new_dependencies = []
  99. for d in dependencies:
  100. if d in targets_to_shard:
  101. for i in range(targets_to_shard[d]):
  102. new_dependencies.append(_ShardName(d, i))
  103. else:
  104. new_dependencies.append(d)
  105. new_target_dicts[t][deptype] = new_dependencies
  106. return (new_target_list, new_target_dicts)
  107. def _GetPdbPath(target_dict, config_name, vars):
  108. """Returns the path to the PDB file that will be generated by a given
  109. configuration.
  110. The lookup proceeds as follows:
  111. - Look for an explicit path in the VCLinkerTool configuration block.
  112. - Look for an 'msvs_large_pdb_path' variable.
  113. - Use '<(PRODUCT_DIR)/<(product_name).(exe|dll).pdb' if 'product_name' is
  114. specified.
  115. - Use '<(PRODUCT_DIR)/<(target_name).(exe|dll).pdb'.
  116. Arguments:
  117. target_dict: The target dictionary to be searched.
  118. config_name: The name of the configuration of interest.
  119. vars: A dictionary of common GYP variables with generator-specific values.
  120. Returns:
  121. The path of the corresponding PDB file.
  122. """
  123. config = target_dict['configurations'][config_name]
  124. msvs = config.setdefault('msvs_settings', {})
  125. linker = msvs.get('VCLinkerTool', {})
  126. pdb_path = linker.get('ProgramDatabaseFile')
  127. if pdb_path:
  128. return pdb_path
  129. variables = target_dict.get('variables', {})
  130. pdb_path = variables.get('msvs_large_pdb_path', None)
  131. if pdb_path:
  132. return pdb_path
  133. pdb_base = target_dict.get('product_name', target_dict['target_name'])
  134. pdb_base = '%s.%s.pdb' % (pdb_base, TARGET_TYPE_EXT[target_dict['type']])
  135. pdb_path = vars['PRODUCT_DIR'] + '/' + pdb_base
  136. return pdb_path
  137. def InsertLargePdbShims(target_list, target_dicts, vars):
  138. """Insert a shim target that forces the linker to use 4KB pagesize PDBs.
  139. This is a workaround for targets with PDBs greater than 1GB in size, the
  140. limit for the 1KB pagesize PDBs created by the linker by default.
  141. Arguments:
  142. target_list: List of target pairs: 'base/base.gyp:base'.
  143. target_dicts: Dict of target properties keyed on target pair.
  144. vars: A dictionary of common GYP variables with generator-specific values.
  145. Returns:
  146. Tuple of the shimmed version of the inputs.
  147. """
  148. # Determine which targets need shimming.
  149. targets_to_shim = []
  150. for t in target_dicts:
  151. target_dict = target_dicts[t]
  152. # We only want to shim targets that have msvs_large_pdb enabled.
  153. if not int(target_dict.get('msvs_large_pdb', 0)):
  154. continue
  155. # This is intended for executable, shared_library and loadable_module
  156. # targets where every configuration is set up to produce a PDB output.
  157. # If any of these conditions is not true then the shim logic will fail
  158. # below.
  159. targets_to_shim.append(t)
  160. large_pdb_shim_cc = _GetLargePdbShimCcPath()
  161. for t in targets_to_shim:
  162. target_dict = target_dicts[t]
  163. target_name = target_dict.get('target_name')
  164. base_dict = _DeepCopySomeKeys(target_dict,
  165. ['configurations', 'default_configuration', 'toolset'])
  166. # This is the dict for copying the source file (part of the GYP tree)
  167. # to the intermediate directory of the project. This is necessary because
  168. # we can't always build a relative path to the shim source file (on Windows
  169. # GYP and the project may be on different drives), and Ninja hates absolute
  170. # paths (it ends up generating the .obj and .obj.d alongside the source
  171. # file, polluting GYPs tree).
  172. copy_suffix = 'large_pdb_copy'
  173. copy_target_name = target_name + '_' + copy_suffix
  174. full_copy_target_name = _SuffixName(t, copy_suffix)
  175. shim_cc_basename = os.path.basename(large_pdb_shim_cc)
  176. shim_cc_dir = vars['SHARED_INTERMEDIATE_DIR'] + '/' + copy_target_name
  177. shim_cc_path = shim_cc_dir + '/' + shim_cc_basename
  178. copy_dict = copy.deepcopy(base_dict)
  179. copy_dict['target_name'] = copy_target_name
  180. copy_dict['type'] = 'none'
  181. copy_dict['sources'] = [ large_pdb_shim_cc ]
  182. copy_dict['copies'] = [{
  183. 'destination': shim_cc_dir,
  184. 'files': [ large_pdb_shim_cc ]
  185. }]
  186. # This is the dict for the PDB generating shim target. It depends on the
  187. # copy target.
  188. shim_suffix = 'large_pdb_shim'
  189. shim_target_name = target_name + '_' + shim_suffix
  190. full_shim_target_name = _SuffixName(t, shim_suffix)
  191. shim_dict = copy.deepcopy(base_dict)
  192. shim_dict['target_name'] = shim_target_name
  193. shim_dict['type'] = 'static_library'
  194. shim_dict['sources'] = [ shim_cc_path ]
  195. shim_dict['dependencies'] = [ full_copy_target_name ]
  196. # Set up the shim to output its PDB to the same location as the final linker
  197. # target.
  198. for config_name, config in shim_dict.get('configurations').iteritems():
  199. pdb_path = _GetPdbPath(target_dict, config_name, vars)
  200. # A few keys that we don't want to propagate.
  201. for key in ['msvs_precompiled_header', 'msvs_precompiled_source', 'test']:
  202. config.pop(key, None)
  203. msvs = config.setdefault('msvs_settings', {})
  204. # Update the compiler directives in the shim target.
  205. compiler = msvs.setdefault('VCCLCompilerTool', {})
  206. compiler['DebugInformationFormat'] = '3'
  207. compiler['ProgramDataBaseFileName'] = pdb_path
  208. # Set the explicit PDB path in the appropriate configuration of the
  209. # original target.
  210. config = target_dict['configurations'][config_name]
  211. msvs = config.setdefault('msvs_settings', {})
  212. linker = msvs.setdefault('VCLinkerTool', {})
  213. linker['GenerateDebugInformation'] = 'true'
  214. linker['ProgramDatabaseFile'] = pdb_path
  215. # Add the new targets. They must go to the beginning of the list so that
  216. # the dependency generation works as expected in ninja.
  217. target_list.insert(0, full_copy_target_name)
  218. target_list.insert(0, full_shim_target_name)
  219. target_dicts[full_copy_target_name] = copy_dict
  220. target_dicts[full_shim_target_name] = shim_dict
  221. # Update the original target to depend on the shim target.
  222. target_dict.setdefault('dependencies', []).append(full_shim_target_name)
  223. return (target_list, target_dicts)