Followon to PR #4348: more bool fixes
[scons.git] / SCons / Tool / mslink.py
blob1e5b71ae10ad97ac432c61b78889ce5a2e56be75
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 .MSCommon import msvc_setup_env_once, msvc_setup_env_tool
47 from .MSCommon.common import get_pch_node
49 tool_name = 'mslink'
51 def pdbGenerator(env, target, source, for_signature):
52 try:
53 return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG']
54 except (AttributeError, IndexError):
55 return None
57 def _dllTargets(target, source, env, for_signature, paramtp):
58 listCmd = []
59 dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp)
60 if dll: listCmd.append("/out:%s"%dll.get_string(for_signature))
62 implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
63 if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature))
65 return listCmd
67 def _dllSources(target, source, env, for_signature, paramtp):
68 listCmd = []
70 deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")
71 for src in source:
72 # Check explicitly for a non-None deffile so that the __cmp__
73 # method of the base SCons.Util.Proxy class used for some Node
74 # proxies doesn't try to use a non-existent __dict__ attribute.
75 if deffile and src == deffile:
76 # Treat this source as a .def file.
77 listCmd.append("/def:%s" % src.get_string(for_signature))
78 else:
79 # Just treat it as a generic source file.
80 listCmd.append(src)
81 return listCmd
83 def windowsShlinkTargets(target, source, env, for_signature):
84 return _dllTargets(target, source, env, for_signature, 'SHLIB')
86 def windowsShlinkSources(target, source, env, for_signature):
87 return _dllSources(target, source, env, for_signature, 'SHLIB')
89 def _windowsLdmodTargets(target, source, env, for_signature):
90 """Get targets for loadable modules."""
91 return _dllTargets(target, source, env, for_signature, 'LDMODULE')
93 def _windowsLdmodSources(target, source, env, for_signature):
94 """Get sources for loadable modules."""
95 return _dllSources(target, source, env, for_signature, 'LDMODULE')
97 def _dllEmitter(target, source, env, paramtp):
98 """Common implementation of dll emitter."""
99 SCons.Tool.msvc.validate_vars(env)
101 extratargets = []
102 extrasources = []
104 dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp)
105 no_import_lib = env.get('no_import_lib', 0)
107 if not dll:
108 raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp))
110 insert_def = env.subst("$WINDOWS_INSERT_DEF")
111 if insert_def not in ['', '0', 0] and \
112 not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"):
114 # append a def file to the list of sources
115 extrasources.append(
116 env.ReplaceIxes(dll,
117 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
118 "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"))
120 version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
121 if version_num >= 8.0 and \
122 (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)):
123 # MSVC 8 and above automatically generate .manifest files that must be installed
124 extratargets.append(
125 env.ReplaceIxes(dll,
126 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
127 "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX"))
129 if 'PDB' in env and env['PDB']:
130 pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
131 extratargets.append(pdb)
132 target[0].attributes.pdb = pdb
134 pch_node = get_pch_node(env, target, source)
135 if version_num >= 11.0 and pch_node:
136 # MSVC 11 and above need the PCH object file to be added to the link line,
137 # otherwise you get link error LNK2011.
138 pchobj = SCons.Util.splitext(str(pch_node))[0] + '.obj'
139 # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj)
140 if pchobj not in extrasources:
141 extrasources.append(pchobj)
143 if not no_import_lib and \
144 not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"):
145 # Append an import library to the list of targets.
146 extratargets.append(
147 env.ReplaceIxes(dll,
148 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
149 "LIBPREFIX", "LIBSUFFIX"))
150 # and .exp file is created if there are exports from a DLL
151 extratargets.append(
152 env.ReplaceIxes(dll,
153 '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp,
154 "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX"))
156 return (target+extratargets, source+extrasources)
158 def windowsLibEmitter(target, source, env):
159 return _dllEmitter(target, source, env, 'SHLIB')
161 def ldmodEmitter(target, source, env):
162 """Emitter for loadable modules.
164 Loadable modules are identical to shared libraries on Windows, but building
165 them is subject to different parameters (LDMODULE*).
167 return _dllEmitter(target, source, env, 'LDMODULE')
169 def prog_emitter(target, source, env):
170 SCons.Tool.msvc.validate_vars(env)
172 extratargets = []
173 extrasources = []
175 exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX")
176 if not exe:
177 raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX"))
179 version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0'))
180 if version_num >= 8.0 and \
181 (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)):
182 # MSVC 8 and above automatically generate .manifest files that have to be installed
183 extratargets.append(
184 env.ReplaceIxes(exe,
185 "PROGPREFIX", "PROGSUFFIX",
186 "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX"))
188 if 'PDB' in env and env['PDB']:
189 pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
190 extratargets.append(pdb)
191 target[0].attributes.pdb = pdb
193 pch_node = get_pch_node(env, target, source)
194 if version_num >= 11.0 and pch_node:
195 # MSVC 11 and above need the PCH object file to be added to the link line,
196 # otherwise you get link error LNK2011.
197 pchobj = SCons.Util.splitext(str(pch_node))[0] + '.obj'
198 # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj)
199 if pchobj not in extrasources:
200 extrasources.append(pchobj)
202 return (target+extratargets,source+extrasources)
204 def RegServerFunc(target, source, env):
205 if 'register' in env and env['register']:
206 ret = regServerAction([target[0]], [source[0]], env)
207 if ret:
208 raise SCons.Errors.UserError("Unable to register %s" % target[0])
209 else:
210 print("Registered %s sucessfully" % target[0])
211 return ret
212 return 0
214 # These are the actual actions run to embed the manifest.
215 # They are only called from the Check versions below.
216 embedManifestExeAction = SCons.Action.Action('$MTEXECOM')
217 embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM')
219 def embedManifestDllCheck(target, source, env):
220 """Function run by embedManifestDllCheckAction to check for existence of manifest
221 and other conditions, and embed the manifest by calling embedManifestDllAction if so."""
222 if env.get('WINDOWS_EMBED_MANIFEST', 0):
223 manifestSrc = target[0].get_abspath() + '.manifest'
224 if os.path.exists(manifestSrc):
225 ret = embedManifestDllAction([target[0]], None, env)
226 if ret:
227 raise SCons.Errors.UserError("Unable to embed manifest into %s" % (target[0]))
228 return ret
229 else:
230 print('(embed: no %s.manifest found; not embedding.)'%str(target[0]))
231 return 0
233 def embedManifestExeCheck(target, source, env):
234 """Function run by embedManifestExeCheckAction to check for existence of manifest
235 and other conditions, and embed the manifest by calling embedManifestExeAction if so."""
236 if env.get('WINDOWS_EMBED_MANIFEST', 0):
237 manifestSrc = target[0].get_abspath() + '.manifest'
238 if os.path.exists(manifestSrc):
239 ret = embedManifestExeAction([target[0]], None, env)
240 if ret:
241 raise SCons.Errors.UserError("Unable to embed manifest into %s" % (target[0]))
242 return ret
243 else:
244 print('(embed: no %s.manifest found; not embedding.)'%str(target[0]))
245 return 0
247 embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None)
248 embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None)
250 regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR")
251 regServerCheck = SCons.Action.Action(RegServerFunc, None)
252 shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES", "$SHLINKCOMSTR")}', '$SHLINKCOMSTR')
253 compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction
254 ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES", "$LDMODULECOMSTR")}', '$LDMODULECOMSTR')
255 compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction
256 exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows", "$LINKCOMSTR")}', '$LINKCOMSTR')
257 compositeLinkAction = exeLinkAction + embedManifestExeCheckAction
259 def generate(env) -> None:
260 """Add Builders and construction variables for ar to an Environment."""
261 SCons.Tool.createSharedLibBuilder(env, shlib_suffix='$SHLIBSUFFIX')
262 SCons.Tool.createProgBuilder(env)
264 env['SHLINK'] = '$LINK'
265 env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll')
266 env['_SHLINK_TARGETS'] = windowsShlinkTargets
267 env['_SHLINK_SOURCES'] = windowsShlinkSources
268 env['SHLINKCOM'] = compositeShLinkAction
269 env.Append(SHLIBEMITTER=[windowsLibEmitter])
270 env.Append(LDMODULEEMITTER=[windowsLibEmitter])
271 env['LINK'] = 'link'
272 env['LINKFLAGS'] = SCons.Util.CLVar('/nologo')
273 env['_PDB'] = pdbGenerator
274 env['LINKCOM'] = compositeLinkAction
275 env.Append(PROGEMITTER=[prog_emitter])
276 env['LIBDIRPREFIX'] = '/LIBPATH:'
277 env['LIBDIRSUFFIX'] = ''
278 env['LIBLINKPREFIX'] = ''
279 env['LIBLINKSUFFIX'] = '$LIBSUFFIX'
281 env['WINDOWSDEFPREFIX'] = ''
282 env['WINDOWSDEFSUFFIX'] = '.def'
283 env['WINDOWSEXPPREFIX'] = ''
284 env['WINDOWSEXPSUFFIX'] = '.exp'
285 env['WINDOWS_INSERT_DEF'] = 0
287 env['WINDOWSSHLIBMANIFESTPREFIX'] = ''
288 env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest'
289 env['WINDOWSPROGMANIFESTPREFIX'] = ''
290 env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest'
292 env['REGSVRACTION'] = regServerCheck
293 env['REGSVR'] = os.path.join(
294 SCons.Platform.win32.get_system_root(), 'System32', 'regsvr32'
296 env['REGSVRFLAGS'] = '/s '
297 env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}'
299 env['WINDOWS_EMBED_MANIFEST'] = 0
300 env['MT'] = 'mt'
301 # env['MTFLAGS'] = ['-hashupdate']
302 env['MTFLAGS'] = SCons.Util.CLVar('/nologo')
303 # Note: use - here to prevent build failure if no manifest produced.
304 # This seems much simpler than a fancy system using a function action to see
305 # if the manifest actually exists before trying to run mt with it.
306 env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1'
307 env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2'
308 # TODO Future work garyo 27-Feb-11
309 env['_MANIFEST_SOURCES'] = None # _windowsManifestSources
311 # Set-up ms tools paths
312 msvc_setup_env_once(env, tool=tool_name)
314 # Loadable modules are on Windows the same as shared libraries, but they
315 # are subject to different build parameters (LDMODULE* variables).
316 # Therefore LDMODULE* variables correspond as much as possible to
317 # SHLINK*/SHLIB* ones.
318 SCons.Tool.createLoadableModuleBuilder(env, loadable_module_suffix='$LDMODULESUFFIX')
319 env['LDMODULE'] = '$SHLINK'
320 env['LDMODULEPREFIX'] = '$SHLIBPREFIX'
321 env['LDMODULESUFFIX'] = '$SHLIBSUFFIX'
322 env['LDMODULEFLAGS'] = '$SHLINKFLAGS'
323 env['_LDMODULE_TARGETS'] = _windowsLdmodTargets
324 env['_LDMODULE_SOURCES'] = _windowsLdmodSources
325 env['LDMODULEEMITTER'] = [ldmodEmitter]
326 env['LDMODULECOM'] = compositeLdmodAction
328 # Issue #3350
329 # Change tempfile argument joining character from a space to a newline
330 # mslink will fail if any single line is too long, but is fine with many lines
331 # in a tempfile
332 env['TEMPFILEARGJOIN'] = os.linesep
334 def exists(env):
335 return msvc_setup_env_tool(env, tool=tool_name)
337 # Local Variables:
338 # tab-width:4
339 # indent-tabs-mode:nil
340 # End:
341 # vim: set expandtab tabstop=4 shiftwidth=4: