ENH: add ability to have manifest files and incremental linking with make and nmake
This commit is contained in:
parent
24d6ecd81c
commit
b479c6a8a9
|
@ -1,30 +0,0 @@
|
|||
|
||||
# Leave the first line of this file empty so this module will not be
|
||||
# included in the documentation.
|
||||
|
||||
# This script is invoked from Windows-cl.cmake and passed the TARGET
|
||||
# variable on the command line.
|
||||
|
||||
# Conditionally embed the manifest in the executable if it exists.
|
||||
IF(EXISTS "${TARGET}.manifest")
|
||||
# Construct the manifest embedding command.
|
||||
SET(CMD
|
||||
mt ${CMAKE_CL_NOLOGO} /manifest ${TARGET}.manifest
|
||||
/outputresource:${TARGET}
|
||||
)
|
||||
|
||||
# Run the embedding command.
|
||||
EXECUTE_PROCESS(COMMAND ${CMD}\;\#2 RESULT_VARIABLE RESULT)
|
||||
|
||||
# Check whether the command failed.
|
||||
IF(NOT "${RESULT}" MATCHES "^0$")
|
||||
# The embedding failed remove the target and the manifest.
|
||||
FILE(REMOVE ${TARGET} ${TARGET}.manifest)
|
||||
|
||||
# Describe the failure in a message.
|
||||
STRING(REGEX REPLACE ";" " " CMD "${CMD}")
|
||||
MESSAGE(FATAL_ERROR
|
||||
"Failed to embed manifest in ${TARGET} using command \"${CMD};#2\""
|
||||
)
|
||||
ENDIF(NOT "${RESULT}" MATCHES "^0$")
|
||||
ENDIF(EXISTS "${TARGET}.manifest")
|
|
@ -1,30 +0,0 @@
|
|||
|
||||
# Leave the first line of this file empty so this module will not be
|
||||
# included in the documentation.
|
||||
|
||||
# This script is invoked from Windows-cl.cmake and passed the TARGET
|
||||
# variable on the command line.
|
||||
|
||||
# Conditionally embed the manifest in the executable if it exists.
|
||||
IF(EXISTS "${TARGET}.manifest")
|
||||
# Construct the manifest embedding command.
|
||||
SET(CMD
|
||||
mt ${CMAKE_CL_NOLOGO} /manifest ${TARGET}.manifest
|
||||
/outputresource:${TARGET}
|
||||
)
|
||||
|
||||
# Run the embedding command.
|
||||
EXECUTE_PROCESS(COMMAND ${CMD}\;\#1 RESULT_VARIABLE RESULT)
|
||||
|
||||
# Check whether the command failed.
|
||||
IF(NOT "${RESULT}" MATCHES "^0$")
|
||||
# The embedding failed remove the target and the manifest.
|
||||
FILE(REMOVE ${TARGET} ${TARGET}.manifest)
|
||||
|
||||
# Describe the failure in a message.
|
||||
STRING(REGEX REPLACE ";" " " CMD "${CMD}")
|
||||
MESSAGE(FATAL_ERROR
|
||||
"Failed to embed manifest in ${TARGET} using command \"${CMD};#1\""
|
||||
)
|
||||
ENDIF(NOT "${RESULT}" MATCHES "^0$")
|
||||
ENDIF(EXISTS "${TARGET}.manifest")
|
|
@ -167,25 +167,15 @@ ENDIF(CMAKE_FORCE_WIN64)
|
|||
|
||||
# default to Debug builds
|
||||
IF(MSVC_VERSION GREATER 1310)
|
||||
# Not used by generator directly but referenced below.
|
||||
SET(CMAKE_CREATE_LIB_MANIFEST
|
||||
"$(CMAKE_COMMAND) -DTARGET=<TARGET> -DCMAKE_CL_NOLOGO=${CMAKE_CL_NOLOGO} -P \"${CMAKE_ROOT}/Modules/CMakeVCManifest.cmake\"")
|
||||
SET(CMAKE_CREATE_EXE_MANIFEST
|
||||
"$(CMAKE_COMMAND) -DTARGET=<TARGET> -DCMAKE_CL_NOLOGO=${CMAKE_CL_NOLOGO} -P \"${CMAKE_ROOT}/Modules/CMakeVCManifestExe.cmake\"")
|
||||
|
||||
# for 2005 make sure the manifest is put in the dll with mt
|
||||
SET(CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY}"
|
||||
${CMAKE_CREATE_LIB_MANIFEST})
|
||||
SET(CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE}"
|
||||
${CMAKE_CREATE_LIB_MANIFEST})
|
||||
SET(CMAKE_CXX_CREATE_SHARED_LIBRARY "$(CMAKE_COMMAND) -E vs_link_dll ${CMAKE_CXX_CREATE_SHARED_LIBRARY}")
|
||||
SET(CMAKE_CXX_CREATE_SHARED_MODULE "$(CMAKE_COMMAND) -E vs_link_dll ${CMAKE_CXX_CREATE_SHARED_MODULE}")
|
||||
# create a C shared library
|
||||
SET(CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY}")
|
||||
# create a C shared module just copy the shared library rule
|
||||
SET(CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE}")
|
||||
SET(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE}"
|
||||
${CMAKE_CREATE_EXE_MANIFEST})
|
||||
SET(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE}"
|
||||
${CMAKE_CREATE_EXE_MANIFEST})
|
||||
SET(CMAKE_CXX_LINK_EXECUTABLE "$(CMAKE_COMMAND) -E vs_link_exe ${CMAKE_CXX_LINK_EXECUTABLE}")
|
||||
SET(CMAKE_C_LINK_EXECUTABLE "$(CMAKE_COMMAND) -E vs_link_exe ${CMAKE_C_LINK_EXECUTABLE}")
|
||||
|
||||
SET(CMAKE_BUILD_TYPE_INIT Debug)
|
||||
SET (CMAKE_CXX_FLAGS_INIT "/DWIN32 /D_WINDOWS /W3 /Zm1000 /EHsc /GR")
|
||||
|
|
|
@ -1020,8 +1020,12 @@ void cmMakefile::AddLinkDirectory(const char* dir)
|
|||
// much bigger than 20. We cannot use a set because of order
|
||||
// dependency of the link search path.
|
||||
|
||||
if(!dir)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// remove trailing slashes
|
||||
if(dir && dir[strlen(dir)-1] == '/')
|
||||
if(dir[strlen(dir)-1] == '/')
|
||||
{
|
||||
std::string newdir = dir;
|
||||
newdir = newdir.substr(0, newdir.size()-1);
|
||||
|
|
|
@ -563,29 +563,16 @@ std::vector<cmStdString> cmSystemTools::ParseArguments(const char* command)
|
|||
return args;
|
||||
}
|
||||
|
||||
bool cmSystemTools::RunSingleCommand(
|
||||
const char* command,
|
||||
std::string* output,
|
||||
int *retVal,
|
||||
const char* dir,
|
||||
bool verbose,
|
||||
double timeout)
|
||||
|
||||
bool cmSystemTools::RunSingleCommand(std::vector<cmStdString>const& command,
|
||||
std::string* output ,
|
||||
int* retVal , const char* dir ,
|
||||
bool verbose ,
|
||||
double timeout )
|
||||
{
|
||||
if(s_DisableRunCommandOutput)
|
||||
{
|
||||
verbose = false;
|
||||
}
|
||||
|
||||
std::vector<cmStdString> args = cmSystemTools::ParseArguments(command);
|
||||
|
||||
if(args.size() < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<const char*> argv;
|
||||
for(std::vector<cmStdString>::const_iterator a = args.begin();
|
||||
a != args.end(); ++a)
|
||||
for(std::vector<cmStdString>::const_iterator a = command.begin();
|
||||
a != command.end(); ++a)
|
||||
{
|
||||
argv.push_back(a->c_str());
|
||||
}
|
||||
|
@ -700,6 +687,29 @@ bool cmSystemTools::RunSingleCommand(
|
|||
cmsysProcess_Delete(cp);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool cmSystemTools::RunSingleCommand(
|
||||
const char* command,
|
||||
std::string* output,
|
||||
int *retVal,
|
||||
const char* dir,
|
||||
bool verbose,
|
||||
double timeout)
|
||||
{
|
||||
if(s_DisableRunCommandOutput)
|
||||
{
|
||||
verbose = false;
|
||||
}
|
||||
|
||||
std::vector<cmStdString> args = cmSystemTools::ParseArguments(command);
|
||||
|
||||
if(args.size() < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return cmSystemTools::RunSingleCommand(args, output,retVal,
|
||||
dir, verbose, timeout);
|
||||
}
|
||||
bool cmSystemTools::RunCommand(const char* command,
|
||||
std::string& output,
|
||||
const char* dir,
|
||||
|
|
|
@ -209,6 +209,16 @@ public:
|
|||
int* retVal = 0, const char* dir = 0,
|
||||
bool verbose = true,
|
||||
double timeout = 0.0);
|
||||
/**
|
||||
* In this version of RunSingleCommand, command[0] should be
|
||||
* the command to run, and each argument to the command should
|
||||
* be in comand[1]...command[command.size()]
|
||||
*/
|
||||
static bool RunSingleCommand(std::vector<cmStdString> const& command,
|
||||
std::string* output = 0,
|
||||
int* retVal = 0, const char* dir = 0,
|
||||
bool verbose = true,
|
||||
double timeout = 0.0);
|
||||
|
||||
/**
|
||||
* Parse arguments out of a single string command
|
||||
|
|
273
Source/cmake.cxx
273
Source/cmake.cxx
|
@ -1451,7 +1451,14 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args)
|
|||
std::cerr << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
else if (args[1] == "vs_link_exe")
|
||||
{
|
||||
return cmake::VisualStudioLink(args, 1);
|
||||
}
|
||||
else if (args[1] == "vs_link_dll")
|
||||
{
|
||||
return cmake::VisualStudioLink(args, 2);
|
||||
}
|
||||
#ifdef CMAKE_BUILD_WITH_CMAKE
|
||||
// Internal CMake color makefile support.
|
||||
else if (args[1] == "cmake_echo_color")
|
||||
|
@ -3652,3 +3659,267 @@ static bool cmakeCheckStampFile(const char* stampName)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// For visual studio 2005 and newer manifest files need to be embeded into
|
||||
// exe and dll's. This code does that in such a way that incremental linking
|
||||
// still works.
|
||||
int cmake::VisualStudioLink(std::vector<std::string>& args, int type)
|
||||
{
|
||||
if(args.size() < 2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
bool verbose = false;
|
||||
if(cmSystemTools::GetEnv("VERBOSE"))
|
||||
{
|
||||
verbose = true;
|
||||
}
|
||||
// figure out if this is an incremental link or not and run the correct
|
||||
// link function.
|
||||
for(std::vector<std::string>::iterator i = args.begin();
|
||||
i != args.end(); ++i)
|
||||
{
|
||||
if(cmSystemTools::Strucmp(i->c_str(), "/INCREMENTAL:YES") == 0)
|
||||
{
|
||||
return cmake::VisualStudioLinkIncremental(args, type, verbose);
|
||||
}
|
||||
}
|
||||
return cmake::VisualStudioLinkNonIncremental(args, type, verbose);
|
||||
}
|
||||
|
||||
int cmake::ParseVisualStudioLinkCommand(std::vector<std::string>& args,
|
||||
std::vector<cmStdString>& command,
|
||||
std::string& targetName)
|
||||
{
|
||||
std::vector<std::string>::iterator i = args.begin();
|
||||
i++; // skip -E
|
||||
i++; // skip vs_link_dll or vs_link_exe
|
||||
command.push_back(*i);
|
||||
i++; // move past link command
|
||||
for(; i != args.end(); ++i)
|
||||
{
|
||||
command.push_back(*i);
|
||||
if(i->find("/Fe") == 0)
|
||||
{
|
||||
targetName = i->substr(3);
|
||||
}
|
||||
if(i->find("/out:") == 0)
|
||||
{
|
||||
targetName = i->substr(5);
|
||||
}
|
||||
}
|
||||
if(targetName.size() == 0 || command.size() == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cmake::RunCommand(const char* comment,
|
||||
std::vector<cmStdString>& command,
|
||||
bool verbose,
|
||||
int* retCodeOut)
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
std::cout << comment << ":\n";
|
||||
for(std::vector<cmStdString>::iterator i = command.begin();
|
||||
i != command.end(); ++i)
|
||||
{
|
||||
std::cout << i->c_str() << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::string output;
|
||||
int retCode =0;
|
||||
// use rc command to create .res file
|
||||
cmSystemTools::RunSingleCommand(command,
|
||||
&output,
|
||||
&retCode);
|
||||
if(verbose)
|
||||
{
|
||||
std::cout << output << "\n";
|
||||
}
|
||||
// if retCodeOut is requested then always return true
|
||||
// and set the retCodeOut to retCode
|
||||
if(retCodeOut)
|
||||
{
|
||||
*retCodeOut = retCode;
|
||||
return true;
|
||||
}
|
||||
if(retCode != 0)
|
||||
{
|
||||
std::cout << comment << " failed. with " << retCode << "\n";
|
||||
}
|
||||
return retCode == 0;
|
||||
}
|
||||
|
||||
int cmake::VisualStudioLinkIncremental(std::vector<std::string>& args,
|
||||
int type, bool verbose)
|
||||
{
|
||||
// This follows the steps listed here:
|
||||
// http://blogs.msdn.com/zakramer/archive/2006/05/22/603558.aspx
|
||||
|
||||
// 1. Compiler compiles the application and generates the *.obj files.
|
||||
// 2. An empty manifest file is generated if this is a clean build and if
|
||||
// not the previous one is reused.
|
||||
// 3. The resource compiler (rc.exe) compiles the *.manifest file to a
|
||||
// *.res file.
|
||||
// 4. Linker generates the binary (EXE or DLL) with the /incremental
|
||||
// switch and embeds the dummy manifest file. The linker also generates
|
||||
// the real manifest file based on the binaries that your binary depends
|
||||
// on.
|
||||
// 5. The manifest tool (mt.exe) is then used to generate the final
|
||||
// manifest.
|
||||
|
||||
// If the final manifest is changed, then 6 and 7 are run, if not
|
||||
// they are skipped, and it is done.
|
||||
|
||||
// 6. The resource compiler is invoked one more time.
|
||||
// 7. Finally, the Linker does another incremental link, but since the
|
||||
// only thing that has changed is the *.res file that contains the
|
||||
// manifest it is a short link.
|
||||
std::vector<cmStdString> linkCommand;
|
||||
std::string targetName;
|
||||
if(cmake::ParseVisualStudioLinkCommand(args, linkCommand, targetName) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
std::string manifestArg = "/MANIFESTFILE:";
|
||||
std::vector<cmStdString> rcCommand;
|
||||
rcCommand.push_back(cmSystemTools::FindProgram("rc.exe"));
|
||||
std::vector<cmStdString> mtCommand;
|
||||
mtCommand.push_back(cmSystemTools::FindProgram("mt.exe"));
|
||||
std::string tempManifest;
|
||||
tempManifest = targetName;
|
||||
tempManifest += ".intermediate.manifest";
|
||||
std::string resourceInputFile = targetName;
|
||||
resourceInputFile += ".resource.txt";
|
||||
if(verbose)
|
||||
{
|
||||
std::cout << "Create " << resourceInputFile.c_str() << "\n";
|
||||
}
|
||||
// Create input file for rc command
|
||||
std::ofstream fout(resourceInputFile.c_str());
|
||||
if(!fout)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
std::string manifestFile = targetName;
|
||||
manifestFile += ".embed.manifest";
|
||||
std::string fullPath=manifestFile;
|
||||
fout << type << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID "
|
||||
"*/ 24 /* RT_MANIFEST */ " << "\"" << fullPath.c_str() << "\"";
|
||||
fout.close();
|
||||
manifestArg += tempManifest;
|
||||
// add the manifest arg to the linkCommand
|
||||
linkCommand.push_back(manifestArg);
|
||||
// if manifestFile is not yet created, create an
|
||||
// empty one
|
||||
if(!cmSystemTools::FileExists(manifestFile.c_str()))
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
std::cout << "Create empty: " << manifestFile.c_str() << "\n";
|
||||
}
|
||||
std::ofstream fout(manifestFile.c_str());
|
||||
}
|
||||
std::string resourceFile = manifestFile;
|
||||
resourceFile += ".res";
|
||||
// add the resource file to the end of the link command
|
||||
linkCommand.push_back(resourceFile);
|
||||
std::string outputOpt = "/fo";
|
||||
outputOpt += resourceFile;
|
||||
rcCommand.push_back(outputOpt);
|
||||
rcCommand.push_back(resourceInputFile);
|
||||
// Run rc command to create resource
|
||||
if(!cmake::RunCommand("RC Pass 1", rcCommand, verbose))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Now run the link command to link and create manifest
|
||||
if(!cmake::RunCommand("LINK Pass 1", linkCommand, verbose))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// create mt command
|
||||
std::string outArg("/out:");
|
||||
outArg+= manifestFile;
|
||||
mtCommand.push_back("/nologo");
|
||||
mtCommand.push_back(outArg);
|
||||
mtCommand.push_back("/notify_update");
|
||||
mtCommand.push_back("/manifest");
|
||||
mtCommand.push_back(tempManifest);
|
||||
// now run mt.exe to create the final manifest file
|
||||
int mtRet =0;
|
||||
cmake::RunCommand("MT", mtCommand, verbose, &mtRet);
|
||||
// if mt returns 0, then the manifest was not changed and
|
||||
// we do not need to do another link step
|
||||
if(mtRet == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// check for magic mt return value if mt returns the magic number
|
||||
// 1090650113 then it means that it updated the manifest file and we need
|
||||
// to do the final link. If mt has any value other than 0 or 1090650113
|
||||
// then there was some problem with the command itself and there was an
|
||||
// error so return the error code back out of cmake so make can report it.
|
||||
if(mtRet != 1090650113)
|
||||
{
|
||||
return mtRet;
|
||||
}
|
||||
// update the resource file with the new manifest from the mt command.
|
||||
if(!cmake::RunCommand("RC Pass 2", rcCommand, verbose))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Run the final incremental link that will put the new manifest resource
|
||||
// into the file incrementally.
|
||||
if(!cmake::RunCommand("FINAL LINK", linkCommand, verbose))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmake::VisualStudioLinkNonIncremental(std::vector<std::string>& args,
|
||||
int type,
|
||||
bool verbose)
|
||||
{
|
||||
std::vector<cmStdString> linkCommand;
|
||||
std::string targetName;
|
||||
if(cmake::ParseVisualStudioLinkCommand(args, linkCommand, targetName) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Run the link command as given
|
||||
if(!cmake::RunCommand("LINK", linkCommand, verbose))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
std::vector<cmStdString> mtCommand;
|
||||
mtCommand.push_back(cmSystemTools::FindProgram("mt.exe"));
|
||||
mtCommand.push_back("/nologo");
|
||||
mtCommand.push_back("/manifest");
|
||||
std::string manifestFile = targetName;
|
||||
manifestFile += ".manifest";
|
||||
mtCommand.push_back(manifestFile);
|
||||
std::string outresource = "/outputresource:";
|
||||
outresource += targetName;
|
||||
outresource += ";#";
|
||||
if(type == 1)
|
||||
{
|
||||
outresource += "1";
|
||||
}
|
||||
else if(type == 2)
|
||||
{
|
||||
outresource += "2";
|
||||
}
|
||||
mtCommand.push_back(outresource);
|
||||
// Now use the mt tool to embed the manifest into the exe or dll
|
||||
if(!cmake::RunCommand("MT", mtCommand, verbose))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -387,7 +387,20 @@ protected:
|
|||
|
||||
static int ExecuteEchoColor(std::vector<std::string>& args);
|
||||
static int ExecuteLinkScript(std::vector<std::string>& args);
|
||||
|
||||
static int VisualStudioLink(std::vector<std::string>& args, int type);
|
||||
static int VisualStudioLinkIncremental(std::vector<std::string>& args,
|
||||
int type,
|
||||
bool verbose);
|
||||
static int VisualStudioLinkNonIncremental(std::vector<std::string>& args,
|
||||
int type,
|
||||
bool verbose);
|
||||
static int ParseVisualStudioLinkCommand(std::vector<std::string>& args,
|
||||
std::vector<cmStdString>& command,
|
||||
std::string& targetName);
|
||||
static bool RunCommand(const char* comment,
|
||||
std::vector<cmStdString>& command,
|
||||
bool verbose,
|
||||
int* retCodeOut = 0);
|
||||
cmVariableWatch* VariableWatch;
|
||||
|
||||
///! Find the full path to one of the cmake programs like ctest, cpack, etc.
|
||||
|
|
Loading…
Reference in New Issue