diff options
author | Gary Oberbrunner <garyo@oberbrunner.com> | 2011-03-02 01:53:21 (GMT) |
---|---|---|
committer | Gary Oberbrunner <garyo@oberbrunner.com> | 2011-03-02 01:53:21 (GMT) |
commit | b25e8d31d6d60f73780309ff9a00cb1b23a58f73 (patch) | |
tree | 3075c0d3885023d45b802e3787d437537ac6f50e | |
parent | 8a1ad1c1942246536c33c11adf2eeb4b6ac68638 (diff) | |
download | SCons-b25e8d31d6d60f73780309ff9a00cb1b23a58f73.zip SCons-b25e8d31d6d60f73780309ff9a00cb1b23a58f73.tar.gz SCons-b25e8d31d6d60f73780309ff9a00cb1b23a58f73.tar.bz2 |
Support automatically embedding manifests in EXEs and DLLs on Windows.
-rw-r--r-- | src/CHANGES.txt | 4 | ||||
-rw-r--r-- | src/RELEASE.txt | 4 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.py | 66 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.xml | 37 | ||||
-rw-r--r-- | test/MSVC/embed-manifest.py | 88 |
5 files changed, 192 insertions, 7 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 2aa48b3..8bb1bb7 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -7,6 +7,10 @@ RELEASE 2.1.0.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE + From Gary Oberbrunner and Sohail Somani: + - new construction variable WINDOWS_EMBED_MANIFEST to automatically + embed manifests in Windows EXEs and DLLs. + From Gary Oberbrunner: - Adding None to an Action no longer fails (just returns original action) diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 306403d..f8db734 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -31,6 +31,9 @@ NEW FUNCTIONALITY + - SCons can now automatically embed manifests in Windows executables + and DLLs, by setting WINDOWS_EMBED_MANIFEST in the environment. + - SCons now searches for site_scons dirs in several system-wide and per-user locations, in addition to the SConstruct top dir. This should enable much easier use of third-party (non-core) @@ -120,6 +123,7 @@ Rob Managan, Gary Oberbrunner, Evgeny Podjachev, + Sohail Somani, Anatoly Techtonik, Allen Weeks, Russel Winder, diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index e0eec8c..07af49e 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -116,8 +116,9 @@ def _dllEmitter(target, source, env, paramtp): "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")) version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) - if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0): - # MSVC 8 automatically generates .manifest files that must be installed + if version_num >= 8.0 and \ + (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): + # MSVC 8 and above automatically generate .manifest files that must be installed extratargets.append( env.ReplaceIxes(dll, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, @@ -164,8 +165,9 @@ def prog_emitter(target, source, env): raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")) version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) - if version_num >= 8.0 and env.get('WINDOWS_INSERT_MANIFEST', 0): - # MSVC 8 automatically generates .manifest files that have to be installed + if version_num >= 8.0 and \ + (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): + # MSVC 8 and above automatically generate .manifest files that have to be installed extratargets.append( env.ReplaceIxes(exe, "PROGPREFIX", "PROGSUFFIX", @@ -188,12 +190,50 @@ def RegServerFunc(target, source, env): return ret return 0 +# These are the actual actions run to embed the manifest. +# They are only called from the Check versions below. +embedManifestExeAction = SCons.Action.Action('$MTEXECOM') +embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM') + +def embedManifestDllCheck(target, source, env): + """Function run by embedManifestDllCheckAction to check for existence of manifest + and other conditions, and embed the manifest by calling embedManifestDllAction if so.""" + if env.get('WINDOWS_EMBED_MANIFEST', 0): + manifestSrc = target[0].abspath + '.manifest' + if os.path.exists(manifestSrc): + ret = (embedManifestDllAction) ([target[0]],None,env) + if ret: + raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) + return ret + else: + print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) + return 0 + +def embedManifestExeCheck(target, source, env): + """Function run by embedManifestExeCheckAction to check for existence of manifest + and other conditions, and embed the manifest by calling embedManifestExeAction if so.""" + if env.get('WINDOWS_EMBED_MANIFEST', 0): + manifestSrc = target[0].abspath + '.manifest' + if os.path.exists(manifestSrc): + ret = (embedManifestExeAction) ([target[0]],None,env) + if ret: + raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) + return ret + else: + print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) + return 0 + +embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None) +embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None) + regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") regServerCheck = SCons.Action.Action(RegServerFunc, None) shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}') -compositeShLinkAction = shlibLinkAction + regServerCheck +compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}') -compositeLdmodAction = ldmodLinkAction + regServerCheck +compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction +exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}') +compositeLinkAction = exeLinkAction + embedManifestExeCheckAction def generate(env): """Add Builders and construction variables for ar to an Environment.""" @@ -209,7 +249,7 @@ def generate(env): env['LINK'] = 'link' env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') env['_PDB'] = pdbGenerator - env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}' + env['LINKCOM'] = compositeLinkAction env.Append(PROGEMITTER = [prog_emitter]) env['LIBDIRPREFIX']='/LIBPATH:' env['LIBDIRSUFFIX']='' @@ -238,6 +278,18 @@ def generate(env): env['REGSVRFLAGS'] = '/s ' env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}' + env['WINDOWS_EMBED_MANIFEST'] = 0 + env['MT'] = 'mt' + #env['MTFLAGS'] = ['-hashupdate'] + env['MTFLAGS'] = SCons.Util.CLVar('/nologo') + # Note: use - here to prevent build failure if no manifest produced. + # This seems much simpler than a fancy system using a function action to see + # if the manifest actually exists before trying to run mt with it. + env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1' + env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2' + # Future work garyo 27-Feb-11 + env['_MANIFEST_SOURCES'] = None # _windowsManifestSources + # Set-up ms tools paths msvc_setup_env_once(env) diff --git a/src/engine/SCons/Tool/mslink.xml b/src/engine/SCons/Tool/mslink.xml index a2105db..35e8b64 100644 --- a/src/engine/SCons/Tool/mslink.xml +++ b/src/engine/SCons/Tool/mslink.xml @@ -94,6 +94,43 @@ see the entry for that variable for specific examples. </summary> </cvar> +<cvar name="WINDOWS_EMBED_MANIFEST"> +<summary> +Set this variable to True or 1 to embed the compiler-generated manifest +(normally <literal>${TARGET}.manifest</literal>) +into all Windows exes and DLLs built with this environment, +as a resource during their link step. +This is done using &cv-link-MT; and &cv-link-MTEXECOM; and &cv-link-MTSHLIBCOM;. +</summary> +</cvar> + +<cvar name="MT"> +<summary> +The program used on Windows systems to embed manifests into DLLs and EXEs. +See also &cv-link-WINDOWS_EMBED_MANIFEST;. +</summary> +</cvar> + +<cvar name="MTFLAGS"> +<summary> +Flags passed to the &cv-link-MT; manifest embedding program (Windows only). +</summary> +</cvar> + +<cvar name="MTEXECOM"> +<summary> +The Windows command line used to embed manifests into executables. +See also &cv-link-MTSHLIBCOM;. +</summary> +</cvar> + +<cvar name="MTSHLIBCOM"> +<summary> +The Windows command line used to embed manifests into shared libraries (DLLs). +See also &cv-link-MTEXECOM;. +</summary> +</cvar> + <cvar name="REGSVR"> <summary> The program used on Windows systems diff --git a/test/MSVC/embed-manifest.py b/test/MSVC/embed-manifest.py new file mode 100644 index 0000000..a5a906e --- /dev/null +++ b/test/MSVC/embed-manifest.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that manifest files get embedded correctly in EXEs and DLLs
+"""
+
+import sys
+
+import TestSCons
+
+_exe = TestSCons._exe
+_dll = TestSCons._dll
+_lib = TestSCons._lib
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+ msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+ test.skip_test(msg)
+
+test.write('SConstruct', """\
+env=Environment(WINDOWS_EMBED_MANIFEST=True)
+env.Append(CCFLAGS = '/MD')
+exe=env.Program('test.cpp')
+dll=env.SharedLibrary('testdll.cpp')
+env.Command('exe-extracted.manifest', exe,
+ '$MT /nologo -inputresource:${SOURCE};1 -out:${TARGET}')
+env.Command('dll-extracted.manifest', dll,
+ '$MT /nologo -inputresource:${SOURCE};2 -out:${TARGET}')
+env2=Environment(WINDOWS_EMBED_MANIFEST=True) # no /MD here
+env2.Program('test-nomanifest', env2.Object('test-nomanifest', 'test.cpp'))
+""")
+
+test.write('test.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+ printf("test.cpp\\n");
+ exit (0);
+}
+""")
+
+test.write('testdll.cpp', """\
+int i;
+""")
+
+test.run(arguments = '.')
+
+test.must_exist('test%s' % _exe)
+test.must_exist('test%s.manifest' % _exe)
+test.must_contain('exe-extracted.manifest', '</assembly>')
+test.must_exist('testdll%s' % _dll)
+test.must_exist('testdll%s.manifest' % _dll)
+test.must_contain('dll-extracted.manifest', '</assembly>')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
|