-
-
Notifications
You must be signed in to change notification settings - Fork 321
EmbedManifestIntoTarget
Here I do a fast explanation how to embed your manifest file into your target using the Microsoft Manifest Tool.
To use this tip, you must to create your environment as usual, but at the final, you must to add a line.
# Create your environment as usual.
env = Environment()
# Here I'm building a Shared Library, but you can use this tip for all build types.
env.SharedLibrary(
target='library_name',
source=['mysource.cpp'],
# If you must embed the manifest file into your target, use the line below.
# The number at the end of the line indicates the file type (1: EXE; 2:DLL).
LINKCOM = [env['LINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2']
)
That's it. Enjoy!
You can also run the Microsoft Manifest Tool (mt.exe) as a post-build step. I have found this to be more reliable than adjusting LINKCOM as the method above does.
# Create your environment as usual.
env = Environment()
# Here I'm building a Shared Library, but you can use this tip for all build types.
buildResult = env.SharedLibrary(target='library_name', source=['mysource.cpp'])
# Add a post-build step to embed the manifest using mt.exe
# The number at the end of the line indicates the file type (1: EXE; 2:DLL).
env.AddPostAction(buildResult, 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2')
Here's a two-liner that sets up the correct link environment to automatically embed manifest files, for both executables and shared libraries:
# Create your environment as usual.
env = Environment()
env['LINKCOM'] = [env['LINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1']
env['SHLINKCOM'] = [env['SHLINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2']
If you don't want to use the Microsoft compiler and tools, using MinGW one needs to do the following:
- Create a .manifest file
- Create a simple .rc file that refers to that manifest file
- Compile the .rc file into an object using MinGW's windres command
- Add the object to the LINKFLAGS
In this example, I will embed a manifest file for linking against the Microsoft VC Runtime library. Python 2.6 installs one for us on the system and includes a package called msvcrt. Below, ask Python for the specific name, version, and key. Then write it out to a file:
import sys
# Defaults
name = "Microsoft.VC90"
version = "9.0.21022.8"
key = "1fc8b3b9a1e18e3b"
try:
import msvcrt
name = msvcrt.LIBRARIES_ASSEMBLY_NAME_PREFIX
version = msvcrt.CRT_ASSEMBLY_VERSION
key = msvcrt.VC_ASSEMBLY_PUBLICKEYTOKEN
except:
pass
template = '''\
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="%s.CRT" version="%s" processorArchitecture="*" publicKeyToken="%s"></assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>''' %(name, version, key)
# Write it out to a file.
fout = open("msvcrt.manifest", "w")
fout.write(template)
fout.close()
Create an .rc file with the following text for embedding into an executable:
with open("msvcr.rc", "w") as fout:
fout.write("""
#include "winuser.h"
1 RT_MANIFEST msvcrt.manifest
""")
The "1" above indicates this manifest to be embedded into an executable, change it to "2" for use with a .DLL.
On the command line one would do:
$ windres --input msvcr.rc --output msvcrc.o
From Python, open a subprocess:
try:
out = subprocess.Popen(["windres", "--input", "msvcr.rc", "--output", "msvcr.o"],
stdout = subprocess.PIPE).communicate()
except:
sys.stderr.write("could not execute 'windres', is mingw installed?\n")
Having separate environments for compiling executables and libraries makes it easy to embed different manifest objects:
exe_env.Append(LINKFLAGS = " %s " % exe_manifest_obj_filename)
lib_env.Append(LINKFLAGS = " %s " % lib_manifest_obj_filename)
If one were to simple copy and paste the Python code above into their SConscript, the code would execute every time scons is called. Instead, add the code to a custom Scons check context function. This way, the manifest objects are only built when necessary.
SohailSomani - Why is the second way more reliable?
AtulVarma - I'm not sure why the second way is more reliable, but it's certainly more readable and architecturally sound, IMO. The first solution uses a somewhat obscure environment variable in non-obvious way--it's a solution that reminds me of GNU make and everything that makes Makefiles difficult to understand--whereas the latter uses a more universal and understandable object-oriented mechanism to accomplish the same thing. The latter is also more universally applicable (perhaps this is why its originator called it more "reliable") because it can be used on any kind of target, not just one that was generated by a C/C++ compiler.
GaryOberbrunner - I actually like the first way better. It makes it automatic so you don't have to call AddPostAction on every executable or library (for libs, use SHLINKCOM). I don't find LINKCOM/SHLINKCOM to be obscure at all; they're well-documented as the command (or commands) to use to link an exe or lib. Running mt as part of the link step seems to me to be quite natural. Oh yes; if you want to do this for all exes/libs (which seems likely), just put the modification to LINKCOM/SHLINKCOM into the environment. Then you don't have to touch your SharedLibrary or Program calls, they remain nicely cross-platform.
Kameleon - I added a third method that is derived from GaryOberbrunner's suggestion just above. I'm wondering if the first method is buggy: it is building a shared library, but sets the LINKCOM variable. Shouldn't it be setting SHLINKCOM???