diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index e20bfe105..71d3c51b9 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -28,6 +28,11 @@ #include "cmTest.h" #include "cmake.h" +#if defined(CMAKE_BUILD_WITH_CMAKE) +# define CM_LG_ENCODE_OBJECT_NAMES +# include +#endif + #include #include // for isalpha @@ -2376,8 +2381,81 @@ cmLocalGenerator } } +#if defined(CM_LG_ENCODE_OBJECT_NAMES) +static std::string cmLocalGeneratorMD5(const char* input) +{ + char md5out[32]; + cmsysMD5* md5 = cmsysMD5_New(); + cmsysMD5_Initialize(md5); + cmsysMD5_Append(md5, reinterpret_cast(input), -1); + cmsysMD5_FinalizeHex(md5, md5out); + cmsysMD5_Delete(md5); + return std::string(md5out, 32); +} + +static bool +cmLocalGeneratorShortenObjectName(std::string& objName, + std::string::size_type max_len) +{ + // Replace the beginning of the path portion of the object name with + // its own md5 sum. + std::string::size_type pos = objName.find('/', objName.size()-max_len+32); + if(pos != objName.npos) + { + std::string md5name = cmLocalGeneratorMD5(objName.substr(0, pos).c_str()); + md5name += objName.substr(pos); + objName = md5name; + + // The object name is now short enough. + return true; + } + else + { + // The object name could not be shortened enough. + return false; + } +} + +static bool cmLocalGeneratorCheckObjectName(std::string& objName, + std::string::size_type dir_len) +{ + // Choose a maximum file name length. +#if defined(_WIN32) || defined(__CYGWIN__) + std::string::size_type const max_total_len = 250; +#else + std::string::size_type const max_total_len = 1000; +#endif + + // Enforce the maximum file name length if possible. + std::string::size_type max_obj_len = max_total_len; + if(dir_len < max_total_len) + { + max_obj_len = max_total_len - dir_len; + if(objName.size() > max_obj_len) + { + // The current object file name is too long. Try to shorten it. + return cmLocalGeneratorShortenObjectName(objName, max_obj_len); + } + else + { + // The object file name is short enough. + return true; + } + } + else + { + // The build directory in which the object will be stored is + // already too deep. + return false; + } +} +#endif + //---------------------------------------------------------------------------- -std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName(const char* sin) +std::string& +cmLocalGenerator +::CreateSafeUniqueObjectFileName(const char* sin, + std::string::size_type dir_len) { // Look for an existing mapped name for this object file. std::map::iterator it = @@ -2435,6 +2513,12 @@ std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName(const char* sin) while ( !done ); } +#if defined(CM_LG_ENCODE_OBJECT_NAMES) + cmLocalGeneratorCheckObjectName(ssin, dir_len); +#else + (void)dir_len; +#endif + // Insert the newly mapped object file name. std::map::value_type e(sin, ssin); it = this->UniqueObjectNamesMap.insert(e).first; @@ -2446,7 +2530,9 @@ std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName(const char* sin) //---------------------------------------------------------------------------- std::string -cmLocalGenerator::GetObjectFileNameWithoutTarget(const cmSourceFile& source) +cmLocalGenerator +::GetObjectFileNameWithoutTarget(const cmSourceFile& source, + std::string::size_type dir_len) { // Construct the object file name using the full path to the source // file which is its only unique identification. @@ -2517,7 +2603,7 @@ cmLocalGenerator::GetObjectFileNameWithoutTarget(const cmSourceFile& source) } // Convert to a safe name. - return this->CreateSafeUniqueObjectFileName(objectName.c_str()); + return this->CreateSafeUniqueObjectFileName(objectName.c_str(), dir_len); } //---------------------------------------------------------------------------- diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index b0d0610ab..651988960 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -266,8 +266,10 @@ protected: std::vector const& configurationTypes); // Compute object file names. - std::string GetObjectFileNameWithoutTarget(const cmSourceFile& source); - std::string& CreateSafeUniqueObjectFileName(const char* sin); + std::string GetObjectFileNameWithoutTarget(const cmSourceFile& source, + std::string::size_type dir_len); + std::string& CreateSafeUniqueObjectFileName(const char* sin, + std::string::size_type dir_len); void ConfigureRelativePaths(); std::string FindRelativePathTopSource(); diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 419329387..5dc138489 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1610,25 +1610,22 @@ cmLocalUnixMakefileGenerator3 const cmSourceFile& source, std::string* nameWithoutTargetDir) { - // Get the object file name independent of target. - std::string objectName = this->GetObjectFileNameWithoutTarget(source); - if(nameWithoutTargetDir) - { - *nameWithoutTargetDir = objectName; - } - - // Prepend the target directory. - std::string obj; - const char* fileTargetDirectory = - source.GetProperty("MACOSX_PACKAGE_LOCATION"); - if ( fileTargetDirectory ) + if(const char* fileTargetDirectory = + source.GetProperty("MACOSX_PACKAGE_LOCATION")) { + // Special handling for OSX package files. + std::string objectName = this->GetObjectFileNameWithoutTarget(source, 0); + if(nameWithoutTargetDir) + { + *nameWithoutTargetDir = objectName; + } objectName = cmSystemTools::GetFilenameName(objectName.c_str()); std::string targetName; std::string targetNameReal; std::string targetNamePDB; target.GetExecutableNames(targetName, targetNameReal, targetNamePDB, this->ConfigurationName.c_str()); + std::string obj; if ( target.GetPropertyAsBool("MACOSX_BUNDLE") ) { // Construct the full path version of the names. @@ -1644,14 +1641,32 @@ cmLocalUnixMakefileGenerator3 } obj = cmSystemTools::RelativePath (this->Makefile->GetHomeOutputDirectory(), obj.c_str()); + obj += "/"; + obj += objectName; + return obj; } else { - obj = this->GetTargetDirectory(target); + // Start with the target directory. + std::string obj = this->GetTargetDirectory(target); + obj += "/"; + + // Get the object file name without the target directory. + std::string::size_type dir_len = 0; + dir_len += strlen(this->Makefile->GetCurrentOutputDirectory()); + dir_len += 1; + dir_len += obj.size(); + std::string objectName = + this->GetObjectFileNameWithoutTarget(source, dir_len); + if(nameWithoutTargetDir) + { + *nameWithoutTargetDir = objectName; + } + + // Append the object name to the target directory. + obj += objectName; + return obj; } - obj += "/"; - obj += objectName; - return obj; } //---------------------------------------------------------------------------- diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx index 6a24b0176..3a72de699 100644 --- a/Source/cmLocalVisualStudio6Generator.cxx +++ b/Source/cmLocalVisualStudio6Generator.cxx @@ -397,7 +397,32 @@ void cmLocalVisualStudio6Generator { this->WriteDSPBeginGroup(fout, name.c_str(), ""); } - + + // Compute the maximum length of a configuration name. + std::string::size_type config_len_max = 0; + for(std::vector::iterator i = this->Configurations.begin(); + i != this->Configurations.end(); ++i) + { + // Strip the subdirectory name out of the configuration name. + std::string config = *i; + std::string::size_type pos = config.find_last_of(" "); + config = config.substr(pos+1, std::string::npos); + config = config.substr(0, config.size()-1); + if(config.size() > config_len_max) + { + config_len_max = config.size(); + } + } + + // Compute the maximum length of the full path to the intermediate + // files directory for any configuration. This is used to construct + // object file names that do not produce paths that are too long. + std::string::size_type dir_len = 0; + dir_len += strlen(this->Makefile->GetCurrentOutputDirectory()); + dir_len += 1; + dir_len += config_len_max; + dir_len += 1; + // Loop through each source in the source group. for(std::vector::const_iterator sf = sourceFiles.begin(); sf != sourceFiles.end(); ++sf) @@ -412,7 +437,7 @@ void cmLocalVisualStudio6Generator { objectNameDir = cmSystemTools::GetFilenamePath( - this->GetObjectFileNameWithoutTarget(*(*sf))); + this->GetObjectFileNameWithoutTarget(*(*sf), dir_len)); } // Add per-source file flags. diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 100d79159..e627f0d6a 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -1055,6 +1055,28 @@ void cmLocalVisualStudio7Generator this->WriteVCProjBeginGroup(fout, name.c_str(), ""); } + // Compute the maximum length of a configuration name. + std::string::size_type config_len_max = 0; + for(std::vector::iterator i = configs->begin(); + i != configs->end(); ++i) + { + if(i->size() > config_len_max) + { + config_len_max = i->size(); + } + } + + // Compute the maximum length of the full path to the intermediate + // files directory for any configuration. This is used to construct + // object file names that do not produce paths that are too long. + std::string::size_type dir_len = 0; + dir_len += strlen(this->Makefile->GetCurrentOutputDirectory()); + dir_len += 1; + dir_len += this->GetTargetDirectory(target).size(); + dir_len += 1; + dir_len += config_len_max; + dir_len += 1; + // Loop through each source in the source group. std::string objectName; for(std::vector::const_iterator sf = @@ -1066,7 +1088,7 @@ void cmLocalVisualStudio7Generator std::string additionalDeps; if(this->NeedObjectName.find(*sf) != this->NeedObjectName.end()) { - objectName = this->GetObjectFileNameWithoutTarget(*(*sf)); + objectName = this->GetObjectFileNameWithoutTarget(*(*sf), dir_len); } else { diff --git a/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt b/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt index bbdaa557e..4daf42586 100644 --- a/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt +++ b/Tests/OutOfSource/OutOfSourceSubdir/CMakeLists.txt @@ -2,8 +2,26 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) IF ("${PROJECT_SOURCE_DIR}" STREQUAL "${ANOTHER_PROJ_SOURCE_DIR}") SET(BUILD_SHARED_LIBS 1) + + # Construct a source file outside the tree whose full path is close to + # the path length limit. This will cause the full path to the object + # file in the build tree to exceed the maximum path length which will + # test cmLocalGenerator::CreateSafeUniqueObjectFileName. + GET_FILENAME_COMPONENT(DEEPDIR + ${OutOfSource_BINARY_DIR}/../OutOfSourceDeep/deeper ABSOLUTE) + # MAXPATH = 250 less 25 for /and/deeper/simple.cxx part and small safety + MATH(EXPR MAXPATH "250 - 25") + STRING(LENGTH "${DEEPDIR}" DEEPDIR_LEN) + WHILE("${DEEPDIR_LEN}" LESS "${MAXPATH}") + SET(DEEPDIR ${DEEPDIR}/and/deeper) + STRING(LENGTH "${DEEPDIR}" DEEPDIR_LEN) + ENDWHILE("${DEEPDIR_LEN}" LESS "${MAXPATH}") + SET(DEEPSRC ${DEEPDIR}/simple.cxx) + STRING(LENGTH "${DEEPSRC}" DEEPSRC_LEN) + CONFIGURE_FILE(simple.cxx.in ${DEEPSRC} COPYONLY) + ADD_LIBRARY(testlib testlib.cxx) - ADD_EXECUTABLE (simple simple.cxx ../simple.cxx) + ADD_EXECUTABLE (simple simple.cxx ../simple.cxx ${DEEPSRC}) TARGET_LINK_LIBRARIES(simple testlib outlib) ENDIF ("${PROJECT_SOURCE_DIR}" STREQUAL "${ANOTHER_PROJ_SOURCE_DIR}") diff --git a/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx b/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx index 124b7f98e..0be71951d 100644 --- a/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx +++ b/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx @@ -5,6 +5,7 @@ #include "testdp.h" extern int simple(); +extern int simple2(); extern "C" int outlib(); int main () @@ -26,5 +27,9 @@ int main () { return -4; } + if(simple2() != 789) + { + return -5; + } return 0; } diff --git a/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx.in b/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx.in new file mode 100644 index 000000000..8339b7c6e --- /dev/null +++ b/Tests/OutOfSource/OutOfSourceSubdir/simple.cxx.in @@ -0,0 +1 @@ +int simple2() { return 789; }