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.
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()
40 import SCons
.Platform
.win32
42 import SCons
.Tool
.msvc
43 import SCons
.Tool
.msvs
46 from SCons
.Tool
.MSCommon
import (
50 from SCons
.Tool
.MSCommon
.common
import get_pch_node
54 def pdbGenerator(env
, target
, source
, for_signature
):
56 return ['/PDB:%s' % target
[0].attributes
.pdb
, '/DEBUG']
57 except (AttributeError, IndexError):
60 def _dllTargets(target
, source
, env
, for_signature
, paramtp
):
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
))
70 def _dllSources(target
, source
, env
, for_signature
, paramtp
):
73 deffile
= env
.FindIxes(source
, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")
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
))
82 # Just treat it as a generic source file.
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
)
107 dll
= env
.FindIxes(target
, '%sPREFIX' % paramtp
, '%sSUFFIX' % paramtp
)
108 no_import_lib
= env
.get('no_import_lib', 0)
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
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
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.
151 '%sPREFIX' % paramtp
, '%sSUFFIX' % paramtp
,
152 "LIBPREFIX", "LIBSUFFIX"))
153 # and .exp file is created if there are exports from a 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
)
178 exe
= env
.FindIxes(target
, "PROGPREFIX", "PROGSUFFIX")
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
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
)
211 raise SCons
.Errors
.UserError("Unable to register %s" % target
[0])
213 print("Registered %s sucessfully" % target
[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
)
230 raise SCons
.Errors
.UserError("Unable to embed manifest into %s" % (target
[0]))
233 print('(embed: no %s.manifest found; not embedding.)'%str
(target
[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
)
244 raise SCons
.Errors
.UserError("Unable to embed manifest into %s" % (target
[0]))
247 print('(embed: no %s.manifest found; not embedding.)'%str
(target
[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
])
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
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
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
335 env
['TEMPFILEARGJOIN'] = os
.linesep
338 return msvc_setup_env_tool(env
, tool
=tool_name
)
342 # indent-tabs-mode:nil
344 # vim: set expandtab tabstop=4 shiftwidth=4: