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