Am 27.07.2016 um 17:49 schrieb Sebastian Holtermann:
Doing so I found that Base64 allows '+' and '/' as characters which is
bad for directory names obviously.
For now these characters get replaced with 'A' and 'B'.
'_' and '@' would be better replacements (with comments why they are
used) since 'A' and 'B' are already characters in Base64.
Some quick research (aka googling) revealed
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_278
It seems '_' and '-' are safer choices.
+ // unexpected hexchar
This raise an internal error, not just be a comment.
Is there an error exit function in CMake?
Or just exit(-1);
+ }
+ }
+ hashBytes[ii] = hbyte[0] | (hbyte[1] << 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. Replace these.
This comment should indicate it is because the string is used as part of
a path and that these characters tend to cause problems in paths.
Ok.
The patch.
-Sebastian
>From 30d0c7b61a2ffbc9004cd32a88330078ec8d75a4 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/cmQtAutoGeneratorInitializer.cxx | 79 +++---------
Source/cmQtAutoGenerators.cxx | 215 +++++++++++++++++++++++---------
Source/cmQtAutoGenerators.h | 21 +++-
3 files changed, 192 insertions(+), 123 deletions(-)
diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx
index dd19760..6bbe29c 100644
--- a/Source/cmQtAutoGeneratorInitializer.cxx
+++ b/Source/cmQtAutoGeneratorInitializer.cxx
@@ -12,6 +12,7 @@
============================================================================*/
#include "cmQtAutoGeneratorInitializer.h"
+#include "cmQtAutoGenerators.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
@@ -53,49 +54,24 @@ static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target)
return targetDir;
}
-static std::string GetSourceRelativePath(cmGeneratorTarget const* target,
- const std::string& fileName)
+static std::string GetQrcBuildPath(cmGeneratorTarget const* target,
+ const std::string& qrcSourceName)
{
- std::string pathRel;
- // Test if the file is child to any of the known directories
+ std::string rccOutputPath = GetAutogenTargetBuildDir(target);
+ // Create output directory
+ cmSystemTools::MakeDirectory(rccOutputPath.c_str());
+
+ rccOutputPath += "qrc_";
{
- 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));
+ cmMakefile* makefile = target->Target->GetMakefile();
+ rccOutputPath += cmQtAutoGeneratorUtil::BuildFileBase(
+ qrcSourceName, makefile->GetCurrentSourceDirectory(),
+ makefile->GetCurrentBinaryDirectory(), makefile->GetHomeDirectory(),
+ makefile->GetHomeOutputDirectory());
}
- // Sanitize relative path
- if (!pathRel.empty()) {
- pathRel += '/';
- cmSystemTools::ReplaceString(pathRel, "..", "__");
- }
- return pathRel;
+ rccOutputPath += ".cpp";
+
+ return rccOutputPath;
}
static void SetupSourceFiles(cmGeneratorTarget const* target,
@@ -129,15 +105,7 @@ 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 basename =
- cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile);
-
- std::string rcc_output_file = rcc_output_dir;
- rcc_output_file += "qrc_" + basename + ".cpp";
+ const std::string rcc_output_file = GetQrcBuildPath(target, absFile);
makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES",
rcc_output_file.c_str(), false);
makefile->GetOrCreateSource(rcc_output_file, true);
@@ -804,17 +772,8 @@ 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);
- }
+ const std::string rcc_output_file = GetQrcBuildPath(target, absFile);
+ 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..af69535 100644
--- a/Source/cmQtAutoGenerators.cxx
+++ b/Source/cmQtAutoGenerators.cxx
@@ -20,6 +20,9 @@
#include "cmState.h"
#include "cmSystemTools.h"
+#include "cmCryptoHash.h"
+#include "cmsys/Base64.h"
+
#include <sys/stat.h>
#include <assert.h>
@@ -31,6 +34,107 @@
#include <unistd.h>
#endif
+std::string cmQtAutoGeneratorUtil::BuildFileBase(
+ const std::string& filePath, const std::string& parenDirCSource,
+ const std::string& parenDirCBinary, const std::string& parenDirPSource,
+ const std::string& parenDirPBinary)
+{
+ std::string baseName;
+
+ std::string sourceFileName = cmsys::SystemTools::GetFilenameName(filePath);
+ std::string sourceBaseName =
+ cmsys::SystemTools::GetFilenameWithoutLastExtension(sourceFileName);
+
+ // Determine the relative path to a known parent directory
+ std::string sourceRelPath;
+ std::string sourceRelSeed;
+ {
+ std::string sourceNameReal = cmsys::SystemTools::GetRealPath(filePath);
+ std::string parentDirectory;
+ bool parentFound(false);
+ {
+ struct TPair
+ {
+ const std::string* dir;
+ const char* seed;
+ };
+ TPair pDirs[] = { { &parenDirCSource, "CurrentSource" },
+ { &parenDirCBinary, "CurrentBinary" },
+ { &parenDirPSource, "ProjectSource" },
+ { &parenDirPBinary, "ProjectBinary" },
+ { 0, 0 } };
+ for (TPair* itc = &pDirs[0]; itc->dir != 0; ++itc) {
+ const std::string pDir = cmsys::SystemTools::GetRealPath(*(itc->dir));
+ if (cmsys::SystemTools::IsSubDirectory(sourceNameReal, pDir)) {
+ sourceRelSeed = itc->seed;
+ parentDirectory = pDir;
+ parentFound = true;
+ break;
+ }
+ }
+ }
+ // Use root as fallback parent directory
+ if (!parentFound) {
+ sourceRelSeed = "FileSystemRoot";
+ cmsys::SystemTools::SplitPathRootComponent(sourceNameReal,
+ &parentDirectory);
+ }
+ sourceRelPath = cmsys::SystemTools::RelativePath(
+ parentDirectory, cmsys::SystemTools::GetParentDirectory(sourceNameReal));
+ }
+
+ // Calculate the file ( seed + relative path + name ) checksum
+ std::string checksumBase64;
+ {
+ std::vector<unsigned char> hashBytes;
+ {
+ // Acquire hex value hash 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 {
+ // Unexpected non hex character
+ std::cerr << "Unexpected non hex character in checksum string";
+ exit(-1);
+ }
+ }
+ hashBytes[ii] = hbyte[0] | (hbyte[1] << 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(), '/', '-');
+ }
+ }
+
+ // Compose the file name
+ {
+ const size_t nameLength(14);
+ const size_t checkSumLength(14);
+ baseName.append(sourceBaseName.substr(0, nameLength));
+ baseName.append("_");
+ baseName.append(checksumBase64.substr(0, checkSumLength));
+ }
+
+ return baseName;
+}
+
static bool requiresMocing(const std::string& text, std::string& macroName)
{
// this simple check is much much faster than the regexp
@@ -87,6 +191,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)
@@ -950,12 +1071,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 + BuildFileName(headerName, "moc_", ".cpp");
}
}
this->ParseForUic(headerName, contents, includedUis);
@@ -1022,7 +1139,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 +1305,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,
@@ -1262,13 +1381,8 @@ bool cmQtAutoGenerators::GenerateQrcFiles()
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;
+ qrcGenMap[*si] =
+ (this->TargetBuildSubDir + this->BuildFileName(*si, "qrc_", ".cpp"));
}
}
@@ -1290,7 +1404,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 +1415,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 +1455,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,47 +1480,17 @@ bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile,
return true;
}
-std::string cmQtAutoGenerators::SourceRelativePath(const std::string& filename)
+std::string cmQtAutoGenerators::BuildFileName(const std::string& filename,
+ const std::string& prefix,
+ const std::string& suffix)
{
- std::string pathRel;
+ std::string buildName(prefix);
+ buildName += cmQtAutoGeneratorUtil::BuildFileBase(
+ filename, this->Srcdir, this->Builddir, this->ProjectSourceDir,
+ this->ProjectBinaryDir);
+ buildName += suffix;
- // 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;
+ return buildName;
}
/**
diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h
index 5e7fab5..2d1d304 100644
--- a/Source/cmQtAutoGenerators.h
+++ b/Source/cmQtAutoGenerators.h
@@ -23,6 +23,21 @@
class cmMakefile;
+/// Class with static utility functions
+///
+class cmQtAutoGeneratorUtil
+{
+public:
+ // Returns a file name (without directory) that consists in part
+ // of the source file name and in part of a Base64 encoded checksum
+ // of the source file path
+ static std::string BuildFileBase(const std::string& filePath,
+ const std::string& parenDirCSource,
+ const std::string& parenDirCBinary,
+ const std::string& parenDirPSource,
+ const std::string& parenDirPBinary);
+};
+
class cmQtAutoGenerators
{
public:
@@ -51,7 +66,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,7 +98,9 @@ private:
void Init();
- std::string SourceRelativePath(const std::string& filename);
+ std::string BuildFileName(const std::string& filename,
+ const std::string& prefix,
+ const std::string& suffix);
bool NameCollisionTest(const std::map<std::string, std::string>& genFiles,
std::multimap<std::string, std::string>& collisions);
--
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