summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBill Hoffman <bill.hoffman@kitware.com>2008-01-01 20:13:41 (GMT)
committerBill Hoffman <bill.hoffman@kitware.com>2008-01-01 20:13:41 (GMT)
commitb479c6a8a98664c19eeaef40b71850c1f5315601 (patch)
treed6bdae61666c26a6872fcc8baecc5d7cdb90ba8c
parent24d6ecd81cf457367c622cd166da5d49f54bf42b (diff)
downloadCMake-b479c6a8a98664c19eeaef40b71850c1f5315601.zip
CMake-b479c6a8a98664c19eeaef40b71850c1f5315601.tar.gz
CMake-b479c6a8a98664c19eeaef40b71850c1f5315601.tar.bz2
ENH: add ability to have manifest files and incremental linking with make and nmake
-rw-r--r--Modules/CMakeVCManifest.cmake30
-rw-r--r--Modules/CMakeVCManifestExe.cmake30
-rw-r--r--Modules/Platform/Windows-cl.cmake18
-rw-r--r--Source/cmMakefile.cxx6
-rw-r--r--Source/cmSystemTools.cxx52
-rw-r--r--Source/cmSystemTools.h10
-rw-r--r--Source/cmake.cxx273
-rw-r--r--Source/cmake.h15
8 files changed, 336 insertions, 98 deletions
diff --git a/Modules/CMakeVCManifest.cmake b/Modules/CMakeVCManifest.cmake
deleted file mode 100644
index 6675f99..0000000
--- a/Modules/CMakeVCManifest.cmake
+++ /dev/null
@@ -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")
diff --git a/Modules/CMakeVCManifestExe.cmake b/Modules/CMakeVCManifestExe.cmake
deleted file mode 100644
index 5066bc8..0000000
--- a/Modules/CMakeVCManifestExe.cmake
+++ /dev/null
@@ -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")
diff --git a/Modules/Platform/Windows-cl.cmake b/Modules/Platform/Windows-cl.cmake
index c9b2822..74ce386 100644
--- a/Modules/Platform/Windows-cl.cmake
+++ b/Modules/Platform/Windows-cl.cmake
@@ -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")
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index c5df4fb..306c00b 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -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);
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 6829bdc..b489c48 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -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)
-{
- if(s_DisableRunCommandOutput)
- {
- verbose = false;
- }
-
- std::vector<cmStdString> args = cmSystemTools::ParseArguments(command);
- if(args.size() < 1)
- {
- return false;
- }
-
+bool cmSystemTools::RunSingleCommand(std::vector<cmStdString>const& command,
+ std::string* output ,
+ int* retVal , const char* dir ,
+ bool verbose ,
+ double timeout )
+{
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,
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 44fcb3d..fcfbafd 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -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
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 45f013f..1317cc4 100644
--- a/Source/cmake.cxx
+++ b/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;
+}
diff --git a/Source/cmake.h b/Source/cmake.h
index 265fc0a..b43e770 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -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.