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.
5 """Utility functions shared amongst the Windows generators."""
11 # A dictionary mapping supported target types to extensions.
14 'loadable_module': 'dll',
15 'shared_library': 'dll',
16 'static_library': 'lib',
20 def _GetLargePdbShimCcPath():
21 """Returns the path of the large_pdb_shim.cc file."""
22 this_dir = os.path.abspath(os.path.dirname(__file__))
23 src_dir = os.path.abspath(os.path.join(this_dir, '..', '..'))
24 win_data_dir = os.path.join(src_dir, 'data', 'win')
25 large_pdb_shim_cc = os.path.join(win_data_dir, 'large-pdb-shim.cc')
26 return large_pdb_shim_cc
29 def _DeepCopySomeKeys(in_dict, keys):
30 """Performs a partial deep-copy on |in_dict|, only copying the keys in |keys|.
33 in_dict: The dictionary to copy.
34 keys: The keys to be copied. If a key is in this list and doesn't exist in
35 |in_dict| this is not an error.
37 The partially deep-copied dictionary.
41 if key not in in_dict:
43 d[key] = copy.deepcopy(in_dict[key])
47 def _SuffixName(name, suffix):
48 """Add a suffix to the end of a target.
51 name: name of the target (foo#target)
52 suffix: the suffix to be added
54 Target name with suffix added (foo_suffix#target)
56 parts = name.rsplit('#', 1)
57 parts[0] = '%s_%s' % (parts[0], suffix)
58 return '#'.join(parts)
61 def _ShardName(name, number):
62 """Add a shard number to the end of a target.
65 name: name of the target (foo#target)
68 Target name with shard added (foo_1#target)
70 return _SuffixName(name, str(number))
73 def ShardTargets(target_list, target_dicts):
74 """Shard some targets apart to work around the linkers limits.
77 target_list: List of target pairs: 'base/base.gyp:base'.
78 target_dicts: Dict of target properties keyed on target pair.
80 Tuple of the new sharded versions of the inputs.
82 # Gather the targets to shard, and how many pieces.
84 for t in target_dicts:
85 shards = int(target_dicts[t].get('msvs_shard', 0))
87 targets_to_shard[t] = shards
91 if t in targets_to_shard:
92 for i in range(targets_to_shard[t]):
93 new_target_list.append(_ShardName(t, i))
95 new_target_list.append(t)
98 for t in target_dicts:
99 if t in targets_to_shard:
100 for i in range(targets_to_shard[t]):
101 name = _ShardName(t, i)
102 new_target_dicts[name] = copy.copy(target_dicts[t])
103 new_target_dicts[name]['target_name'] = _ShardName(
104 new_target_dicts[name]['target_name'], i)
105 sources = new_target_dicts[name].get('sources', [])
107 for pos in range(i, len(sources), targets_to_shard[t]):
108 new_sources.append(sources[pos])
109 new_target_dicts[name]['sources'] = new_sources
111 new_target_dicts[t] = target_dicts[t]
112 # Shard dependencies.
113 for t in new_target_dicts:
114 for deptype in ('dependencies', 'dependencies_original'):
115 dependencies = copy.copy(new_target_dicts[t].get(deptype, []))
116 new_dependencies = []
117 for d in dependencies:
118 if d in targets_to_shard:
119 for i in range(targets_to_shard[d]):
120 new_dependencies.append(_ShardName(d, i))
122 new_dependencies.append(d)
123 new_target_dicts[t][deptype] = new_dependencies
125 return (new_target_list, new_target_dicts)
128 def _GetPdbPath(target_dict, config_name, vars):
129 """Returns the path to the PDB file that will be generated by a given
132 The lookup proceeds as follows:
133 - Look for an explicit path in the VCLinkerTool configuration block.
134 - Look for an 'msvs_large_pdb_path' variable.
135 - Use '<(PRODUCT_DIR)/<(product_name).(exe|dll).pdb' if 'product_name' is
137 - Use '<(PRODUCT_DIR)/<(target_name).(exe|dll).pdb'.
140 target_dict: The target dictionary to be searched.
141 config_name: The name of the configuration of interest.
142 vars: A dictionary of common GYP variables with generator-specific values.
144 The path of the corresponding PDB file.
146 config = target_dict['configurations'][config_name]
147 msvs = config.setdefault('msvs_settings', {})
149 linker = msvs.get('VCLinkerTool', {})
151 pdb_path = linker.get('ProgramDatabaseFile')
155 variables = target_dict.get('variables', {})
156 pdb_path = variables.get('msvs_large_pdb_path', None)
161 pdb_base = target_dict.get('product_name', target_dict['target_name'])
162 pdb_base = '%s.%s.pdb' % (pdb_base, TARGET_TYPE_EXT[target_dict['type']])
163 pdb_path = vars['PRODUCT_DIR'] + '/' + pdb_base
168 def InsertLargePdbShims(target_list, target_dicts, vars):
169 """Insert a shim target that forces the linker to use 4KB pagesize PDBs.
171 This is a workaround for targets with PDBs greater than 1GB in size, the
172 limit for the 1KB pagesize PDBs created by the linker by default.
175 target_list: List of target pairs: 'base/base.gyp:base'.
176 target_dicts: Dict of target properties keyed on target pair.
177 vars: A dictionary of common GYP variables with generator-specific values.
179 Tuple of the shimmed version of the inputs.
181 # Determine which targets need shimming.
183 for t in target_dicts:
184 target_dict = target_dicts[t]
186 # We only want to shim targets that have msvs_large_pdb enabled.
187 if not int(target_dict.get('msvs_large_pdb', 0)):
189 # This is intended for executable, shared_library and loadable_module
190 # targets where every configuration is set up to produce a PDB output.
191 # If any of these conditions is not true then the shim logic will fail
193 targets_to_shim.append(t)
195 large_pdb_shim_cc = _GetLargePdbShimCcPath()
197 for t in targets_to_shim:
198 target_dict = target_dicts[t]
199 target_name = target_dict.get('target_name')
201 base_dict = _DeepCopySomeKeys(target_dict,
202 ['configurations', 'default_configuration', 'toolset'])
204 # This is the dict for copying the source file (part of the GYP tree)
205 # to the intermediate directory of the project. This is necessary because
206 # we can't always build a relative path to the shim source file (on Windows
207 # GYP and the project may be on different drives), and Ninja hates absolute
208 # paths (it ends up generating the .obj and .obj.d alongside the source
209 # file, polluting GYPs tree).
210 copy_suffix = 'large_pdb_copy'
211 copy_target_name = target_name + '_' + copy_suffix
212 full_copy_target_name = _SuffixName(t, copy_suffix)
213 shim_cc_basename = os.path.basename(large_pdb_shim_cc)
214 shim_cc_dir = vars['SHARED_INTERMEDIATE_DIR'] + '/' + copy_target_name
215 shim_cc_path = shim_cc_dir + '/' + shim_cc_basename
216 copy_dict = copy.deepcopy(base_dict)
217 copy_dict['target_name'] = copy_target_name
218 copy_dict['type'] = 'none'
219 copy_dict['sources'] = [ large_pdb_shim_cc ]
220 copy_dict['copies'] = [{
221 'destination': shim_cc_dir,
222 'files': [ large_pdb_shim_cc ]
225 # This is the dict for the PDB generating shim target. It depends on the
227 shim_suffix = 'large_pdb_shim'
228 shim_target_name = target_name + '_' + shim_suffix
229 full_shim_target_name = _SuffixName(t, shim_suffix)
230 shim_dict = copy.deepcopy(base_dict)
231 shim_dict['target_name'] = shim_target_name
232 shim_dict['type'] = 'static_library'
233 shim_dict['sources'] = [ shim_cc_path ]
234 shim_dict['dependencies'] = [ full_copy_target_name ]
236 # Set up the shim to output its PDB to the same location as the final linker
238 for config_name, config in shim_dict.get('configurations').iteritems():
239 pdb_path = _GetPdbPath(target_dict, config_name, vars)
241 # A few keys that we don't want to propagate.
242 for key in ['msvs_precompiled_header', 'msvs_precompiled_source', 'test']:
243 config.pop(key, None)
245 msvs = config.setdefault('msvs_settings', {})
247 # Update the compiler directives in the shim target.
248 compiler = msvs.setdefault('VCCLCompilerTool', {})
249 compiler['DebugInformationFormat'] = '3'
250 compiler['ProgramDataBaseFileName'] = pdb_path
252 # Set the explicit PDB path in the appropriate configuration of the
254 config = target_dict['configurations'][config_name]
255 msvs = config.setdefault('msvs_settings', {})
256 linker = msvs.setdefault('VCLinkerTool', {})
257 linker['GenerateDebugInformation'] = 'true'
258 linker['ProgramDatabaseFile'] = pdb_path
260 # Add the new targets. They must go to the beginning of the list so that
261 # the dependency generation works as expected in ninja.
262 target_list.insert(0, full_copy_target_name)
263 target_list.insert(0, full_shim_target_name)
264 target_dicts[full_copy_target_name] = copy_dict
265 target_dicts[full_shim_target_name] = shim_dict
267 # Update the original target to depend on the shim target.
268 target_dict.setdefault('dependencies', []).append(full_shim_target_name)
270 return (target_list, target_dicts)