So I have a开发者_JAVA技巧 DLL that is being built with CMake that requires a specific manifest file to be embedded. In Visual Studio settings I can just add the manifest filename under Manifest Tool/Input and Ouput/Additional Manifest Files, and it works correctly. It seems like this is something that should be doable with CMake, but I have been unable to figure it out.
Any ideas on how I can accomplish this with CMake?
cmake-3.4 has now learned how to handle *.manifest files listed as source files.
https://cmake.org/cmake/help/v3.4/release/3.4.html#other
It's not possible to generate the Additional Manifest Files
field in CMake (I checked the source code). So we have to be sneakier.
Visual generates a manifest of its own ( yourapp.exe.manifest.intermediate ) and mixes it with yours. So we have to generate this manifest once, disable the generation, and use the generated manifest afterwards.
Generating the manifest :
This step is optional if you know how to write a complete manifest by yourself. If you're like the rest of the world :
- Create your own manifest as usual
- Add it in the interface (
Additional Manifest Files
) - Recompile, relink
- Locate yourapp.exe.manifest (next to your .exe). Copy it in your sources directory and version it. Don't hesitate to rename it, like yourapp.final.manifest, if it's clearer for you
Disabling the generation :
IF( WIN32 )
SET ( CMAKE_SHARED_LINKER_FLAGS /MANIFEST:NO )
ENDIF( WIN32 )
Using the generated manifest afterwards :
This is done by a manual call to mt.exe (the manifest tool which is normally called after the linker... unless it's disabled) in a post-build step :
add_custom_command(
TARGET YourApp
POST_BUILD
COMMAND "mt.exe" -manifest \"$(TargetDir)\\yourapp.final.manifest\" -outputresource:"$(TargetDir)$(TargetFileName)"\;\#1
COMMENT "Adding manifest..."
)
(You'll probably need to change $(TargetDir) to $(OutDir) depending on how you wrote your CMake; Use Visual's Macros
button to see their values. And remember : #1 for executables, #2 for dlls)
I just found out that you can merge multiple manifest files (or embedded manifests inside executables) into an existing manifest file (or executable) with mt.exe. This way, you don't have to disable automatic manifest generation of visual studio. You can just add new manifest data with mt.exe as a postbuild step. Example:
program.exe has embedded manifest:
<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
dpiaware.manifest contains:
<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware>
</windowsSettings>
</application>
</assembly>
Run command:
mt.exe -manifest dpiaware.manifest "-inputresource:program.exe;#1" -outputresource:program.exe;#1
Now program.exe contains embedded manifest:
<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware>
</windowsSettings>
</application>
</assembly>
I just went through this exercise myself, which is what brought me to this page. Calvin1602's answer pretty much lays out the solution, but I had to finagle the syntax a bit to make it work for me. Here are the exact commands that finally worked:
if (WIN32)
set(CMAKE_SHARED_LINKER_FLAGS /MANIFEST:NO)
endif()
add_custom_command(TARGET
odrmanager
POST_BUILD
COMMAND
"mt.exe" -manifest \"${CMAKE_CURRENT_SOURCE_DIR}\\odrmanager.dll.manifest\" -outputresource:\"${CMAKE_CURRENT_BINARY_DIR}\\odrmanager\\odrmanager.dll\"\;\#2
COMMENT
"Adding custom manifest containing MSVCRT80 dependency..."
)
Note that you should use #1
in the mt.exe
command when the target is an application, and #2
when it's a DLL (at least, as far as I understand it--it didn't work for me until I changed the 1
to a 2
).
Also, you can use mt.exe
to extract the original manifest from the DLL if you want/need to. The command looks like this:
mt -inputresource:odrmanager.dll;#2 -out:odrmanager.manifest
It's not too hard to hand-edit the output if you have a manifest file for the dependency you want to merge in. But I sorta like Calvin1602's trick of having Visual Studio do it for you if you're using Visual Studio solution files rather than nmake.
This was very helpful. Here's what I ended up doing for a DLL that needed an MSVCR90 manifest, your mileage may vary:
add_custom_command(
TARGET foo
POST_BUILD COMMAND
mt.exe -manifest \"${MYDEPDIR}/msvcr90/Microsoft.VC90.CRT.manifest\" "-inputresource:\"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/foo.dll\";#2" -outputresource:\"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/foo.dll\";#2
COMMENT
"Appending manifest for MSVCRT90 dependency."
)
精彩评论