renamed SCons.Tool.ninja -> SCons.Tool.ninja_tool and added alias in tool loading...
[scons.git] / SCons / Tool / mslink.py
blob74ceaa8572aeab4e160f8053a5fcd3d0d459ab6d
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """SCons.Tool.mslink
26 Tool-specific initialization for the Microsoft linker.
28 There normally shouldn't be any need to import this module directly.
29 It will usually be imported through the generic SCons.Tool.Tool()
30 selection method.
32 """
34 import os
35 import os.path
37 import SCons.Action
38 import SCons.Defaults
39 import SCons.Errors
40 import SCons.Platform.win32
41 import SCons.Tool
42 import SCons.Tool.msvc
43 import SCons.Tool.msvs
44 import SCons.Util
46 from SCons.Tool.MSCommon import (
47 msvc_setup_env_once,
48 msvc_setup_env_tool,
50 from SCons.Tool.MSCommon.common import get_pch_node
52 tool_name = 'mslink'
54 def pdbGenerator(env, target, source, for_signature):
55 try:
56 return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG']
57 except (AttributeError, IndexError):
58 return None
60 def _dllTargets(target, source, env, for_signature, paramtp):
61 listCmd = []
62 dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp)
63 if dll: listCmd.append("/out:%s"%dll.get_string(for_signature))
65 implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
66 if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature))
68 return listCmd
70 def _dllSources(target, source, env, for_signature, paramtp):
71 listCmd = []
73 deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")
74 for src in source:
75 # Check explicitly for a non-None deffile so that the __cmp__
76 # method of the base SCons.Util.Proxy class used for some Node
77 # proxies doesn't try to use a non-existent __dict__ attribute.
78 if deffile and src == deffile:
79 # Treat this source as a .def file.
80 listCmd.append("/def:%s" % src.get_string(for_signature))
81 else:
82 # Just treat it as a generic source file.
83 listCmd.append(src)
84 return listCmd
86 def windowsShlinkTargets(target, source, env, for_signature):
87 return _dllTargets(target, source, env, for_signature, 'SHLIB')
89 def windowsShlinkSources(target, source, env, for_signature):
90 return _dllSources(target, source, env, for_signature, 'SHLIB')
92 def _windowsLdmodTargets(target, source, env, for_signature):
93 """Get targets for loadable modules."""
94 return _dllTargets(target, source, env, for_signature, 'LDMODULE')
96 def _windowsLdmodSources(target, source, env, for_signature):
97 """Get sources for loadable modules."""
98 return _dllSources(target, source, env, for_signature, 'LDMODULE')
100 def _dllEmitter(target, source, env, paramtp):
101 """Common implementation of dll emitter."""
102 SCons.Tool.msvc.validate_vars(env)
104 extratargets = []
105 extrasources = []
107 dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp)
108 no_import_lib = env.get('no_import_lib', 0)
110 if not dll:
111 raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp))
113 insert_def = env.subst("$WINDOWS_INSERT_DEF")
114 if insert_def not in ['', '0', 0] and \
115 not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"):
117 # append a def file to the list of sources
118 extrasources.append(
119 env.ReplaceIxes(dll,
120 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
121 "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"))
123 version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
124 if version_num >= 8.0 and \
125 (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)):
126 # MSVC 8 and above automatically generate .manifest files that must be installed
127 extratargets.append(
128 env.ReplaceIxes(dll,
129 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
130 "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX"))
132 if 'PDB' in env and env['PDB']:
133 pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
134 extratargets.append(pdb)
135 target[0].attributes.pdb = pdb
137 pch_node = get_pch_node(env, target, source)
138 if version_num >= 11.0 and pch_node:
139 # MSVC 11 and above need the PCH object file to be added to the link line,
140 # otherwise you get link error LNK2011.
141 pchobj = SCons.Util.splitext(str(pch_node))[0] + '.obj'
142 # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj)
143 if pchobj not in extrasources:
144 extrasources.append(pchobj)
146 if not no_import_lib and \
147 not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"):
148 # Append an import library to the list of targets.
149 extratargets.append(
150 env.ReplaceIxes(dll,
151 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
152 "LIBPREFIX", "LIBSUFFIX"))
153 # and .exp file is created if there are exports from a DLL
154 extratargets.append(
155 env.ReplaceIxes(dll,
156 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
157 "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX"))
159 return (target+extratargets, source+extrasources)
161 def windowsLibEmitter(target, source, env):
162 return _dllEmitter(target, source, env, 'SHLIB')
164 def ldmodEmitter(target, source, env):
165 """Emitter for loadable modules.
167 Loadable modules are identical to shared libraries on Windows, but building
168 them is subject to different parameters (LDMODULE*).
170 return _dllEmitter(target, source, env, 'LDMODULE')
172 def prog_emitter(target, source, env):
173 SCons.Tool.msvc.validate_vars(env)
175 extratargets = []
176 extrasources = []
178 exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX")
179 if not exe:
180 raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX"))
182 version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
183 if version_num >= 8.0 and \
184 (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)):
185 # MSVC 8 and above automatically generate .manifest files that have to be installed
186 extratargets.append(
187 env.ReplaceIxes(exe,
188 "PROGPREFIX", "PROGSUFFIX",
189 "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX"))
191 if 'PDB' in env and env['PDB']:
192 pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
193 extratargets.append(pdb)
194 target[0].attributes.pdb = pdb
196 pch_node = get_pch_node(env, target, source)
197 if version_num >= 11.0 and pch_node:
198 # MSVC 11 and above need the PCH object file to be added to the link line,
199 # otherwise you get link error LNK2011.
200 pchobj = SCons.Util.splitext(str(pch_node))[0] + '.obj'
201 # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj)
202 if pchobj not in extrasources:
203 extrasources.append(pchobj)
205 return (target+extratargets,source+extrasources)
207 def RegServerFunc(target, source, env):
208 if 'register' in env and env['register']:
209 ret = regServerAction([target[0]], [source[0]], env)
210 if ret:
211 raise SCons.Errors.UserError("Unable to register %s" % target[0])
212 else:
213 print("Registered %s sucessfully" % target[0])
214 return ret
215 return 0
217 # These are the actual actions run to embed the manifest.
218 # They are only called from the Check versions below.
219 embedManifestExeAction = SCons.Action.Action('$MTEXECOM')
220 embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM')
222 def embedManifestDllCheck(target, source, env):
223 """Function run by embedManifestDllCheckAction to check for existence of manifest
224 and other conditions, and embed the manifest by calling embedManifestDllAction if so."""
225 if env.get('WINDOWS_EMBED_MANIFEST', 0):
226 manifestSrc = target[0].get_abspath() + '.manifest'
227 if os.path.exists(manifestSrc):
228 ret = embedManifestDllAction([target[0]], None, env)
229 if ret:
230 raise SCons.Errors.UserError("Unable to embed manifest into %s" % (target[0]))
231 return ret
232 else:
233 print('(embed: no %s.manifest found; not embedding.)'%str(target[0]))
234 return 0
236 def embedManifestExeCheck(target, source, env):
237 """Function run by embedManifestExeCheckAction to check for existence of manifest
238 and other conditions, and embed the manifest by calling embedManifestExeAction if so."""
239 if env.get('WINDOWS_EMBED_MANIFEST', 0):
240 manifestSrc = target[0].get_abspath() + '.manifest'
241 if os.path.exists(manifestSrc):
242 ret = embedManifestExeAction([target[0]], None, env)
243 if ret:
244 raise SCons.Errors.UserError("Unable to embed manifest into %s" % (target[0]))
245 return ret
246 else:
247 print('(embed: no %s.manifest found; not embedding.)'%str(target[0]))
248 return 0
250 embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None)
251 embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None)
253 regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR")
254 regServerCheck = SCons.Action.Action(RegServerFunc, None)
255 shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES", "$SHLINKCOMSTR")}', '$SHLINKCOMSTR')
256 compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction
257 ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES", "$LDMODULECOMSTR")}', '$LDMODULECOMSTR')
258 compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction
259 exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows", "$LINKCOMSTR")}', '$LINKCOMSTR')
260 compositeLinkAction = exeLinkAction + embedManifestExeCheckAction
262 def generate(env) -> None:
263 """Add Builders and construction variables for ar to an Environment."""
264 SCons.Tool.createSharedLibBuilder(env, shlib_suffix='$SHLIBSUFFIX')
265 SCons.Tool.createProgBuilder(env)
267 env['SHLINK'] = '$LINK'
268 env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll')
269 env['_SHLINK_TARGETS'] = windowsShlinkTargets
270 env['_SHLINK_SOURCES'] = windowsShlinkSources
271 env['SHLINKCOM'] = compositeShLinkAction
272 env.Append(SHLIBEMITTER=[windowsLibEmitter])
273 env.Append(LDMODULEEMITTER=[windowsLibEmitter])
274 env['LINK'] = 'link'
275 env['LINKFLAGS'] = SCons.Util.CLVar('/nologo')
276 env['_PDB'] = pdbGenerator
277 env['LINKCOM'] = compositeLinkAction
278 env.Append(PROGEMITTER=[prog_emitter])
279 env['LIBDIRPREFIX'] = '/LIBPATH:'
280 env['LIBDIRSUFFIX'] = ''
281 env['LIBLINKPREFIX'] = ''
282 env['LIBLINKSUFFIX'] = '$LIBSUFFIX'
284 env['WINDOWSDEFPREFIX'] = ''
285 env['WINDOWSDEFSUFFIX'] = '.def'
286 env['WINDOWSEXPPREFIX'] = ''
287 env['WINDOWSEXPSUFFIX'] = '.exp'
288 env['WINDOWS_INSERT_DEF'] = 0
290 env['WINDOWSSHLIBMANIFESTPREFIX'] = ''
291 env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest'
292 env['WINDOWSPROGMANIFESTPREFIX'] = ''
293 env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest'
295 env['REGSVRACTION'] = regServerCheck
296 env['REGSVR'] = os.path.join(
297 SCons.Platform.win32.get_system_root(), 'System32', 'regsvr32'
299 env['REGSVRFLAGS'] = '/s '
300 env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}'
302 env['WINDOWS_EMBED_MANIFEST'] = 0
303 env['MT'] = 'mt'
304 # env['MTFLAGS'] = ['-hashupdate']
305 env['MTFLAGS'] = SCons.Util.CLVar('/nologo')
306 # Note: use - here to prevent build failure if no manifest produced.
307 # This seems much simpler than a fancy system using a function action to see
308 # if the manifest actually exists before trying to run mt with it.
309 env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1'
310 env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2'
311 # TODO Future work garyo 27-Feb-11
312 env['_MANIFEST_SOURCES'] = None # _windowsManifestSources
314 # Set-up ms tools paths
315 msvc_setup_env_once(env, tool=tool_name)
317 # Loadable modules are on Windows the same as shared libraries, but they
318 # are subject to different build parameters (LDMODULE* variables).
319 # Therefore LDMODULE* variables correspond as much as possible to
320 # SHLINK*/SHLIB* ones.
321 SCons.Tool.createLoadableModuleBuilder(env, loadable_module_suffix='$LDMODULESUFFIX')
322 env['LDMODULE'] = '$SHLINK'
323 env['LDMODULEPREFIX'] = '$SHLIBPREFIX'
324 env['LDMODULESUFFIX'] = '$SHLIBSUFFIX'
325 env['LDMODULEFLAGS'] = '$SHLINKFLAGS'
326 env['_LDMODULE_TARGETS'] = _windowsLdmodTargets
327 env['_LDMODULE_SOURCES'] = _windowsLdmodSources
328 env['LDMODULEEMITTER'] = [ldmodEmitter]
329 env['LDMODULECOM'] = compositeLdmodAction
331 # Issue #3350
332 # Change tempfile argument joining character from a space to a newline
333 # mslink will fail if any single line is too long, but is fine with many lines
334 # in a tempfile
335 env['TEMPFILEARGJOIN'] = os.linesep
337 def exists(env):
338 return msvc_setup_env_tool(env, tool=tool_name)
340 # Local Variables:
341 # tab-width:4
342 # indent-tabs-mode:nil
343 # End:
344 # vim: set expandtab tabstop=4 shiftwidth=4: