Hi, I have updated the patch for CMake 3.6.0 to fix https://gitlab.kitware.com/cmake/cmake/issues/16209 This patch introduces a new class cmFilePathUuid which is used in QtAutogen to generated unique filenames. The name generation algorithm is the same as in http://public.kitware.com/pipermail/cmake-developers/2016-July/029087.html but the paths initialization moved to the class constructor - which also accepts a cmMakefile pointer.
Please review. -Sebastian P.S. I'm just an enthusiastic amateur doing this in my spare time. Please apologize me if I can't reply immediately.
>From ccdfb4f132ed3a1ddbbff466d885b4f63a178a2e Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann <sebh...@xwmw.org> Date: Tue, 26 Jul 2016 16:39:12 +0200 Subject: [PATCH] QtAutogen fix for too deep nested directory generation. Instead of generating moc_* and qrc_* files in subdirectories that reflect their source's location in the source tree the files get generated solely in the TARGET_NAME_automoc.dir/ but get a Base64 encoded checksum suffix that was generated from their source path and a few more seed strings. --- Source/CMakeLists.txt | 1 + Source/cmFilePathUuid.cxx | 162 ++++++++++++++++++++++++++++++++ Source/cmFilePathUuid.h | 70 ++++++++++++++ Source/cmQtAutoGeneratorInitializer.cxx | 76 +++------------ Source/cmQtAutoGenerators.cxx | 123 +++++++++++------------- Source/cmQtAutoGenerators.h | 4 +- 6 files changed, 300 insertions(+), 136 deletions(-) create mode 100644 Source/cmFilePathUuid.cxx create mode 100644 Source/cmFilePathUuid.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 3223831..645542c 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -238,6 +238,7 @@ set(SRCS cmFileLockPool.h cmFileLockResult.cxx cmFileLockResult.h + cmFilePathUuid.cxx cmFileTimeComparison.cxx cmFileTimeComparison.h cmFortranLexer.cxx diff --git a/Source/cmFilePathUuid.cxx b/Source/cmFilePathUuid.cxx new file mode 100644 index 0000000..4ca99c7 --- /dev/null +++ b/Source/cmFilePathUuid.cxx @@ -0,0 +1,162 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2016 Sebastian Holtermann (sebh...@xwmw.org) + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmFilePathUuid.h" + +#include "cmCryptoHash.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmsys/Base64.h" + +cmFilePathUuid::cmFilePathUuid(cmMakefile* makefile) +{ + initParentDirs(makefile->GetCurrentSourceDirectory(), + makefile->GetCurrentBinaryDirectory(), + makefile->GetHomeDirectory(), + makefile->GetHomeOutputDirectory()); +} + +cmFilePathUuid::cmFilePathUuid(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir) +{ + initParentDirs(currentSrcDir, currentBinDir, projectSrcDir, projectBinDir); +} + +void cmFilePathUuid::initParentDirs(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir) +{ + parentDirs[0].first = cmsys::SystemTools::GetRealPath(currentSrcDir); + parentDirs[0].second = "CurrentSource"; + parentDirs[1].first = currentBinDir; + parentDirs[1].second = "CurrentBinary"; + parentDirs[2].first = projectSrcDir; + parentDirs[2].second = "ProjectSource"; + parentDirs[3].first = projectBinDir; + parentDirs[3].second = "ProjectBinary"; +} + +std::string cmFilePathUuid::get(const std::string& filePath, + const char* outputPrefix, + const char* outputSuffix) +{ + std::string sourceFilename = cmsys::SystemTools::GetFilenameName(filePath); + std::string sourceBasename = + cmsys::SystemTools::GetFilenameWithoutLastExtension(sourceFilename); + + // Acquire checksum string + std::string checksum; + { + std::string sourceRelPath; + std::string sourceRelSeed; + GetRelPathSeed(filePath, sourceRelPath, sourceRelSeed); + checksum = GetChecksumString(sourceFilename, sourceRelPath, sourceRelSeed); + } + + // Compose the file name + std::string uuid; + { + const size_t nameLength(14); + const size_t checkSumLength(14); + if (outputPrefix) { + uuid += outputPrefix; + } + uuid += sourceBasename.substr(0, nameLength); + uuid += "_"; + uuid += checksum.substr(0, checkSumLength); + if (outputSuffix) { + uuid += outputSuffix; + } + } + return uuid; +} + +void cmFilePathUuid::GetRelPathSeed(const std::string& filePath, + std::string& sourceRelPath, + std::string& sourceRelSeed) +{ + const std::string sourceNameReal = cmsys::SystemTools::GetRealPath(filePath); + std::string parentDirectory; + // Find closest project parent directory + // Note: + // Using relative paths to project directories guarantees + // Uuid reproducibility on different build/source locations. + for (size_t ii = 0; ii != numParentDirs; ++ii) { + const std::string& pDir = parentDirs[ii].first; + if (!pDir.empty() && + cmsys::SystemTools::IsSubDirectory(sourceNameReal, pDir)) { + sourceRelSeed = parentDirs[ii].second; + parentDirectory = pDir; + break; + } + } + // Check if the file path is below a known project directory + if (parentDirectory.empty()) { + // Use file syste root as fallback parent directory + sourceRelSeed = "FileSystemRoot"; + cmsys::SystemTools::SplitPathRootComponent(sourceNameReal, + &parentDirectory); + } + sourceRelPath = cmsys::SystemTools::RelativePath( + parentDirectory, cmsys::SystemTools::GetParentDirectory(sourceNameReal)); +} + +std::string cmFilePathUuid::GetChecksumString( + const std::string& sourceFilename, const std::string& sourceRelPath, + const std::string& sourceRelSeed) +{ + // Calculate the file ( seed + relative path + name ) checksum + std::string checksumBase64; + + std::vector<unsigned char> hashBytes; + { + // Acquire hash in a hex value string + std::string hexHash = cmCryptoHash::New("SHA256")->HashString( + (sourceRelSeed + sourceRelPath + sourceFilename).c_str()); + // Convert hex value string to bytes + hashBytes.resize(hexHash.size() / 2); + for (unsigned int ii = 0; ii != hashBytes.size(); ++ii) { + unsigned char hbyte[2] = { 0, 0 }; + for (unsigned int jj = 0; jj != 2; ++jj) { + unsigned char nibble = hexHash[ii * 2 + jj]; + if ('0' <= nibble && nibble <= '9') { + hbyte[jj] = nibble - '0'; + } else if ('a' <= nibble && nibble <= 'f') { + hbyte[jj] = nibble - 'a' + 10; + } else if ('A' <= nibble && nibble <= 'f') { + hbyte[jj] = nibble - 'A' + 10; + } else { + // Unexpected non hex character + std::cerr << "Unexpected non hex character in checksum string"; + exit(-1); + } + } + hashBytes[ii] = hbyte[1] | (hbyte[0] << 4); + } + } + // Convert hash bytes to Base64 text string + { + std::vector<unsigned char> base64Bytes(hashBytes.size() * 2, 0); + cmsysBase64_Encode(&hashBytes[0], hashBytes.size(), &base64Bytes[0], 0); + checksumBase64 = reinterpret_cast<const char*>(&base64Bytes[0]); + // Base64 allows '+' and '/' characters. + // Both are problematic when used in file names. + // Replace them with safer alternatives. + std::replace(checksumBase64.begin(), checksumBase64.end(), '+', '_'); + std::replace(checksumBase64.begin(), checksumBase64.end(), '/', '-'); + } + + return checksumBase64; +} diff --git a/Source/cmFilePathUuid.h b/Source/cmFilePathUuid.h new file mode 100644 index 0000000..df1a984 --- /dev/null +++ b/Source/cmFilePathUuid.h @@ -0,0 +1,70 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2016 Sebastian Holtermann (sebh...@xwmw.org) + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmFilePathUuid_h +#define cmFilePathUuid_h + +#include "cmStandardIncludes.h" + +#include <string> +#include <utility> + +class cmMakefile; + +/** \class cmFilePathUuid + * @brief Generates a unique pathless file name with a checksum component + * calculated from the file path. + * + * The checksum is calculated from the relative file path to the + * closest known project directory. This guarantees reproducibility + * when source and build directory differ e.g. for different project + * build directories. + */ +class cmFilePathUuid +{ +public: + // Initilizes the parent directories from a makefile + cmFilePathUuid(cmMakefile* makefile); + + // Initilizes the parent directories manually + cmFilePathUuid(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir); + + /* @brief Calculates and returns the uuid for a file path + * + * @arg outputPrefix optional string to prepend to the result + * @arg outputSuffix optional string to append to the result + */ + std::string get(const std::string& filePath, const char* outputPrefix = NULL, + const char* outputSuffix = NULL); + +private: + void initParentDirs(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir); + + void GetRelPathSeed(const std::string& filePath, std::string& sourceRelPath, + std::string& sourceRelSeed); + + std::string GetChecksumString(const std::string& sourceFilename, + const std::string& sourceRelPath, + const std::string& sourceRelSeed); + + static const size_t numParentDirs = 4; + // List of (directory name, seed name) pairs + std::pair<std::string, std::string> parentDirs[numParentDirs]; +}; + +#endif diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index dd19760..e504314 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -13,6 +13,7 @@ #include "cmQtAutoGeneratorInitializer.h" +#include "cmFilePathUuid.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmSourceFile.h" @@ -53,51 +54,6 @@ static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target) return targetDir; } -static std::string GetSourceRelativePath(cmGeneratorTarget const* target, - const std::string& fileName) -{ - std::string pathRel; - // Test if the file is child to any of the known directories - { - const std::string fileNameReal = cmsys::SystemTools::GetRealPath(fileName); - std::string parentDirectory; - bool match(false); - { - std::string testDirs[4]; - { - cmMakefile* makefile = target->Target->GetMakefile(); - testDirs[0] = makefile->GetCurrentSourceDirectory(); - testDirs[1] = makefile->GetCurrentBinaryDirectory(); - testDirs[2] = makefile->GetHomeDirectory(); - testDirs[3] = makefile->GetHomeOutputDirectory(); - } - for (int ii = 0; ii != sizeof(testDirs) / sizeof(std::string); ++ii) { - const ::std::string testDir = - cmsys::SystemTools::GetRealPath(testDirs[ii]); - if (!testDir.empty() && - cmsys::SystemTools::IsSubDirectory(fileNameReal, testDir)) { - parentDirectory = testDir; - match = true; - break; - } - } - } - // Use root as fallback parent directory - if (!match) { - cmsys::SystemTools::SplitPathRootComponent(fileNameReal, - &parentDirectory); - } - pathRel = cmsys::SystemTools::RelativePath( - parentDirectory, cmsys::SystemTools::GetParentDirectory(fileNameReal)); - } - // Sanitize relative path - if (!pathRel.empty()) { - pathRel += '/'; - cmSystemTools::ReplaceString(pathRel, "..", "__"); - } - return pathRel; -} - static void SetupSourceFiles(cmGeneratorTarget const* target, std::vector<std::string>& skipMoc, std::vector<std::string>& mocSources, @@ -111,6 +67,7 @@ static void SetupSourceFiles(cmGeneratorTarget const* target, std::vector<std::string> newRccFiles; + cmFilePathUuid fpathUuid(makefile); for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; @@ -129,15 +86,11 @@ static void SetupSourceFiles(cmGeneratorTarget const* target, if (ext == "qrc" && !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) { - std::string rcc_output_dir = GetAutogenTargetBuildDir(target); - rcc_output_dir += GetSourceRelativePath(target, absFile); - cmSystemTools::MakeDirectory(rcc_output_dir.c_str()); + std::string rcc_output_file = GetAutogenTargetBuildDir(target); + // Create output directory + cmSystemTools::MakeDirectory(rcc_output_file.c_str()); + rcc_output_file += fpathUuid.get(absFile, "qrc_", ".cpp"); - std::string basename = - cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile); - - std::string rcc_output_file = rcc_output_dir; - rcc_output_file += "qrc_" + basename + ".cpp"; makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", rcc_output_file.c_str(), false); makefile->GetOrCreateSource(rcc_output_file, true); @@ -793,6 +746,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( ) { std::vector<cmSourceFile*> srcFiles; target->GetConfigCommonSourceFiles(srcFiles); + cmFilePathUuid fpathUuid(makefile); for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; @@ -804,17 +758,11 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( if (ext == "qrc" && !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) { - { - std::string rcc_output_dir = GetAutogenTargetBuildDir(target); - rcc_output_dir += GetSourceRelativePath(target, absFile); - cmSystemTools::MakeDirectory(rcc_output_dir.c_str()); - - std::string basename = - cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile); - std::string rcc_output_file = rcc_output_dir; - rcc_output_file += "qrc_" + basename + ".cpp"; - rcc_output.push_back(rcc_output_file); - } + std::string rcc_output_file = GetAutogenTargetBuildDir(target); + // Create output directory + cmSystemTools::MakeDirectory(rcc_output_file.c_str()); + rcc_output_file += fpathUuid.get(absFile, "qrc_", ".cpp"); + rcc_output.push_back(rcc_output_file); if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { if (qtMajorVersion == "5") { diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index ac64397..9d9735a 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -14,6 +14,7 @@ #include "cmQtAutoGenerators.h" #include "cmAlgorithms.h" +#include "cmFilePathUuid.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" @@ -87,6 +88,23 @@ static std::string extractSubDir(const std::string& absPath, return subDir; } +static bool FileNameIsUnique(const std::string& filePath, + const std::map<std::string, std::string>& fileMap) +{ + size_t count(0); + const std::string fileName = cmsys::SystemTools::GetFilenameName(filePath); + for (std::map<std::string, std::string>::const_iterator si = fileMap.begin(); + si != fileMap.end(); ++si) { + if (cmsys::SystemTools::GetFilenameName(si->first) == fileName) { + ++count; + if (count > 1) { + return false; + } + } + } + return true; +} + cmQtAutoGenerators::cmQtAutoGenerators() : Verbose(cmsys::SystemTools::GetEnv("VERBOSE") != 0) , ColorOutput(true) @@ -935,6 +953,8 @@ void cmQtAutoGenerators::ParseHeaders( std::map<std::string, std::string>& notIncludedMocs, std::map<std::string, std::vector<std::string> >& includedUis) { + cmFilePathUuid fpathUuid(this->Srcdir, this->Builddir, + this->ProjectSourceDir, this->ProjectBinaryDir); for (std::set<std::string>::const_iterator hIt = absHeaders.begin(); hIt != absHeaders.end(); ++hIt) { const std::string& headerName = *hIt; @@ -950,12 +970,8 @@ void cmQtAutoGenerators::ParseHeaders( std::string macroName; if (requiresMocing(contents, macroName)) { - const std::string parentDir = - this->TargetBuildSubDir + this->SourceRelativePath(headerName); - const std::string basename = - cmsys::SystemTools::GetFilenameWithoutLastExtension(headerName); - const std::string currentMoc = parentDir + "moc_" + basename + ".cpp"; - notIncludedMocs[headerName] = currentMoc; + notIncludedMocs[headerName] = + this->TargetBuildSubDir + fpathUuid.get(headerName, "moc_", ".cpp"); } } this->ParseForUic(headerName, contents, includedUis); @@ -1022,7 +1038,9 @@ bool cmQtAutoGenerators::GenerateMocFiles( for (std::map<std::string, std::string>::const_iterator it = notIncludedMocs.begin(); it != notIncludedMocs.end(); ++it) { - outStream << "#include \"" << it->second << "\"\n"; + outStream << "#include \"" + << cmsys::SystemTools::ConvertToOutputPath(it->second) + << "\"\n"; } } outStream.flush(); @@ -1186,7 +1204,7 @@ bool cmQtAutoGenerators::GenerateUi(const std::string& realName, cmsys::SystemTools::MakeDirectory(this->Builddir.c_str()); } - const ::std::string uiBuildFile = this->Builddir + uiOutputFile; + const std::string uiBuildFile = this->Builddir + uiOutputFile; int sourceNewerThanUi = 0; bool success = cmsys::SystemTools::FileTimeCompare(uiInputFile, uiBuildFile, @@ -1258,17 +1276,18 @@ bool cmQtAutoGenerators::GenerateQrcFiles() { // generate single map with input / output names std::map<std::string, std::string> qrcGenMap; - for (std::vector<std::string>::const_iterator si = this->RccSources.begin(); - si != this->RccSources.end(); ++si) { - const std::string ext = cmsys::SystemTools::GetFilenameLastExtension(*si); - if (ext == ".qrc") { - std::string basename = - cmsys::SystemTools::GetFilenameWithoutLastExtension(*si); - std::string qrcOutputFile = this->TargetBuildSubDir + - this->SourceRelativePath(*si) + "qrc_" + basename + ".cpp"; - // std::string qrcOutputFile = "CMakeFiles/" + this->OriginTargetName - // + ".dir/qrc_" + basename + ".cpp"; - qrcGenMap[*si] = qrcOutputFile; + { + cmFilePathUuid fpathUuid(this->Srcdir, this->Builddir, + this->ProjectSourceDir, this->ProjectBinaryDir); + for (std::vector<std::string>::const_iterator si = + this->RccSources.begin(); + si != this->RccSources.end(); ++si) { + const std::string ext = + cmsys::SystemTools::GetFilenameLastExtension(*si); + if (ext == ".qrc") { + qrcGenMap[*si] = + (this->TargetBuildSubDir + fpathUuid.get(*si, "qrc_", ".cpp")); + } } } @@ -1290,7 +1309,8 @@ bool cmQtAutoGenerators::GenerateQrcFiles() for (std::map<std::string, std::string>::const_iterator si = qrcGenMap.begin(); si != qrcGenMap.end(); ++si) { - if (!this->GenerateQrc(si->first, si->second)) { + if (!this->GenerateQrc(si->first, si->second, + FileNameIsUnique(si->first, qrcGenMap))) { if (this->RunRccFailed) { return false; } @@ -1300,13 +1320,21 @@ bool cmQtAutoGenerators::GenerateQrcFiles() } bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile, - const std::string& qrcOutputFile) + const std::string& qrcOutputFile, + bool unique_n) { - std::string relName = this->SourceRelativePath(qrcInputFile); - std::replace(relName.begin(), relName.end(), '/', '_'); - relName += cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcInputFile); + std::string symbolName; + if (unique_n) { + symbolName = + cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcInputFile); + } else { + symbolName = + cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcOutputFile); + // Remove "qrc_" at string begin + symbolName.erase(0, 4); + } - const ::std::string qrcBuildFile = this->Builddir + qrcOutputFile; + const std::string qrcBuildFile = this->Builddir + qrcOutputFile; int sourceNewerThanQrc = 0; bool generateQrc = !cmsys::SystemTools::FileTimeCompare( @@ -1332,7 +1360,7 @@ bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile, } command.push_back("-name"); - command.push_back(relName); + command.push_back(symbolName); command.push_back("-o"); command.push_back(qrcBuildFile); command.push_back(qrcInputFile); @@ -1357,49 +1385,6 @@ bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile, return true; } -std::string cmQtAutoGenerators::SourceRelativePath(const std::string& filename) -{ - std::string pathRel; - - // Test if the file is child to any of the known directories - { - std::string fileNameReal = cmsys::SystemTools::GetRealPath(filename); - std::string parentDirectory; - bool match(false); - { - const ::std::string* testDirs[4]; - testDirs[0] = &(this->Srcdir); - testDirs[1] = &(this->Builddir); - testDirs[2] = &(this->ProjectSourceDir); - testDirs[3] = &(this->ProjectBinaryDir); - for (int ii = 0; ii != sizeof(testDirs) / sizeof(const ::std::string*); - ++ii) { - const ::std::string testDir = - cmsys::SystemTools::GetRealPath(*(testDirs[ii])); - if (cmsys::SystemTools::IsSubDirectory(fileNameReal, testDir)) { - parentDirectory = testDir; - match = true; - break; - } - } - } - // Use root as fallback parent directory - if (!match) { - cmsys::SystemTools::SplitPathRootComponent(fileNameReal, - &parentDirectory); - } - pathRel = cmsys::SystemTools::RelativePath( - parentDirectory, cmsys::SystemTools::GetParentDirectory(fileNameReal)); - } - - // Sanitize relative path - if (!pathRel.empty()) { - pathRel += '/'; - cmSystemTools::ReplaceString(pathRel, "..", "__"); - } - return pathRel; -} - /** * @brief Collects name collisions as output/input pairs * @return True if there were collisions diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index 5e7fab5..8f2b45e 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -51,7 +51,7 @@ private: const std::string& uiOutputFile); bool GenerateQrcFiles(); bool GenerateQrc(const std::string& qrcInputFile, - const std::string& qrcOutputFile); + const std::string& qrcOutputFile, bool unique_n); void ParseCppFile( const std::string& absFilename, const std::vector<std::string>& headerExtensions, @@ -83,8 +83,6 @@ private: void Init(); - std::string SourceRelativePath(const std::string& filename); - bool NameCollisionTest(const std::map<std::string, std::string>& genFiles, std::multimap<std::string, std::string>& collisions); void NameCollisionLog( -- 2.8.1
-- Powered by www.kitware.com Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ Kitware offers various services to support the CMake community. For more information on each offering, please visit: CMake Support: http://cmake.org/cmake/help/support.html CMake Consulting: http://cmake.org/cmake/help/consulting.html CMake Training Courses: http://cmake.org/cmake/help/training.html Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html Follow this link to subscribe/unsubscribe: http://public.kitware.com/mailman/listinfo/cmake-developers