diff options
Diffstat (limited to 'Source')
25 files changed, 1002 insertions, 792 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 5b381b4..d15fdbe 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -243,8 +243,8 @@ set(SRCS cmFileLockPool.h cmFileLockResult.cxx cmFileLockResult.h - cmFilePathUuid.cxx - cmFilePathUuid.h + cmFilePathChecksum.cxx + cmFilePathChecksum.h cmFileTimeComparison.cxx cmFileTimeComparison.h cmFortranLexer.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index d5f5724..f1fca76 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 7) -set(CMake_VERSION_PATCH 20161202) +set(CMake_VERSION_PATCH 20161207) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index ab43dbc..68f9a54 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -797,8 +797,9 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const // Prepare some maps to help us find setup and cleanup tests for // any given fixture - typedef std::set<ListOfTests::const_iterator> TestIteratorSet; - typedef std::map<std::string, TestIteratorSet> FixtureDependencies; + typedef ListOfTests::const_iterator TestIterator; + typedef std::multimap<std::string, TestIterator> FixtureDependencies; + typedef FixtureDependencies::const_iterator FixtureDepsIterator; FixtureDependencies fixtureSetups; FixtureDependencies fixtureDeps; @@ -809,14 +810,14 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const const std::set<std::string>& setups = p.FixturesSetup; for (std::set<std::string>::const_iterator depsIt = setups.begin(); depsIt != setups.end(); ++depsIt) { - fixtureSetups[*depsIt].insert(it); - fixtureDeps[*depsIt].insert(it); + fixtureSetups.insert(std::make_pair(*depsIt, it)); + fixtureDeps.insert(std::make_pair(*depsIt, it)); } const std::set<std::string>& cleanups = p.FixturesCleanup; for (std::set<std::string>::const_iterator depsIt = cleanups.begin(); depsIt != cleanups.end(); ++depsIt) { - fixtureDeps[*depsIt].insert(it); + fixtureDeps.insert(std::make_pair(*depsIt, it)); } } @@ -859,17 +860,15 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const // associated with the required fixture. If any of those setup // tests fail, this test should not run. We make the fixture's // cleanup tests depend on this test case later. - FixtureDependencies::const_iterator setupIt = - fixtureSetups.find(requiredFixtureName); - if (setupIt != fixtureSetups.end()) { - for (TestIteratorSet::const_iterator sIt = setupIt->second.begin(); - sIt != setupIt->second.end(); ++sIt) { - const std::string& setupTestName = (**sIt).Name; - tests[i].RequireSuccessDepends.insert(setupTestName); - if (std::find(tests[i].Depends.begin(), tests[i].Depends.end(), - setupTestName) == tests[i].Depends.end()) { - tests[i].Depends.push_back(setupTestName); - } + std::pair<FixtureDepsIterator, FixtureDepsIterator> setupRange = + fixtureSetups.equal_range(requiredFixtureName); + for (FixtureDepsIterator sIt = setupRange.first; + sIt != setupRange.second; ++sIt) { + const std::string& setupTestName = sIt->second->Name; + tests[i].RequireSuccessDepends.insert(setupTestName); + if (std::find(tests[i].Depends.begin(), tests[i].Depends.end(), + setupTestName) == tests[i].Depends.end()) { + tests[i].Depends.push_back(setupTestName); } } @@ -882,17 +881,11 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const // Already added this fixture continue; } - FixtureDependencies::const_iterator fixtureIt = - fixtureDeps.find(requiredFixtureName); - if (fixtureIt == fixtureDeps.end()) { - // No setup or cleanup tests for this fixture - continue; - } - - const TestIteratorSet& testIters = fixtureIt->second; - for (TestIteratorSet::const_iterator depsIt = testIters.begin(); - depsIt != testIters.end(); ++depsIt) { - ListOfTests::const_iterator lotIt = *depsIt; + std::pair<FixtureDepsIterator, FixtureDepsIterator> fixtureRange = + fixtureDeps.equal_range(requiredFixtureName); + for (FixtureDepsIterator it = fixtureRange.first; + it != fixtureRange.second; ++it) { + ListOfTests::const_iterator lotIt = it->second; const cmCTestTestProperties& p = *lotIt; if (!addedTests.insert(p.Name).second) { diff --git a/Source/cmFilePathChecksum.cxx b/Source/cmFilePathChecksum.cxx new file mode 100644 index 0000000..3d8b695 --- /dev/null +++ b/Source/cmFilePathChecksum.cxx @@ -0,0 +1,88 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFilePathChecksum.h" + +#include "cmBase32.h" +#include "cmCryptoHash.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" + +#include <vector> + +cmFilePathChecksum::cmFilePathChecksum() +{ +} + +cmFilePathChecksum::cmFilePathChecksum(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir) +{ + setupParentDirs(currentSrcDir, currentBinDir, projectSrcDir, projectBinDir); +} + +cmFilePathChecksum::cmFilePathChecksum(cmMakefile* makefile) +{ + setupParentDirs(makefile->GetCurrentSourceDirectory(), + makefile->GetCurrentBinaryDirectory(), + makefile->GetHomeDirectory(), + makefile->GetHomeOutputDirectory()); +} + +void cmFilePathChecksum::setupParentDirs(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir) +{ + parentDirs[0].first = cmsys::SystemTools::GetRealPath(currentSrcDir); + parentDirs[1].first = cmsys::SystemTools::GetRealPath(currentBinDir); + parentDirs[2].first = cmsys::SystemTools::GetRealPath(projectSrcDir); + parentDirs[3].first = cmsys::SystemTools::GetRealPath(projectBinDir); + + parentDirs[0].second = "CurrentSource"; + parentDirs[1].second = "CurrentBinary"; + parentDirs[2].second = "ProjectSource"; + parentDirs[3].second = "ProjectBinary"; +} + +std::string cmFilePathChecksum::get(const std::string& filePath) +{ + std::string relPath; + std::string relSeed; + { + const std::string fileReal = cmsys::SystemTools::GetRealPath(filePath); + std::string parentDir; + // Find closest project parent directory + for (size_t ii = 0; ii != numParentDirs; ++ii) { + const std::string& pDir = parentDirs[ii].first; + if (!pDir.empty() && + cmsys::SystemTools::IsSubDirectory(fileReal, pDir)) { + relSeed = parentDirs[ii].second; + parentDir = pDir; + break; + } + } + // Use file system root as fallback parent directory + if (parentDir.empty()) { + relSeed = "FileSystemRoot"; + cmsys::SystemTools::SplitPathRootComponent(fileReal, &parentDir); + } + // Calculate relative path from project parent directory + relPath = cmsys::SystemTools::RelativePath( + parentDir, cmsys::SystemTools::GetParentDirectory(fileReal)); + } + + // Calculate the file ( seed + relative path ) binary checksum + std::vector<unsigned char> hashBytes = + cmCryptoHash(cmCryptoHash::AlgoSHA256).ByteHashString(relSeed + relPath); + + // Convert binary checksum to string + return cmBase32Encoder().encodeString(&hashBytes[0], hashBytes.size(), + false); +} + +std::string cmFilePathChecksum::getPart(const std::string& filePath, + size_t length) +{ + return get(filePath).substr(0, length); +} diff --git a/Source/cmFilePathChecksum.h b/Source/cmFilePathChecksum.h new file mode 100644 index 0000000..df19053 --- /dev/null +++ b/Source/cmFilePathChecksum.h @@ -0,0 +1,65 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFilePathChecksum_h +#define cmFilePathChecksum_h + +#include <cmConfigure.h> // IWYU pragma: keep + +#include <stddef.h> +#include <string> +#include <utility> + +class cmMakefile; + +/** \class cmFilePathChecksum + * @brief Generates a checksum for the parent directory of a file + * + * 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 cmFilePathChecksum +{ +public: + /// Maximum number of characters to use from the path checksum + static const size_t partLengthDefault = 10; + + /// @brief Parent directories are empty + cmFilePathChecksum(); + + /// @brief Initilizes the parent directories manually + cmFilePathChecksum(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir); + + /// @brief Initilizes the parent directories from a makefile + cmFilePathChecksum(cmMakefile* makefile); + + /// @brief Allows parent directories setup after construction + /// + void setupParentDirs(const std::string& currentSrcDir, + const std::string& currentBinDir, + const std::string& projectSrcDir, + const std::string& projectBinDir); + + /* @brief Calculates the path checksum for the parent directory of a file + * + */ + std::string get(const std::string& filePath); + + /* @brief Same as get() but returns only the first length characters + * + */ + std::string getPart(const std::string& filePath, + size_t length = partLengthDefault); + +private: + /// Size of the parent directory list + 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/cmFilePathUuid.cxx b/Source/cmFilePathUuid.cxx deleted file mode 100644 index 03d2524..0000000 --- a/Source/cmFilePathUuid.cxx +++ /dev/null @@ -1,118 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmFilePathUuid.h" - -#include "cmBase32.h" -#include "cmCryptoHash.h" -#include "cmMakefile.h" -#include "cmSystemTools.h" - -#include <vector> - -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[1].first = cmsys::SystemTools::GetRealPath(currentBinDir); - parentDirs[2].first = cmsys::SystemTools::GetRealPath(projectSrcDir); - parentDirs[3].first = cmsys::SystemTools::GetRealPath(projectBinDir); - - parentDirs[0].second = "CurrentSource"; - parentDirs[1].second = "CurrentBinary"; - parentDirs[2].second = "ProjectSource"; - 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; - if (outputPrefix) { - uuid += outputPrefix; - } - uuid += sourceBasename.substr(0, partLengthName); - uuid += "_"; - uuid += checksum.substr(0, partLengthCheckSum); - 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 - 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) -{ - std::string checksumBase32; - { - // Calculate the file ( seed + relative path + name ) checksum - std::vector<unsigned char> hashBytes = - cmCryptoHash(cmCryptoHash::AlgoSHA256) - .ByteHashString(sourceRelSeed + sourceRelPath + sourceFilename); - - checksumBase32 = - cmBase32Encoder().encodeString(&hashBytes[0], hashBytes.size(), false); - } - - return checksumBase32; -} diff --git a/Source/cmFilePathUuid.h b/Source/cmFilePathUuid.h deleted file mode 100644 index a450526..0000000 --- a/Source/cmFilePathUuid.h +++ /dev/null @@ -1,69 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFilePathUuid_h -#define cmFilePathUuid_h - -#include <cmConfigure.h> // IWYU pragma: keep - -#include <stddef.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: - /// Maximum number of characters to use from the file name - static const size_t partLengthName = 14; - /// Maximum number of characters to use from the path checksum - static const size_t partLengthCheckSum = 14; - - /// @brief Initilizes the parent directories from a makefile - cmFilePathUuid(cmMakefile* makefile); - - /// @brief 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 = CM_NULLPTR, - const char* outputSuffix = CM_NULLPTR); - -private: - void initParentDirs(const std::string& currentSrcDir, - const std::string& currentBinDir, - const std::string& projectSrcDir, - const std::string& projectBinDir); - - /// Returns the relative path and the parent directory key string (seed) - 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); - - /// Size of the parent directory list - 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/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 903bcec..6ee2c14 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -533,6 +533,21 @@ void cmGeneratorTarget::AddTracedSources(std::vector<std::string> const& srcs) } } +void cmGeneratorTarget::AddIncludeDirectory(const std::string& src, + bool before) +{ + this->Target->InsertInclude(src, this->Makefile->GetBacktrace(), before); + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + cmGeneratorExpression ge(lfbt); + CM_AUTO_PTR<cmCompiledGeneratorExpression> cge = ge.Parse(src); + cge->SetEvaluateForBuildsystem(true); + // Insert before begin/end + std::vector<TargetPropertyEntry*>::iterator pos = before + ? this->IncludeDirectoriesEntries.begin() + : this->IncludeDirectoriesEntries.end(); + this->IncludeDirectoriesEntries.insert(pos, new TargetPropertyEntry(cge)); +} + std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends( cmSourceFile const* sf) const { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 4c3c14b..f568699 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -396,6 +396,12 @@ public: void AddTracedSources(std::vector<std::string> const& srcs); /** + * Adds an entry to the INCLUDE_DIRECTORIES list. + * If before is true the entry is pushed at the front. + */ + void AddIncludeDirectory(const std::string& src, bool before = false); + + /** * Flags for a given source file as used in this target. Typically assigned * via SET_TARGET_PROPERTIES when the property is a list of source files. */ diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 46e49dc..ead1e72 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1470,6 +1470,7 @@ void cmLocalGenerator::AddCompilerRequirementFlag( static std::map<std::string, std::vector<std::string> > langStdMap; if (langStdMap.empty()) { // Maintain sorted order, most recent first. + langStdMap["CXX"].push_back("17"); langStdMap["CXX"].push_back("14"); langStdMap["CXX"].push_back("11"); langStdMap["CXX"].push_back("98"); diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index a42c8c9..f52fe26 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -4068,7 +4068,7 @@ static const char* const CXX_FEATURES[] = { CM_NULLPTR FOR_EACH_CXX_FEATURE( #undef FEATURE_STRING static const char* const C_STANDARDS[] = { "90", "99", "11" }; -static const char* const CXX_STANDARDS[] = { "98", "11", "14" }; +static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17" }; bool cmMakefile::AddRequiredTargetFeature(cmTarget* target, const std::string& feature, @@ -4301,7 +4301,9 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, bool needCxx98 = false; bool needCxx11 = false; bool needCxx14 = false; - this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14); + bool needCxx17 = false; + this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14, + needCxx17); const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); if (!existingCxxStandard) { @@ -4340,7 +4342,7 @@ bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98, bool& needCxx11, - bool& needCxx14) const + bool& needCxx14, bool& needCxx17) const { if (const char* propCxx98 = this->GetDefinition("CMAKE_CXX98_COMPILE_FEATURES")) { @@ -4360,6 +4362,12 @@ void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, cmSystemTools::ExpandListArgument(propCxx14, props); needCxx14 = std::find(props.begin(), props.end(), feature) != props.end(); } + if (const char* propCxx17 = + this->GetDefinition("CMAKE_CXX17_COMPILE_FEATURES")) { + std::vector<std::string> props; + cmSystemTools::ExpandListArgument(propCxx17, props); + needCxx17 = std::find(props.begin(), props.end(), feature) != props.end(); + } } bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, @@ -4369,8 +4377,10 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, bool needCxx98 = false; bool needCxx11 = false; bool needCxx14 = false; + bool needCxx17 = false; - this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14); + this->CheckNeededCxxLanguage(feature, needCxx98, needCxx11, needCxx14, + needCxx17); const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); if (existingCxxStandard) { @@ -4397,11 +4407,17 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, bool setCxx98 = needCxx98 && !existingCxxStandard; bool setCxx11 = needCxx11 && !existingCxxStandard; bool setCxx14 = needCxx14 && !existingCxxStandard; + bool setCxx17 = needCxx17 && !existingCxxStandard; - if (needCxx14 && existingCxxStandard && + if (needCxx17 && existingCxxStandard && existingCxxIt < std::find_if(cmArrayBegin(CXX_STANDARDS), cmArrayEnd(CXX_STANDARDS), - cmStrCmp("14"))) { + cmStrCmp("17"))) { + setCxx17 = true; + } else if (needCxx14 && existingCxxStandard && + existingCxxIt < std::find_if(cmArrayBegin(CXX_STANDARDS), + cmArrayEnd(CXX_STANDARDS), + cmStrCmp("14"))) { setCxx14 = true; } else if (needCxx11 && existingCxxStandard && existingCxxIt < std::find_if(cmArrayBegin(CXX_STANDARDS), @@ -4415,7 +4431,10 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, setCxx98 = true; } - if (setCxx14) { + if (setCxx17) { + target->SetProperty("CXX_STANDARD", "17"); + target->SetProperty("CUDA_STANDARD", "17"); + } else if (setCxx14) { target->SetProperty("CXX_STANDARD", "14"); target->SetProperty("CUDA_STANDARD", "14"); } else if (setCxx11) { diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 859b3c8..3484e5a 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -905,7 +905,8 @@ private: void CheckNeededCLanguage(const std::string& feature, bool& needC90, bool& needC99, bool& needC11) const; void CheckNeededCxxLanguage(const std::string& feature, bool& needCxx98, - bool& needCxx11, bool& needCxx14) const; + bool& needCxx11, bool& needCxx14, + bool& needCxx17) const; bool HaveCStandardAvailable(cmTarget const* target, const std::string& feature) const; diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index 8afd532..f0847b1 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -4,7 +4,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommandLines.h" -#include "cmFilePathUuid.h" +#include "cmFilePathChecksum.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" @@ -35,14 +35,34 @@ #include <utility> #include <vector> +static void utilCopyTargetProperty(cmTarget* destinationTarget, + cmTarget* sourceTarget, + const std::string& propertyName) +{ + const char* propertyValue = sourceTarget->GetProperty(propertyName); + if (propertyValue) { + destinationTarget->SetProperty(propertyName, propertyValue); + } +} + +static std::string utilStripCR(std::string const& line) +{ + // Strip CR characters rcc may have printed (possibly more than one!). + std::string::size_type cr = line.find('\r'); + if (cr != line.npos) { + return line.substr(0, cr); + } + return line; +} + static std::string GetAutogenTargetName(cmGeneratorTarget const* target) { std::string autogenTargetName = target->GetName(); - autogenTargetName += "_automoc"; + autogenTargetName += "_autogen"; return autogenTargetName; } -static std::string GetAutogenTargetDir(cmGeneratorTarget const* target) +static std::string GetAutogenTargetFilesDir(cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); std::string targetDir = makefile->GetCurrentBinaryDirectory(); @@ -59,10 +79,25 @@ static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target) std::string targetDir = makefile->GetCurrentBinaryDirectory(); targetDir += "/"; targetDir += GetAutogenTargetName(target); - targetDir += ".dir/"; + targetDir += "/"; return targetDir; } +static std::string GetQtMajorVersion(cmGeneratorTarget const* target) +{ + cmMakefile* makefile = target->Target->GetMakefile(); + std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); + if (qtMajorVersion.empty()) { + qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); + } + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); + if (targetQtVersion != CM_NULLPTR) { + qtMajorVersion = targetQtVersion; + } + return qtMajorVersion; +} + static void SetupSourceFiles(cmGeneratorTarget const* target, std::vector<std::string>& skipMoc, std::vector<std::string>& mocSources, @@ -74,41 +109,20 @@ static void SetupSourceFiles(cmGeneratorTarget const* target, std::vector<cmSourceFile*> srcFiles; target->GetConfigCommonSourceFiles(srcFiles); - std::vector<std::string> newRccFiles; - - cmFilePathUuid fpathUuid(makefile); + cmFilePathChecksum fpathCheckSum(makefile); for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; - std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - bool skipFileForMoc = - cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC")); - bool generated = cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED")); + const std::string absFile = + cmsys::SystemTools::GetRealPath(sf->GetFullPath()); + const std::string ext = sf->GetExtension(); if (cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOUIC"))) { skipUic.push_back(absFile); } - std::string ext = sf->GetExtension(); - - if (target->GetPropertyAsBool("AUTORCC")) { - if (ext == "qrc" && - !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) { - - 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"); - - makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", - rcc_output_file.c_str(), false); - makefile->GetOrCreateSource(rcc_output_file, true); - newRccFiles.push_back(rcc_output_file); - } - } - - if (!generated) { - if (skipFileForMoc) { + if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { + if (cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC"))) { skipMoc.push_back(absFile); } else { cmSystemTools::FileFormat fileType = @@ -121,11 +135,6 @@ static void SetupSourceFiles(cmGeneratorTarget const* target, } } } - - for (std::vector<std::string>::const_iterator fileIt = newRccFiles.begin(); - fileIt != newRccFiles.end(); ++fileIt) { - const_cast<cmGeneratorTarget*>(target)->AddSource(*fileIt); - } } static void GetCompileDefinitionsAndDirectories( @@ -146,7 +155,7 @@ static void GetCompileDefinitionsAndDirectories( defs += cmJoin(defines, ";"); } -static void SetupAutoMocTarget( +static void MocSetupAutoTarget( cmGeneratorTarget const* target, const std::string& autogenTargetName, std::vector<std::string> const& skipMoc, std::vector<std::string> const& mocHeaders, @@ -229,7 +238,7 @@ static void SetupAutoMocTarget( } } -static void GetUicOpts(cmGeneratorTarget const* target, +static void UicGetOpts(cmGeneratorTarget const* target, const std::string& config, std::string& optString) { std::vector<std::string> opts; @@ -237,7 +246,7 @@ static void GetUicOpts(cmGeneratorTarget const* target, optString = cmJoin(opts, ";"); } -static void SetupAutoUicTarget( +static void UicSetupAutoTarget( cmGeneratorTarget const* target, std::vector<std::string> const& skipUic, std::map<std::string, std::string>& configUicOptions) { @@ -259,7 +268,7 @@ static void SetupAutoUicTarget( std::string _uic_opts; std::vector<std::string> configs; const std::string& config = makefile->GetConfigurations(configs); - GetUicOpts(target, config, _uic_opts); + UicGetOpts(target, config, _uic_opts); if (!_uic_opts.empty()) { _uic_opts = cmOutputConverter::EscapeForCMake(_uic_opts); @@ -268,7 +277,7 @@ static void SetupAutoUicTarget( for (std::vector<std::string>::const_iterator li = configs.begin(); li != configs.end(); ++li) { std::string config_uic_opts; - GetUicOpts(target, *li, config_uic_opts); + UicGetOpts(target, *li, config_uic_opts); if (config_uic_opts != _uic_opts) { configUicOptions[*li] = cmOutputConverter::EscapeForCMake(config_uic_opts); @@ -331,25 +340,13 @@ static void SetupAutoUicTarget( } } -static std::string GetRccExecutable(cmGeneratorTarget const* target) +static std::string RccGetExecutable(cmGeneratorTarget const* target, + const std::string& qtMajorVersion) { cmLocalGenerator* lg = target->GetLocalGenerator(); - cmMakefile* makefile = target->Target->GetMakefile(); - const char* qtVersion = makefile->GetDefinition("_target_qt_version"); - if (!qtVersion) { - qtVersion = makefile->GetDefinition("Qt5Core_VERSION_MAJOR"); - if (!qtVersion) { - qtVersion = makefile->GetDefinition("QT_VERSION_MAJOR"); - } - if (const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", - "")) { - qtVersion = targetQtVersion; - } - } - std::string targetName = target->GetName(); - if (strcmp(qtVersion, "5") == 0) { + std::string const& targetName = target->GetName(); + if (qtMajorVersion == "5") { cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc"); if (!qt5Rcc) { cmSystemTools::Error("Qt5::rcc target not found ", targetName.c_str()); @@ -357,7 +354,7 @@ static std::string GetRccExecutable(cmGeneratorTarget const* target) } return qt5Rcc->ImportedGetLocation(""); } - if (strcmp(qtVersion, "4") == 0) { + if (qtMajorVersion == "4") { cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc"); if (!qt4Rcc) { cmSystemTools::Error("Qt4::rcc target not found ", targetName.c_str()); @@ -372,7 +369,7 @@ static std::string GetRccExecutable(cmGeneratorTarget const* target) return std::string(); } -static void MergeRccOptions(std::vector<std::string>& opts, +static void RccMergeOptions(std::vector<std::string>& opts, const std::vector<std::string>& fileOpts, bool isQt5) { @@ -404,41 +401,16 @@ static void MergeRccOptions(std::vector<std::string>& opts, opts.insert(opts.end(), extraOpts.begin(), extraOpts.end()); } -static void copyTargetProperty(cmTarget* destinationTarget, - cmTarget* sourceTarget, - const std::string& propertyName) -{ - const char* propertyValue = sourceTarget->GetProperty(propertyName); - if (propertyValue) { - destinationTarget->SetProperty(propertyName, propertyValue); - } -} - -static std::string cmQtAutoGeneratorsStripCR(std::string const& line) -{ - // Strip CR characters rcc may have printed (possibly more than one!). - std::string::size_type cr = line.find('\r'); - if (cr != line.npos) { - return line.substr(0, cr); - } - return line; -} - -static std::string ReadAll(const std::string& filename) -{ - cmsys::ifstream file(filename.c_str()); - std::ostringstream stream; - stream << file.rdbuf(); - file.close(); - return stream.str(); -} - /// @brief Reads the resource files list from from a .qrc file - Qt5 version /// @return True if the .qrc file was successfully parsed -static bool ListQt5RccInputs(cmSourceFile* sf, cmGeneratorTarget const* target, +static bool RccListInputsQt5(cmSourceFile* sf, cmGeneratorTarget const* target, std::vector<std::string>& depends) { - std::string rccCommand = GetRccExecutable(target); + const std::string rccCommand = RccGetExecutable(target, "5"); + if (rccCommand.empty()) { + cmSystemTools::Error("AUTOGEN: error: rcc executable not available\n"); + return false; + } bool hasDashDashList = false; // Read rcc features @@ -486,7 +458,7 @@ static bool ListQt5RccInputs(cmSourceFile* sf, cmGeneratorTarget const* target, std::istringstream ostr(rccStdOut); std::string oline; while (std::getline(ostr, oline)) { - oline = cmQtAutoGeneratorsStripCR(oline); + oline = utilStripCR(oline); if (!oline.empty()) { depends.push_back(oline); } @@ -497,7 +469,7 @@ static bool ListQt5RccInputs(cmSourceFile* sf, cmGeneratorTarget const* target, std::istringstream estr(rccStdErr); std::string eline; while (std::getline(estr, eline)) { - eline = cmQtAutoGeneratorsStripCR(eline); + eline = utilStripCR(eline); if (cmHasLiteralPrefix(eline, "RCC: Error in")) { static std::string searchString = "Cannot find file '"; @@ -521,10 +493,16 @@ static bool ListQt5RccInputs(cmSourceFile* sf, cmGeneratorTarget const* target, /// @brief Reads the resource files list from from a .qrc file - Qt4 version /// @return True if the .qrc file was successfully parsed -static bool ListQt4RccInputs(cmSourceFile* sf, +static bool RccListInputsQt4(cmSourceFile* sf, std::vector<std::string>& depends) { - const std::string qrcContents = ReadAll(sf->GetFullPath()); + // Read file into string + std::string qrcContents; + { + std::ostringstream stream; + stream << cmsys::ifstream(sf->GetFullPath().c_str()).rdbuf(); + qrcContents = stream.str(); + } cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); @@ -551,17 +529,18 @@ static bool ListQt4RccInputs(cmSourceFile* sf, /// @brief Reads the resource files list from from a .qrc file /// @return True if the rcc file was successfully parsed -static bool ListQtRccInputs(const std::string& qtMajorVersion, - cmSourceFile* sf, cmGeneratorTarget const* target, - std::vector<std::string>& depends) +static bool RccListInputs(const std::string& qtMajorVersion, cmSourceFile* sf, + cmGeneratorTarget const* target, + std::vector<std::string>& depends) { if (qtMajorVersion == "5") { - return ListQt5RccInputs(sf, target, depends); + return RccListInputsQt5(sf, target, depends); } - return ListQt4RccInputs(sf, depends); + return RccListInputsQt4(sf, depends); } -static void SetupAutoRccTarget(cmGeneratorTarget const* target) +static void RccSetupAutoTarget(cmGeneratorTarget const* target, + const std::string& qtMajorVersion) { std::string _rcc_files; const char* sepRccFiles = ""; @@ -577,16 +556,12 @@ static void SetupAutoRccTarget(cmGeneratorTarget const* target) std::string rccFileOptions; const char* optionSep = ""; - const char* qtVersion = makefile->GetDefinition("_target_qt_version"); + const bool qtMajorVersion5 = (qtMajorVersion == "5"); std::vector<std::string> rccOptions; if (const char* opts = target->GetProperty("AUTORCC_OPTIONS")) { cmSystemTools::ExpandListArgument(opts, rccOptions); } - std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); - if (qtMajorVersion == "") { - qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); - } for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { @@ -604,7 +579,7 @@ static void SetupAutoRccTarget(cmGeneratorTarget const* target) if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) { std::vector<std::string> optsVec; cmSystemTools::ExpandListArgument(prop, optsVec); - MergeRccOptions(rccOptions, optsVec, strcmp(qtVersion, "5") == 0); + RccMergeOptions(rccOptions, optsVec, qtMajorVersion5); } if (!rccOptions.empty()) { @@ -624,7 +599,7 @@ static void SetupAutoRccTarget(cmGeneratorTarget const* target) std::string entriesList; if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { std::vector<std::string> depends; - if (ListQtRccInputs(qtMajorVersion, sf, target, depends)) { + if (RccListInputs(qtMajorVersion, sf, target, depends)) { entriesList = cmJoin(depends, "@list_sep@"); } else { return; @@ -637,38 +612,27 @@ static void SetupAutoRccTarget(cmGeneratorTarget const* target) } } makefile->AddDefinition( - "_qt_rcc_inputs_" + target->GetName(), - cmOutputConverter::EscapeForCMake(qrcInputs).c_str()); - + "_rcc_inputs", cmOutputConverter::EscapeForCMake(qrcInputs).c_str()); makefile->AddDefinition( "_rcc_files", cmOutputConverter::EscapeForCMake(_rcc_files).c_str()); - makefile->AddDefinition( - "_qt_rcc_options_files", + "_rcc_options_files", cmOutputConverter::EscapeForCMake(rccFileFiles).c_str()); makefile->AddDefinition( - "_qt_rcc_options_options", + "_rcc_options_options", cmOutputConverter::EscapeForCMake(rccFileOptions).c_str()); - makefile->AddDefinition("_qt_rcc_executable", - GetRccExecutable(target).c_str()); + RccGetExecutable(target, qtMajorVersion).c_str()); } void cmQtAutoGeneratorInitializer::InitializeAutogenSources( cmGeneratorTarget* target) { - cmMakefile* makefile = target->Target->GetMakefile(); - if (target->GetPropertyAsBool("AUTOMOC")) { - std::string automocTargetName = GetAutogenTargetName(target); - std::string mocCppFile = makefile->GetCurrentBinaryDirectory(); - mocCppFile += "/"; - mocCppFile += automocTargetName; - mocCppFile += ".cpp"; + cmMakefile* makefile = target->Target->GetMakefile(); + const std::string mocCppFile = + GetAutogenTargetBuildDir(target) + "moc_compilation.cpp"; makefile->GetOrCreateSource(mocCppFile, true); - makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", mocCppFile.c_str(), - false); - target->AddSource(mocCppFile); } } @@ -678,56 +642,77 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( { cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); - if (qtMajorVersion == "") { - qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); - } - - // create a custom target for running generators at buildtime: - std::string autogenTargetName = GetAutogenTargetName(target); - - std::string targetDir = GetAutogenTargetDir(target); + // Create a custom target for running generators at buildtime + const std::string autogenTargetName = GetAutogenTargetName(target); + const std::string autogenBuildDir = GetAutogenTargetBuildDir(target); + const std::string workingDirectory = + cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory()); + const std::string qtMajorVersion = GetQtMajorVersion(target); + std::vector<std::string> autogenOutputFiles; - cmCustomCommandLine currentLine; - currentLine.push_back(cmSystemTools::GetCMakeCommand()); - currentLine.push_back("-E"); - currentLine.push_back("cmake_autogen"); - currentLine.push_back(targetDir); - currentLine.push_back("$<CONFIGURATION>"); + // Create autogen target build directory and add it to the clean files + cmSystemTools::MakeDirectory(autogenBuildDir); + makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", + autogenBuildDir.c_str(), false); - cmCustomCommandLines commandLines; - commandLines.push_back(currentLine); + if (target->GetPropertyAsBool("AUTOMOC") || + target->GetPropertyAsBool("AUTOUIC")) { + // Create autogen target includes directory and + // add it to the origin target INCLUDE_DIRECTORIES + const std::string incsDir = autogenBuildDir + "include"; + cmSystemTools::MakeDirectory(incsDir); + target->AddIncludeDirectory(incsDir, true); + } - std::string workingDirectory = - cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory()); + if (target->GetPropertyAsBool("AUTOMOC")) { + // Register moc compilation file as generated + autogenOutputFiles.push_back(autogenBuildDir + "moc_compilation.cpp"); + } + // Initialize autogen target dependencies std::vector<std::string> depends; if (const char* autogenDepends = target->GetProperty("AUTOGEN_TARGET_DEPENDS")) { cmSystemTools::ExpandListArgument(autogenDepends, depends); } - std::vector<std::string> toolNames; - if (target->GetPropertyAsBool("AUTOMOC")) { - toolNames.push_back("moc"); - } - if (target->GetPropertyAsBool("AUTOUIC")) { - toolNames.push_back("uic"); - } - if (target->GetPropertyAsBool("AUTORCC")) { - toolNames.push_back("rcc"); + + // Compose command lines + cmCustomCommandLines commandLines; + { + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autogen"); + currentLine.push_back(GetAutogenTargetFilesDir(target)); + currentLine.push_back("$<CONFIGURATION>"); + commandLines.push_back(currentLine); } - std::string tools = toolNames[0]; - toolNames.erase(toolNames.begin()); - while (toolNames.size() > 1) { - tools += ", " + toolNames[0]; + // Compose target comment + std::string autogenComment; + { + std::vector<std::string> toolNames; + if (target->GetPropertyAsBool("AUTOMOC")) { + toolNames.push_back("MOC"); + } + if (target->GetPropertyAsBool("AUTOUIC")) { + toolNames.push_back("UIC"); + } + if (target->GetPropertyAsBool("AUTORCC")) { + toolNames.push_back("RCC"); + } + + std::string tools = toolNames[0]; toolNames.erase(toolNames.begin()); + while (toolNames.size() > 1) { + tools += ", " + toolNames[0]; + toolNames.erase(toolNames.begin()); + } + if (toolNames.size() == 1) { + tools += " and " + toolNames[0]; + } + autogenComment = "Automatic " + tools + " for target " + target->GetName(); } - if (toolNames.size() == 1) { - tools += " and " + toolNames[0]; - } - std::string autogenComment = "Automatic " + tools + " for target "; - autogenComment += target->GetName(); #if defined(_WIN32) && !defined(__CYGWIN__) bool usePRE_BUILD = false; @@ -741,6 +726,8 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( // https://connect.microsoft.com/VisualStudio/feedback/details/769495 usePRE_BUILD = vsgg->GetVersion() >= cmGlobalVisualStudioGenerator::VS7; if (usePRE_BUILD) { + // If the autogen target depends on an other target + // don't use PRE_BUILD for (std::vector<std::string>::iterator it = depends.begin(); it != depends.end(); ++it) { if (!makefile->FindTargetToUse(it->c_str())) { @@ -752,35 +739,42 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } #endif - std::vector<std::string> rcc_output; - bool const isNinja = lg->GetGlobalGenerator()->GetName() == "Ninja"; - if (isNinja -#if defined(_WIN32) && !defined(__CYGWIN__) - || usePRE_BUILD -#endif - ) { + if (target->GetPropertyAsBool("AUTORCC")) { + cmFilePathChecksum fpathCheckSum(makefile); 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; - std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - - std::string ext = sf->GetExtension(); - - if (target->GetPropertyAsBool("AUTORCC")) { - if (ext == "qrc" && - !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) { - { - 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 (sf->GetExtension() == "qrc" && + !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) { + { + const std::string absFile = + cmsys::SystemTools::GetRealPath(sf->GetFullPath()); + + // Run cmake again when .qrc file changes + makefile->AddCMakeDependFile(absFile); + + std::string rccOutputFile = autogenBuildDir; + rccOutputFile += fpathCheckSum.getPart(absFile); + rccOutputFile += "/qrc_"; + rccOutputFile += + cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile); + rccOutputFile += ".cpp"; + + // Add rcc output file to origin target sources + makefile->GetOrCreateSource(rccOutputFile, true); + target->AddSource(rccOutputFile); + // Register rcc output file as generated + autogenOutputFiles.push_back(rccOutputFile); + } + if (lg->GetGlobalGenerator()->GetName() == "Ninja" +#if defined(_WIN32) && !defined(__CYGWIN__) + || usePRE_BUILD +#endif + ) { if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { - ListQtRccInputs(qtMajorVersion, sf, target, depends); + RccListInputs(qtMajorVersion, sf, target, depends); #if defined(_WIN32) && !defined(__CYGWIN__) // Cannot use PRE_BUILD because the resource files themselves // may not be sources within the target so VS may not know the @@ -811,7 +805,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( { cmTarget* autogenTarget = makefile->AddUtilityCommand( autogenTargetName, true, workingDirectory.c_str(), - /*byproducts=*/rcc_output, depends, commandLines, false, + /*byproducts=*/autogenOutputFiles, depends, commandLines, false, autogenComment.c_str()); cmGeneratorTarget* gt = new cmGeneratorTarget(autogenTarget, lg); @@ -828,7 +822,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( autogenTarget->SetProperty("FOLDER", autogenFolder); } else { // inherit FOLDER property from target (#13688) - copyTargetProperty(gt->Target, target->Target, "FOLDER"); + utilCopyTargetProperty(gt->Target, target->Target, "FOLDER"); } target->Target->AddUtility(autogenTargetName); @@ -845,7 +839,8 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( static_cast<void>(varScope); // create a custom target for running generators at buildtime: - std::string autogenTargetName = GetAutogenTargetName(target); + const std::string autogenTargetName = GetAutogenTargetName(target); + const std::string qtMajorVersion = GetQtMajorVersion(target); makefile->AddDefinition( "_moc_target_name", @@ -853,28 +848,14 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( makefile->AddDefinition( "_origin_target_name", cmOutputConverter::EscapeForCMake(target->GetName()).c_str()); - - std::string targetDir = GetAutogenTargetDir(target); - - const char* qtVersion = makefile->GetDefinition("Qt5Core_VERSION_MAJOR"); - if (!qtVersion) { - qtVersion = makefile->GetDefinition("QT_VERSION_MAJOR"); - } - if (const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", - "")) { - qtVersion = targetQtVersion; - } - if (qtVersion) { - makefile->AddDefinition("_target_qt_version", qtVersion); - } + makefile->AddDefinition("_target_qt_version", qtMajorVersion.c_str()); std::vector<std::string> skipUic; std::vector<std::string> skipMoc; std::vector<std::string> mocSources; std::vector<std::string> mocHeaders; - std::map<std::string, std::string> configIncludes; - std::map<std::string, std::string> configDefines; + std::map<std::string, std::string> configMocIncludes; + std::map<std::string, std::string> configMocDefines; std::map<std::string, std::string> configUicOptions; if (target->GetPropertyAsBool("AUTOMOC") || @@ -886,39 +867,41 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( "_cpp_files", cmOutputConverter::EscapeForCMake(cmJoin(mocSources, ";")).c_str()); if (target->GetPropertyAsBool("AUTOMOC")) { - SetupAutoMocTarget(target, autogenTargetName, skipMoc, mocHeaders, - configIncludes, configDefines); + MocSetupAutoTarget(target, autogenTargetName, skipMoc, mocHeaders, + configMocIncludes, configMocDefines); } if (target->GetPropertyAsBool("AUTOUIC")) { - SetupAutoUicTarget(target, skipUic, configUicOptions); + UicSetupAutoTarget(target, skipUic, configUicOptions); } if (target->GetPropertyAsBool("AUTORCC")) { - SetupAutoRccTarget(target); + RccSetupAutoTarget(target, qtMajorVersion); } + // Generate config file std::string inputFile = cmSystemTools::GetCMakeRoot(); inputFile += "/Modules/AutogenInfo.cmake.in"; - std::string outputFile = targetDir; + std::string outputFile = GetAutogenTargetFilesDir(target); outputFile += "/AutogenInfo.cmake"; - makefile->AddDefinition( - "_qt_rcc_inputs", - makefile->GetDefinition("_qt_rcc_inputs_" + target->GetName())); + makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), false, true, false); - // Ensure we have write permission in case .in was read-only. - mode_t perm = 0; + // Append custom definitions to config file + if (!configMocDefines.empty() || !configMocIncludes.empty() || + !configUicOptions.empty()) { + + // Ensure we have write permission in case .in was read-only. + mode_t perm = 0; #if defined(_WIN32) && !defined(__CYGWIN__) - mode_t mode_write = S_IWRITE; + mode_t mode_write = S_IWRITE; #else - mode_t mode_write = S_IWUSR; + mode_t mode_write = S_IWUSR; #endif - cmSystemTools::GetPermissions(outputFile, perm); - if (!(perm & mode_write)) { - cmSystemTools::SetPermissions(outputFile, perm | mode_write); - } - if (!configDefines.empty() || !configIncludes.empty() || - !configUicOptions.empty()) { + cmSystemTools::GetPermissions(outputFile, perm); + if (!(perm & mode_write)) { + cmSystemTools::SetPermissions(outputFile, perm | mode_write); + } + cmsys::ofstream infoFile(outputFile.c_str(), std::ios::app); if (!infoFile) { std::string error = "Internal CMake error when trying to open file: "; @@ -927,19 +910,19 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( cmSystemTools::Error(error.c_str()); return; } - if (!configDefines.empty()) { + if (!configMocDefines.empty()) { for (std::map<std::string, std::string>::iterator - it = configDefines.begin(), - end = configDefines.end(); + it = configMocDefines.begin(), + end = configMocDefines.end(); it != end; ++it) { infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " " << it->second << ")\n"; } } - if (!configIncludes.empty()) { + if (!configMocIncludes.empty()) { for (std::map<std::string, std::string>::iterator - it = configIncludes.begin(), - end = configIncludes.end(); + it = configMocIncludes.begin(), + end = configMocIncludes.end(); it != end; ++it) { infoFile << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second << ")\n"; diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index fa33cf2..eb513e5 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -15,7 +15,6 @@ #include <utility> #include "cmAlgorithms.h" -#include "cmFilePathUuid.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" @@ -159,7 +158,6 @@ void cmQtAutoGenerators::MergeUicOptions( bool cmQtAutoGenerators::Run(const std::string& targetDirectory, const std::string& config) { - bool success = true; cmake cm; cm.SetHomeOutputDirectory(targetDirectory); cm.SetHomeDirectory(targetDirectory); @@ -173,18 +171,18 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory, CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&gg, snapshot)); gg.SetCurrentMakefile(mf.get()); - this->ReadAutogenInfoFile(mf.get(), targetDirectory, config); + if (!this->ReadAutogenInfoFile(mf.get(), targetDirectory, config)) { + return false; + } this->ReadOldMocDefinitionsFile(mf.get(), targetDirectory); - this->Init(); if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") { - success = this->RunAutogen(mf.get()); + if (!this->RunAutogen(mf.get())) { + return false; + } } - - this->WriteOldMocDefinitionsFile(targetDirectory); - - return success; + return this->WriteOldMocDefinitionsFile(targetDirectory); } bool cmQtAutoGenerators::ReadAutogenInfoFile( @@ -196,30 +194,41 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( filename += "/AutogenInfo.cmake"; if (!makefile->ReadListFile(filename.c_str())) { - cmSystemTools::Error("Error processing file: ", filename.c_str()); + std::ostringstream err; + err << "AUTOGEN: error processing file: " << filename << std::endl; + this->LogError(err.str()); return false; } + // - Target names + this->OriginTargetName = + makefile->GetSafeDefinition("AM_ORIGIN_TARGET_NAME"); + this->AutogenTargetName = makefile->GetSafeDefinition("AM_TARGET_NAME"); + + // - Directories + this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR"); + this->ProjectBinaryDir = makefile->GetSafeDefinition("AM_CMAKE_BINARY_DIR"); + this->CurrentSourceDir = + makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR"); + this->CurrentBinaryDir = + makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR"); + + // - Qt environment this->QtMajorVersion = makefile->GetSafeDefinition("AM_QT_VERSION_MAJOR"); if (this->QtMajorVersion == "") { this->QtMajorVersion = makefile->GetSafeDefinition("AM_Qt5Core_VERSION_MAJOR"); } - this->Sources = makefile->GetSafeDefinition("AM_SOURCES"); - { - std::string rccSources = makefile->GetSafeDefinition("AM_RCC_SOURCES"); - cmSystemTools::ExpandListArgument(rccSources, this->RccSources); - } - this->SkipMoc = makefile->GetSafeDefinition("AM_SKIP_MOC"); - this->SkipUic = makefile->GetSafeDefinition("AM_SKIP_UIC"); - this->Headers = makefile->GetSafeDefinition("AM_HEADERS"); - this->IncludeProjectDirsBefore = - makefile->IsOn("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - this->Srcdir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR"); - this->Builddir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR"); this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE"); this->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE"); this->RccExecutable = makefile->GetSafeDefinition("AM_QT_RCC_EXECUTABLE"); + + // - File Lists + this->Sources = makefile->GetSafeDefinition("AM_SOURCES"); + this->Headers = makefile->GetSafeDefinition("AM_HEADERS"); + + // - Moc + this->SkipMoc = makefile->GetSafeDefinition("AM_SKIP_MOC"); { std::string compileDefsPropOrig = "AM_MOC_COMPILE_DEFINITIONS"; std::string compileDefsProp = compileDefsPropOrig; @@ -244,12 +253,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( includes ? includes : makefile->GetSafeDefinition(includesPropOrig); } this->MocOptionsStr = makefile->GetSafeDefinition("AM_MOC_OPTIONS"); - this->ProjectBinaryDir = makefile->GetSafeDefinition("AM_CMAKE_BINARY_DIR"); - this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR"); - this->TargetName = makefile->GetSafeDefinition("AM_TARGET_NAME"); - this->OriginTargetName = - makefile->GetSafeDefinition("AM_ORIGIN_TARGET_NAME"); + // - Uic + this->SkipUic = makefile->GetSafeDefinition("AM_SKIP_UIC"); { const char* uicOptionsFiles = makefile->GetSafeDefinition("AM_UIC_OPTIONS_FILES"); @@ -280,6 +286,12 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->UicOptions[*fileIt] = *optionIt; } } + + // - Rcc + { + std::string rccSources = makefile->GetSafeDefinition("AM_RCC_SOURCES"); + cmSystemTools::ExpandListArgument(rccSources, this->RccSources); + } { const char* rccOptionsFiles = makefile->GetSafeDefinition("AM_RCC_OPTIONS_FILES"); @@ -303,8 +315,15 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( std::vector<std::string> rccInputLists; cmSystemTools::ExpandListArgument(rccInputs, rccInputLists); + // qrc files in the end of the list may have been empty + if (rccInputLists.size() < this->RccSources.size()) { + rccInputLists.resize(this->RccSources.size()); + } if (this->RccSources.size() != rccInputLists.size()) { - cmSystemTools::Error("Error processing file: ", filename.c_str()); + std::ostringstream err; + err << "AUTOGEN: RCC sources lists size missmatch in: " << filename; + err << std::endl; + this->LogError(err.str()); return false; } @@ -318,9 +337,14 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->RccInputs[*fileIt] = rccInputFiles; } } + + // - Settings this->CurrentCompileSettingsStr = this->MakeCompileSettingsString(makefile); - this->RelaxedMode = makefile->IsOn("AM_RELAXED_MODE"); + // - Flags + this->IncludeProjectDirsBefore = + makefile->IsOn("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); + this->MocRelaxedMode = makefile->IsOn("AM_MOC_RELAXED_MODE"); return true; } @@ -341,7 +365,7 @@ std::string cmQtAutoGenerators::MakeCompileSettingsString(cmMakefile* makefile) return s; } -bool cmQtAutoGenerators::ReadOldMocDefinitionsFile( +void cmQtAutoGenerators::ReadOldMocDefinitionsFile( cmMakefile* makefile, const std::string& targetDirectory) { std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); @@ -352,34 +376,49 @@ bool cmQtAutoGenerators::ReadOldMocDefinitionsFile( this->OldCompileSettingsStr = makefile->GetSafeDefinition("AM_OLD_COMPILE_SETTINGS"); } - return true; } -void cmQtAutoGenerators::WriteOldMocDefinitionsFile( +bool cmQtAutoGenerators::WriteOldMocDefinitionsFile( const std::string& targetDirectory) { + bool success = true; + std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); cmSystemTools::ConvertToUnixSlashes(filename); filename += "/AutomocOldMocDefinitions.cmake"; - cmsys::ofstream outfile; - outfile.open(filename.c_str(), std::ios::trunc); - outfile << "set(AM_OLD_COMPILE_SETTINGS " - << cmOutputConverter::EscapeForCMake(this->CurrentCompileSettingsStr) - << ")\n"; + { + cmsys::ofstream outfile; + outfile.open(filename.c_str(), std::ios::trunc); + if (outfile) { + outfile << "set(AM_OLD_COMPILE_SETTINGS " + << cmOutputConverter::EscapeForCMake( + this->CurrentCompileSettingsStr) + << ")\n"; + success = outfile.good(); + } else { + success = false; + } + } - outfile.close(); + return success; } void cmQtAutoGenerators::Init() { - this->TargetBuildSubDir = this->TargetName; - this->TargetBuildSubDir += ".dir/"; + this->AutogenBuildSubDir = this->AutogenTargetName; + this->AutogenBuildSubDir += "/"; - this->OutMocCppFilenameRel = this->TargetName; - this->OutMocCppFilenameRel += ".cpp"; + this->OutMocCppFilenameRel = this->AutogenBuildSubDir; + this->OutMocCppFilenameRel += "moc_compilation.cpp"; - this->OutMocCppFilenameAbs = this->Builddir + this->OutMocCppFilenameRel; + this->OutMocCppFilenameAbs = + this->CurrentBinaryDir + this->OutMocCppFilenameRel; + + // Init file path checksum generator + fpathCheckSum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir, + this->ProjectSourceDir, + this->ProjectBinaryDir); std::vector<std::string> cdefList; cmSystemTools::ExpandListArgument(this->MocCompileDefinitionsStr, cdefList); @@ -464,10 +503,10 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) // the program goes through all .cpp files to see which moc files are // included. It is not really interesting how the moc file is named, but // what file the moc is created from. Once a moc is included the same moc - // may not be included in the _automoc.cpp file anymore. OTOH if there's a - // header containing Q_OBJECT where no corresponding moc file is included - // anywhere a moc_<filename>.cpp file is created and included in - // the _automoc.cpp file. + // may not be included in the moc_compilation.cpp file anymore. OTOH if + // there's a header containing Q_OBJECT where no corresponding moc file + // is included anywhere a moc_<filename>.cpp file is created and included in + // the moc_compilation.cpp file. // key = moc source filepath, value = moc output filepath std::map<std::string, std::string> includedMocs; @@ -497,11 +536,16 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) err << "AUTOGEN: Checking " << absFilename << std::endl; this->LogInfo(err.str()); } - if (this->RelaxedMode) { - this->ParseCppFile(absFilename, headerExtensions, includedMocs, uiFiles); + if (this->MocRelaxedMode) { + if (!this->ParseCppFile(absFilename, headerExtensions, includedMocs, + uiFiles)) { + return false; + } } else { - this->StrictParseCppFile(absFilename, headerExtensions, includedMocs, - uiFiles); + if (!this->StrictParseCppFile(absFilename, headerExtensions, + includedMocs, uiFiles)) { + return false; + } } this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles); } @@ -533,38 +577,28 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) this->ParseHeaders(headerFiles, includedMocs, notIncludedMocs, includedUis); if (!this->MocExecutable.empty()) { - this->GenerateMocFiles(includedMocs, notIncludedMocs); + if (!this->GenerateMocFiles(includedMocs, notIncludedMocs)) { + return false; + } } if (!this->UicExecutable.empty()) { - this->GenerateUiFiles(includedUis); + if (!this->GenerateUiFiles(includedUis)) { + return false; + } } if (!this->RccExecutable.empty()) { - this->GenerateQrcFiles(); - } - - if (this->RunMocFailed) { - std::ostringstream err; - err << "moc failed..." << std::endl; - this->LogError(err.str()); - return false; - } - if (this->RunUicFailed) { - std::ostringstream err; - err << "uic failed..." << std::endl; - this->LogError(err.str()); - return false; - } - if (this->RunRccFailed) { - std::ostringstream err; - err << "rcc failed..." << std::endl; - this->LogError(err.str()); - return false; + if (!this->GenerateQrcFiles()) { + return false; + } } return true; } -void cmQtAutoGenerators::ParseCppFile( +/** + * @return True on success + */ +bool cmQtAutoGenerators::ParseCppFile( const std::string& absFilename, const std::vector<std::string>& headerExtensions, std::map<std::string, std::string>& includedMocs, @@ -579,12 +613,12 @@ void cmQtAutoGenerators::ParseCppFile( std::ostringstream err; err << "AUTOGEN: warning: " << absFilename << ": file is empty\n" << std::endl; - this->LogError(err.str()); - return; + this->LogWarning(err.str()); + return true; } this->ParseForUic(absFilename, contentsString, includedUis); if (this->MocExecutable.empty()) { - return; + return true; } const std::string absPath = cmsys::SystemTools::GetFilenamePath( @@ -648,7 +682,7 @@ void cmQtAutoGenerators::ParseCppFile( << std::endl; } this->LogError(err.str()); - ::exit(EXIT_FAILURE); + return false; } } else { std::string fileToMoc = absFilename; @@ -670,7 +704,7 @@ void cmQtAutoGenerators::ParseCppFile( << ".cpp\" for a compatibility with " "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n" << std::endl; - this->LogError(err.str()); + this->LogWarning(err.str()); } else { std::ostringstream err; err << "AUTOGEN: warning: " << absFilename @@ -683,7 +717,7 @@ void cmQtAutoGenerators::ParseCppFile( << ".cpp\" for compatibility with " "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n" << std::endl; - this->LogError(err.str()); + this->LogWarning(err.str()); } } else { std::ostringstream err; @@ -696,7 +730,7 @@ void cmQtAutoGenerators::ParseCppFile( "header.\n" << std::endl; this->LogError(err.str()); - ::exit(EXIT_FAILURE); + return false; } } else { dotMocIncluded = true; @@ -727,7 +761,7 @@ void cmQtAutoGenerators::ParseCppFile( << ".moc\" for compatibility with " "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n" << std::endl; - this->LogError(err.str()); + this->LogWarning(err.str()); includedMocs[absFilename] = ownMocUnderscoreFile; includedMocs.erase(ownMocHeaderFile); @@ -740,13 +774,17 @@ void cmQtAutoGenerators::ParseCppFile( << "\"" << scannedFileBasename << ".moc\" !\n" << std::endl; this->LogError(err.str()); - - ::exit(EXIT_FAILURE); + return false; } } + + return true; } -void cmQtAutoGenerators::StrictParseCppFile( +/** + * @return True on success + */ +bool cmQtAutoGenerators::StrictParseCppFile( const std::string& absFilename, const std::vector<std::string>& headerExtensions, std::map<std::string, std::string>& includedMocs, @@ -761,12 +799,12 @@ void cmQtAutoGenerators::StrictParseCppFile( std::ostringstream err; err << "AUTOGEN: warning: " << absFilename << ": file is empty\n" << std::endl; - this->LogError(err.str()); - return; + this->LogWarning(err.str()); + return true; } this->ParseForUic(absFilename, contentsString, includedUis); if (this->MocExecutable.empty()) { - return; + return true; } const std::string absPath = cmsys::SystemTools::GetFilenamePath( @@ -819,7 +857,7 @@ void cmQtAutoGenerators::StrictParseCppFile( << std::endl; } this->LogError(err.str()); - ::exit(EXIT_FAILURE); + return false; } } else { if (basename != scannedFileBasename) { @@ -835,7 +873,7 @@ void cmQtAutoGenerators::StrictParseCppFile( "moc on this source file.\n" << std::endl; this->LogError(err.str()); - ::exit(EXIT_FAILURE); + return false; } dotMocIncluded = true; includedMocs[absFilename] = currentMoc; @@ -857,8 +895,10 @@ void cmQtAutoGenerators::StrictParseCppFile( << "\"" << scannedFileBasename << ".moc\" !\n" << std::endl; this->LogError(err.str()); - ::exit(EXIT_FAILURE); + return false; } + + return true; } void cmQtAutoGenerators::ParseForUic( @@ -873,7 +913,7 @@ void cmQtAutoGenerators::ParseForUic( std::ostringstream err; err << "AUTOGEN: warning: " << absFilename << ": file is empty\n" << std::endl; - this->LogError(err.str()); + this->LogWarning(err.str()); return; } this->ParseForUic(absFilename, contentsString, includedUis); @@ -950,8 +990,6 @@ 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; @@ -967,8 +1005,10 @@ void cmQtAutoGenerators::ParseHeaders( std::string macroName; if (requiresMocing(contents, macroName)) { - notIncludedMocs[headerName] = - this->TargetBuildSubDir + fpathUuid.get(headerName, "moc_", ".cpp"); + notIncludedMocs[headerName] = fpathCheckSum.getPart(headerName) + + "/moc_" + + cmsys::SystemTools::GetFilenameWithoutLastExtension(headerName) + + ".cpp"; } } this->ParseForUic(headerName, contents, includedUis); @@ -994,44 +1034,52 @@ bool cmQtAutoGenerators::GenerateMocFiles( << "To avoid this error either" << std::endl << "- rename the source files or" << std::endl << "- do not include the (moc_NAME.cpp|NAME.moc) file" << std::endl; - this->NameCollisionLog(err.str(), collisions); - ::exit(EXIT_FAILURE); + this->LogErrorNameCollision(err.str(), collisions); + return false; } } // generate moc files that are included by source files. - for (std::map<std::string, std::string>::const_iterator it = - includedMocs.begin(); - it != includedMocs.end(); ++it) { - if (!this->GenerateMoc(it->first, it->second)) { - if (this->RunMocFailed) { - return false; + { + const std::string subDirPrefix = "include/"; + for (std::map<std::string, std::string>::const_iterator it = + includedMocs.begin(); + it != includedMocs.end(); ++it) { + if (!this->GenerateMoc(it->first, it->second, subDirPrefix)) { + if (this->RunMocFailed) { + return false; + } } } } // generate moc files that are _not_ included by source files. bool automocCppChanged = false; - for (std::map<std::string, std::string>::const_iterator it = - notIncludedMocs.begin(); - it != notIncludedMocs.end(); ++it) { - if (this->GenerateMoc(it->first, it->second)) { - automocCppChanged = true; - } else { - if (this->RunMocFailed) { - return false; + { + const std::string subDirPrefix; + for (std::map<std::string, std::string>::const_iterator it = + notIncludedMocs.begin(); + it != notIncludedMocs.end(); ++it) { + if (this->GenerateMoc(it->first, it->second, subDirPrefix)) { + automocCppChanged = true; + } else { + if (this->RunMocFailed) { + return false; + } } } } - // compose _automoc.cpp content + // Compose moc_compilation.cpp content std::string automocSource; { std::ostringstream outStream; outStream << "/* This file is autogenerated, do not edit*/\n"; if (notIncludedMocs.empty()) { + // Dummy content outStream << "enum some_compilers { need_more_than_nothing };\n"; } else { + // Valid content for (std::map<std::string, std::string>::const_iterator it = notIncludedMocs.begin(); it != notIncludedMocs.end(); ++it) { @@ -1042,12 +1090,12 @@ bool cmQtAutoGenerators::GenerateMocFiles( automocSource = outStream.str(); } - // check if we even need to update _automoc.cpp + // Check if we even need to update moc_compilation.cpp if (!automocCppChanged) { - // compare contents of the _automoc.cpp file + // compare contents of the moc_compilation.cpp file const std::string oldContents = ReadAll(this->OutMocCppFilenameAbs); if (oldContents == automocSource) { - // nothing changed: don't touch the _automoc.cpp file + // nothing changed: don't touch the moc_compilation.cpp file if (this->Verbose) { std::ostringstream err; err << "AUTOGEN: " << this->OutMocCppFilenameRel << " still up to date" @@ -1058,43 +1106,58 @@ bool cmQtAutoGenerators::GenerateMocFiles( } } - // actually write _automoc.cpp + // Actually write moc_compilation.cpp { - std::string msg = "Generating moc compilation "; + std::string msg = "Generating MOC compilation "; msg += this->OutMocCppFilenameRel; - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - msg.c_str(), true, this->ColorOutput); + this->LogBold(msg); } - { + // Make sure the parent directory exists + bool success = this->makeParentDirectory(this->OutMocCppFilenameAbs); + if (success) { cmsys::ofstream outfile; outfile.open(this->OutMocCppFilenameAbs.c_str(), std::ios::trunc); - outfile << automocSource; - outfile.close(); + if (!outfile) { + success = false; + std::ostringstream err; + err << "AUTOGEN: error opening " << this->OutMocCppFilenameAbs << "\n"; + this->LogError(err.str()); + } else { + outfile << automocSource; + // Check for write errors + if (!outfile.good()) { + success = false; + std::ostringstream err; + err << "AUTOGEN: error writing " << this->OutMocCppFilenameAbs << "\n"; + this->LogError(err.str()); + } + } } - - return true; + return success; } +/** + * @return True if a moc file was created. False may indicate an error. + */ bool cmQtAutoGenerators::GenerateMoc(const std::string& sourceFile, - const std::string& mocFileName) + const std::string& mocFileName, + const std::string& subDirPrefix) { - const std::string mocFilePath = this->Builddir + mocFileName; + const std::string mocFileRel = + this->AutogenBuildSubDir + subDirPrefix + mocFileName; + const std::string mocFileAbs = this->CurrentBinaryDir + mocFileRel; int sourceNewerThanMoc = 0; - bool success = cmsys::SystemTools::FileTimeCompare(sourceFile, mocFilePath, + bool success = cmsys::SystemTools::FileTimeCompare(sourceFile, mocFileAbs, &sourceNewerThanMoc); if (this->GenerateAll || !success || sourceNewerThanMoc >= 0) { - // make sure the directory for the resulting moc file exists - std::string mocDir = mocFilePath.substr(0, mocFilePath.rfind('/')); - if (!cmsys::SystemTools::FileExists(mocDir.c_str(), false)) { - cmsys::SystemTools::MakeDirectory(mocDir.c_str()); - } + // Log + this->LogBold("Generating MOC source " + mocFileRel); - std::string msg = "Generating moc source "; - msg += mocFileName; - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - msg.c_str(), true, this->ColorOutput); + // Make sure the parent directory exists + if (!this->makeParentDirectory(mocFileAbs)) { + this->RunMocFailed = true; + return false; + } std::vector<std::string> command; command.push_back(this->MocExecutable); @@ -1108,7 +1171,7 @@ bool cmQtAutoGenerators::GenerateMoc(const std::string& sourceFile, command.push_back("-DWIN32"); #endif command.push_back("-o"); - command.push_back(mocFilePath); + command.push_back(mocFileAbs); command.push_back(sourceFile); if (this->Verbose) { @@ -1120,12 +1183,15 @@ bool cmQtAutoGenerators::GenerateMoc(const std::string& sourceFile, bool result = cmSystemTools::RunSingleCommand(command, &output, &output, &retVal); if (!result || retVal) { - std::ostringstream err; - err << "AUTOGEN: error: process for " << mocFilePath << " failed:\n" - << output << std::endl; - this->LogError(err.str()); + { + std::ostringstream err; + err << "AUTOGEN: error: moc process for " << mocFileRel << " failed:\n" + << output << std::endl; + this->LogError(err.str()); + } + cmSystemTools::RemoveFile(mocFileAbs); this->RunMocFailed = true; - cmSystemTools::RemoveFile(mocFilePath); + return false; } return true; } @@ -1166,8 +1232,8 @@ bool cmQtAutoGenerators::GenerateUiFiles( "from different sources." << std::endl << "To avoid this error rename the source files." << std::endl; - this->NameCollisionLog(err.str(), collisions); - ::exit(EXIT_FAILURE); + this->LogErrorNameCollision(err.str(), collisions); + return false; } } testMap.clear(); @@ -1191,25 +1257,29 @@ bool cmQtAutoGenerators::GenerateUiFiles( return true; } +/** + * @return True if a uic file was created. False may indicate an error. + */ bool cmQtAutoGenerators::GenerateUi(const std::string& realName, const std::string& uiInputFile, const std::string& uiOutputFile) { - if (!cmsys::SystemTools::FileExists(this->Builddir.c_str(), false)) { - cmsys::SystemTools::MakeDirectory(this->Builddir.c_str()); - } - - const std::string uiBuildFile = this->Builddir + uiOutputFile; + const std::string uicFileRel = + this->AutogenBuildSubDir + "include/" + uiOutputFile; + const std::string uicFileAbs = this->CurrentBinaryDir + uicFileRel; int sourceNewerThanUi = 0; - bool success = cmsys::SystemTools::FileTimeCompare(uiInputFile, uiBuildFile, + bool success = cmsys::SystemTools::FileTimeCompare(uiInputFile, uicFileAbs, &sourceNewerThanUi); if (this->GenerateAll || !success || sourceNewerThanUi >= 0) { - std::string msg = "Generating ui header "; - msg += uiOutputFile; - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - msg.c_str(), true, this->ColorOutput); + // Log + this->LogBold("Generating UIC header " + uicFileRel); + + // Make sure the parent directory exists + if (!this->makeParentDirectory(uicFileAbs)) { + this->RunUicFailed = true; + return false; + } std::vector<std::string> command; command.push_back(this->UicExecutable); @@ -1226,7 +1296,7 @@ bool cmQtAutoGenerators::GenerateUi(const std::string& realName, command.insert(command.end(), opts.begin(), opts.end()); command.push_back("-o"); - command.push_back(uiBuildFile); + command.push_back(uicFileAbs); command.push_back(uiInputFile); if (this->Verbose) { @@ -1237,13 +1307,15 @@ bool cmQtAutoGenerators::GenerateUi(const std::string& realName, bool result = cmSystemTools::RunSingleCommand(command, &output, &output, &retVal); if (!result || retVal) { - std::ostringstream err; - err << "AUTOUIC: error: process for " << uiOutputFile - << " needed by\n \"" << realName << "\"\nfailed:\n" - << output << std::endl; - this->LogError(err.str()); + { + std::ostringstream err; + err << "AUTOUIC: error: uic process for " << uicFileRel + << " needed by\n \"" << realName << "\"\nfailed:\n" + << output << std::endl; + this->LogError(err.str()); + } + cmSystemTools::RemoveFile(uicFileAbs); this->RunUicFailed = true; - cmSystemTools::RemoveFile(uiOutputFile); return false; } return true; @@ -1271,18 +1343,13 @@ bool cmQtAutoGenerators::GenerateQrcFiles() { // generate single map with input / output names std::map<std::string, std::string> qrcGenMap; - { - 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")); - } + 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->AutogenBuildSubDir + fpathCheckSum.getPart(*si) + + "/qrc_" + cmsys::SystemTools::GetFilenameWithoutLastExtension(*si) + + ".cpp"; } } @@ -1295,8 +1362,8 @@ bool cmQtAutoGenerators::GenerateQrcFiles() " will be generated from different sources." << std::endl << "To avoid this error rename the source .qrc files." << std::endl; - this->NameCollisionLog(err.str(), collisions); - ::exit(EXIT_FAILURE); + this->LogErrorNameCollision(err.str(), collisions); + return false; } } @@ -1314,25 +1381,24 @@ bool cmQtAutoGenerators::GenerateQrcFiles() return true; } +/** + * @return True if a rcc file was created. False may indicate an error. + */ bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile, const std::string& qrcOutputFile, bool unique_n) { - 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); + std::string symbolName = + cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcInputFile); + if (!unique_n) { + symbolName += "_"; + symbolName += fpathCheckSum.getPart(qrcInputFile); } // Replace '-' with '_'. The former is valid for // file names but not for symbol names. std::replace(symbolName.begin(), symbolName.end(), '-', '_'); - const std::string qrcBuildFile = this->Builddir + qrcOutputFile; + const std::string qrcBuildFile = this->CurrentBinaryDir + qrcOutputFile; int sourceNewerThanQrc = 0; bool generateQrc = !cmsys::SystemTools::FileTimeCompare( @@ -1342,21 +1408,27 @@ bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile, generateQrc || this->InputFilesNewerThanQrc(qrcInputFile, qrcBuildFile); if (this->GenerateAll || generateQrc) { - std::string msg = "Generating qrc source "; - msg += qrcOutputFile; - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - msg.c_str(), true, this->ColorOutput); + { + std::string msg = "Generating RCC source "; + msg += qrcOutputFile; + this->LogBold(msg); + } + + // Make sure the parent directory exists + if (!this->makeParentDirectory(qrcOutputFile)) { + this->RunRccFailed = true; + return false; + } std::vector<std::string> command; command.push_back(this->RccExecutable); - - std::map<std::string, std::string>::const_iterator optionIt = - this->RccOptions.find(qrcInputFile); - if (optionIt != this->RccOptions.end()) { - cmSystemTools::ExpandListArgument(optionIt->second, command); + { + std::map<std::string, std::string>::const_iterator optionIt = + this->RccOptions.find(qrcInputFile); + if (optionIt != this->RccOptions.end()) { + cmSystemTools::ExpandListArgument(optionIt->second, command); + } } - command.push_back("-name"); command.push_back(symbolName); command.push_back("-o"); @@ -1371,16 +1443,20 @@ bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile, bool result = cmSystemTools::RunSingleCommand(command, &output, &output, &retVal); if (!result || retVal) { - std::ostringstream err; - err << "AUTORCC: error: process for " << qrcOutputFile << " failed:\n" - << output << std::endl; - this->LogError(err.str()); - this->RunRccFailed = true; + { + std::ostringstream err; + err << "AUTORCC: error: rcc process for " << qrcOutputFile + << " failed:\n" + << output << std::endl; + this->LogError(err.str()); + } cmSystemTools::RemoveFile(qrcBuildFile); + this->RunRccFailed = true; return false; } + return true; } - return true; + return false; } /** @@ -1413,7 +1489,7 @@ bool cmQtAutoGenerators::NameCollisionTest( return !collisions.empty(); } -void cmQtAutoGenerators::NameCollisionLog( +void cmQtAutoGenerators::LogErrorNameCollision( const std::string& message, const std::multimap<std::string, std::string>& collisions) { @@ -1429,14 +1505,30 @@ void cmQtAutoGenerators::NameCollisionLog( this->LogError(err.str()); } +void cmQtAutoGenerators::LogBold(const std::string& message) +{ + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, this->ColorOutput); +} + void cmQtAutoGenerators::LogInfo(const std::string& message) { - std::cout << message; + std::cout << message.c_str(); +} + +void cmQtAutoGenerators::LogWarning(const std::string& message) +{ + std::ostringstream ostr; + ostr << message << "\n"; + std::cout << message.c_str(); } void cmQtAutoGenerators::LogError(const std::string& message) { - std::cerr << message; + std::ostringstream ostr; + ostr << message << "\n\n"; + std::cerr << ostr.str(); } void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command) @@ -1455,6 +1547,25 @@ void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command) } } +/** + * @brief Generates the parent directory of the given file on demand + * @return True on success + */ +bool cmQtAutoGenerators::makeParentDirectory(const std::string& filename) +{ + bool success = true; + const std::string dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + success = cmsys::SystemTools::MakeDirectory(dirName); + if (!success) { + std::ostringstream err; + err << "AUTOGEN: Directory creation failed: " << dirName << std::endl; + this->LogError(err.str()); + } + } + return success; +} + std::string cmQtAutoGenerators::JoinExts(const std::vector<std::string>& lst) { if (lst.empty()) { diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index 08d7e03..c241579 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -4,6 +4,7 @@ #define cmQtAutoGenerators_h #include <cmConfigure.h> // IWYU pragma: keep +#include <cmFilePathChecksum.h> #include <list> #include <map> @@ -23,32 +24,36 @@ private: bool ReadAutogenInfoFile(cmMakefile* makefile, const std::string& targetDirectory, const std::string& config); - bool ReadOldMocDefinitionsFile(cmMakefile* makefile, + void ReadOldMocDefinitionsFile(cmMakefile* makefile, const std::string& targetDirectory); - void WriteOldMocDefinitionsFile(const std::string& targetDirectory); + bool WriteOldMocDefinitionsFile(const std::string& targetDirectory); std::string MakeCompileSettingsString(cmMakefile* makefile); bool RunAutogen(cmMakefile* makefile); + bool GenerateMocFiles( const std::map<std::string, std::string>& includedMocs, const std::map<std::string, std::string>& notIncludedMocs); bool GenerateMoc(const std::string& sourceFile, - const std::string& mocFileName); + const std::string& mocFileName, + const std::string& subDirPrefix); + bool GenerateUiFiles( const std::map<std::string, std::vector<std::string> >& includedUis); bool GenerateUi(const std::string& realName, const std::string& uiInputFile, const std::string& uiOutputFile); + bool GenerateQrcFiles(); bool GenerateQrc(const std::string& qrcInputFile, const std::string& qrcOutputFile, bool unique_n); - void ParseCppFile( + bool ParseCppFile( const std::string& absFilename, const std::vector<std::string>& headerExtensions, std::map<std::string, std::string>& includedMocs, std::map<std::string, std::vector<std::string> >& includedUis); - void StrictParseCppFile( + bool StrictParseCppFile( const std::string& absFilename, const std::vector<std::string>& headerExtensions, std::map<std::string, std::string>& includedMocs, @@ -76,13 +81,18 @@ private: bool NameCollisionTest(const std::map<std::string, std::string>& genFiles, std::multimap<std::string, std::string>& collisions); - void NameCollisionLog( + + void LogErrorNameCollision( const std::string& message, const std::multimap<std::string, std::string>& collisions); - + void LogBold(const std::string& message); void LogInfo(const std::string& message); + void LogWarning(const std::string& message); void LogError(const std::string& message); void LogCommand(const std::vector<std::string>& command); + + bool makeParentDirectory(const std::string& filename); + std::string JoinExts(const std::vector<std::string>& lst); static void MergeUicOptions(std::vector<std::string>& opts, @@ -92,39 +102,47 @@ private: bool InputFilesNewerThanQrc(const std::string& qrcFile, const std::string& rccOutput); + // - Target names + std::string OriginTargetName; + std::string AutogenTargetName; + // - Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildSubDir; + // - Qt environment std::string QtMajorVersion; - std::string Sources; - std::vector<std::string> RccSources; - std::string SkipMoc; - std::string SkipUic; - std::string Headers; - std::string Srcdir; - std::string Builddir; std::string MocExecutable; std::string UicExecutable; std::string RccExecutable; + // - File lists + std::string Sources; + std::string Headers; + // - Moc + std::string SkipMoc; std::string MocCompileDefinitionsStr; std::string MocIncludesStr; std::string MocOptionsStr; - std::string ProjectBinaryDir; - std::string ProjectSourceDir; - std::string TargetName; - std::string OriginTargetName; - - std::string CurrentCompileSettingsStr; - std::string OldCompileSettingsStr; - - std::string TargetBuildSubDir; std::string OutMocCppFilenameRel; std::string OutMocCppFilenameAbs; std::list<std::string> MocIncludes; std::list<std::string> MocDefinitions; std::vector<std::string> MocOptions; + // - Uic + std::string SkipUic; std::vector<std::string> UicTargetOptions; std::map<std::string, std::string> UicOptions; + // - Rcc + std::vector<std::string> RccSources; std::map<std::string, std::string> RccOptions; std::map<std::string, std::vector<std::string> > RccInputs; - + // - Settings + std::string CurrentCompileSettingsStr; + std::string OldCompileSettingsStr; + // - Utility + cmFilePathChecksum fpathCheckSum; + // - Flags bool IncludeProjectDirsBefore; bool Verbose; bool ColorOutput; @@ -132,7 +150,7 @@ private: bool RunUicFailed; bool RunRccFailed; bool GenerateAll; - bool RelaxedMode; + bool MocRelaxedMode; }; #endif diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 3776538..c08a36b 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -672,8 +672,9 @@ static Json::Value DumpSourceFilesList( std::string compileFlags = ld.Flags; if (const char* cflags = file->GetProperty("COMPILE_FLAGS")) { cmGeneratorExpression ge; + auto cge = ge.Parse(cflags); const char* processed = - ge.Parse(cflags)->Evaluate(target->GetLocalGenerator(), config); + cge->Evaluate(target->GetLocalGenerator(), config); lg->AppendFlags(compileFlags, processed); } fileData.Flags = compileFlags; diff --git a/Source/cmake.h b/Source/cmake.h index 0f1891d..5347745 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -549,6 +549,7 @@ private: F(cxx_std_98) \ F(cxx_std_11) \ F(cxx_std_14) \ + F(cxx_std_17) \ F(cxx_aggregate_default_initializers) \ F(cxx_alias_templates) \ F(cxx_alignas) \ diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index b8a9a6b..9eb3b2d 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -429,6 +429,17 @@ SET_SOURCE_FILES_PROPERTIES(ProcessUNIX.c System.c PROPERTIES COMPILE_FLAGS "-DKWSYS_C_HAS_PTRDIFF_T=${KWSYS_C_HAS_PTRDIFF_T} -DKWSYS_C_HAS_SSIZE_T=${KWSYS_C_HAS_SSIZE_T}" ) +IF(KWSYS_USE_DynamicLoader) + GET_PROPERTY(KWSYS_SUPPORTS_SHARED_LIBS GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) + IF(KWSYS_SUPPORTS_SHARED_LIBS) + SET(KWSYS_SUPPORTS_SHARED_LIBS 1) + ELSE() + SET(KWSYS_SUPPORTS_SHARED_LIBS 0) + ENDIF() + SET_PROPERTY(SOURCE DynamicLoader.cxx APPEND PROPERTY COMPILE_DEFINITIONS + KWSYS_SUPPORTS_SHARED_LIBS=${KWSYS_SUPPORTS_SHARED_LIBS}) +ENDIF() + IF(KWSYS_USE_SystemTools) KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_SETENV "Checking whether CXX compiler has setenv" DIRECT) @@ -868,7 +879,7 @@ ENDIF() IF(KWSYS_USE_Encoding) # Set default 8 bit encoding in "EndcodingC.c". - SET_PROPERTY(SOURCE EncodingC.c APPEND PROPERTY COMPILE_DEFINITIONS + SET_PROPERTY(SOURCE EncodingC.c EncodingCXX.cxx APPEND PROPERTY COMPILE_DEFINITIONS KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE}) ENDIF() diff --git a/Source/kwsys/ConsoleBuf.hxx.in b/Source/kwsys/ConsoleBuf.hxx.in index c45a351..cb58865 100644 --- a/Source/kwsys/ConsoleBuf.hxx.in +++ b/Source/kwsys/ConsoleBuf.hxx.in @@ -327,14 +327,13 @@ private: const int length = WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), (int)wbuffer.size(), NULL, 0, NULL, NULL); - char* buf = new char[length + 1]; + char* buf = new char[length]; const bool success = WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), (int)wbuffer.size(), buf, length, NULL, NULL) > 0 ? true : false; - buf[length] = '\0'; - buffer = buf; + buffer = std::string(buf, length); delete[] buf; return success; } diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx index e0268c0..e494db6 100644 --- a/Source/kwsys/DynamicLoader.cxx +++ b/Source/kwsys/DynamicLoader.cxx @@ -12,20 +12,63 @@ #include "DynamicLoader.hxx.in" #endif -// This file is actually 3 different implementations. -// 1. HP machines which uses shl_load -// 2. Mac OS X 10.2.x and earlier which uses NSLinkModule -// 3. Windows which uses LoadLibrary -// 4. Most unix systems (including Mac OS X 10.3 and later) which use dlopen -// (default) Each part of the ifdef contains a complete implementation for +// This file actually contains several different implementations: +// * NOOP for environments without dynamic libs +// * HP machines which uses shl_load +// * Mac OS X 10.2.x and earlier which uses NSLinkModule +// * Windows which uses LoadLibrary +// * BeOS / Haiku +// * FreeMiNT for Atari +// * Default implementation for *NIX systems (including Mac OS X 10.3 and +// later) which use dlopen +// +// Each part of the ifdef contains a complete implementation for // the static methods of DynamicLoader. -// --------------------------------------------------------------- -// 1. Implementation for HPUX machines -#ifdef __hpux +#if !KWSYS_SUPPORTS_SHARED_LIBS +//---------------------------------------------------------------------------- +// Implementation for environments without dynamic libs +#include <string.h> // for strerror() + +namespace KWSYS_NAMESPACE { + +//---------------------------------------------------------------------------- +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname) +{ + return 0; +} + +//---------------------------------------------------------------------------- +int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) +{ + if (!lib) { + return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- +DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( + DynamicLoader::LibraryHandle lib, const std::string& sym) +{ + return 0; +} + +//---------------------------------------------------------------------------- +const char* DynamicLoader::LastError() +{ + return "General error"; +} + +} // namespace KWSYS_NAMESPACE + +#elif defined(__hpux) +//---------------------------------------------------------------------------- +// Implementation for HPUX machines #include <dl.h> #include <errno.h> -#define DYNAMICLOADER_DEFINED 1 namespace KWSYS_NAMESPACE { @@ -88,15 +131,11 @@ const char* DynamicLoader::LastError() } // namespace KWSYS_NAMESPACE -#endif //__hpux - -// --------------------------------------------------------------- -// 2. Implementation for Mac OS X 10.2.x and earlier -#ifdef __APPLE__ -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1030 +#elif defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED < 1030) +//---------------------------------------------------------------------------- +// Implementation for Mac OS X 10.2.x and earlier #include <mach-o/dyld.h> #include <string.h> // for strlen -#define DYNAMICLOADER_DEFINED 1 namespace KWSYS_NAMESPACE { @@ -160,14 +199,10 @@ const char* DynamicLoader::LastError() } // namespace KWSYS_NAMESPACE -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1030 -#endif // __APPLE__ - -// --------------------------------------------------------------- -// 3. Implementation for Windows win32 code but not cygwin -#if defined(_WIN32) && !defined(__CYGWIN__) +#elif defined(_WIN32) && !defined(__CYGWIN__) +//---------------------------------------------------------------------------- +// Implementation for Windows win32 code but not cygwin #include <windows.h> -#define DYNAMICLOADER_DEFINED 1 namespace KWSYS_NAMESPACE { @@ -263,19 +298,14 @@ const char* DynamicLoader::LastError() } // namespace KWSYS_NAMESPACE -#endif //_WIN32 - -// --------------------------------------------------------------- -// 4. Implementation for BeOS -#if defined __BEOS__ - +#elif defined(__BEOS__) +//---------------------------------------------------------------------------- +// Implementation for BeOS / Haiku #include <string.h> // for strerror() #include <be/kernel/image.h> #include <be/support/Errors.h> -#define DYNAMICLOADER_DEFINED 1 - namespace KWSYS_NAMESPACE { static image_id last_dynamic_err = B_OK; @@ -351,54 +381,10 @@ const char* DynamicLoader::LastError() } } // namespace KWSYS_NAMESPACE -#endif - -// --------------------------------------------------------------- -// 5. Implementation for systems without dynamic libs -// __gnu_blrts__ is IBM BlueGene/L -// __LIBCATAMOUNT__ is defined on Catamount on Cray compute nodes -#if defined(__gnu_blrts__) || defined(__LIBCATAMOUNT__) || \ - defined(__CRAYXT_COMPUTE_LINUX_TARGET) -#include <string.h> // for strerror() -#define DYNAMICLOADER_DEFINED 1 - -namespace KWSYS_NAMESPACE { - -//---------------------------------------------------------------------------- -DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) -{ - return 0; -} +#elif defined(__MINT__) //---------------------------------------------------------------------------- -int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) -{ - if (!lib) { - return 0; - } - - return 1; -} - -//---------------------------------------------------------------------------- -DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( - DynamicLoader::LibraryHandle lib, const std::string& sym) -{ - return 0; -} - -//---------------------------------------------------------------------------- -const char* DynamicLoader::LastError() -{ - return "General error"; -} - -} // namespace KWSYS_NAMESPACE -#endif - -#ifdef __MINT__ -#define DYNAMICLOADER_DEFINED 1 +// Implementation for FreeMiNT on Atari #define _GNU_SOURCE /* for program_invocation_name */ #include <dld.h> #include <errno.h> @@ -447,14 +433,11 @@ const char* DynamicLoader::LastError() } } // namespace KWSYS_NAMESPACE -#endif -// --------------------------------------------------------------- -// 6. Implementation for default UNIX machines. -// if nothing has been defined then use this -#ifndef DYNAMICLOADER_DEFINED -#define DYNAMICLOADER_DEFINED 1 -// Setup for most unix machines +#else +//---------------------------------------------------------------------------- +// Default implementation for *NIX systems (including Mac OS X 10.3 and +// later) which use dlopen #include <dlfcn.h> namespace KWSYS_NAMESPACE { @@ -498,5 +481,4 @@ const char* DynamicLoader::LastError() } } // namespace KWSYS_NAMESPACE - #endif diff --git a/Source/kwsys/EncodingCXX.cxx b/Source/kwsys/EncodingCXX.cxx index 5c58bcb..e904c1a 100644 --- a/Source/kwsys/EncodingCXX.cxx +++ b/Source/kwsys/EncodingCXX.cxx @@ -125,12 +125,68 @@ char const* const* Encoding::CommandLineArguments::argv() const std::wstring Encoding::ToWide(const std::string& str) { - return ToWide(str.c_str()); + std::wstring wstr; +#if defined(_WIN32) + const int wlength = MultiByteToWideChar( + KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), NULL, 0); + if (wlength > 0) { + wchar_t* wdata = new wchar_t[wlength]; + int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), + int(str.size()), wdata, wlength); + if (r > 0) { + wstr = std::wstring(wdata, wlength); + } + delete[] wdata; + } +#else + size_t pos = 0; + size_t nullPos = 0; + do { + if (pos < str.size() && str.at(pos) != '\0') { + wstr += ToWide(str.c_str() + pos); + } + nullPos = str.find('\0', pos); + if (nullPos != str.npos) { + pos = nullPos + 1; + wstr += wchar_t('\0'); + } + } while (nullPos != str.npos); +#endif + return wstr; } std::string Encoding::ToNarrow(const std::wstring& str) { - return ToNarrow(str.c_str()); + std::string nstr; +#if defined(_WIN32) + int length = + WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), + int(str.size()), NULL, 0, NULL, NULL); + if (length > 0) { + char* data = new char[length]; + int r = + WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), + int(str.size()), data, length, NULL, NULL); + if (r > 0) { + nstr = std::string(data, length); + } + delete[] data; + } +#else + size_t pos = 0; + size_t nullPos = 0; + do { + if (pos < str.size() && str.at(pos) != '\0') { + nstr += ToNarrow(str.c_str() + pos); + } + nullPos = str.find(wchar_t('\0'), pos); + if (nullPos != str.npos) { + pos = nullPos + 1; + nstr += '\0'; + } + } while (nullPos != str.npos); +#endif + return nstr; } std::wstring Encoding::ToWide(const char* cstr) diff --git a/Source/kwsys/testConsoleBuf.cxx b/Source/kwsys/testConsoleBuf.cxx index bd58fb6..3b8cdab 100644 --- a/Source/kwsys/testConsoleBuf.cxx +++ b/Source/kwsys/testConsoleBuf.cxx @@ -18,6 +18,7 @@ #if defined(_WIN32) +#include <algorithm> #include <iomanip> #include <iostream> #include <stdexcept> @@ -318,6 +319,7 @@ static int testPipe() bytesRead == 0) { throw std::runtime_error("ReadFile#1 failed!"); } + buffer[bytesRead] = 0; if ((bytesRead < encodedTestString.size() + 1 + encodedInputTestString.size() && !ReadFile(outPipeRead, buffer + bytesRead, @@ -336,8 +338,12 @@ static int testPipe() bytesRead == 0) { throw std::runtime_error("ReadFile#3 failed!"); } - buffer2[bytesRead - 1] = 0; - didFail = encodedTestString.compare(buffer2) == 0 ? 0 : 1; + buffer2[bytesRead] = 0; + didFail = + encodedTestString.compare(0, encodedTestString.npos, buffer2, + encodedTestString.size()) == 0 + ? 0 + : 1; } if (didFail != 0) { std::cerr << "Pipe's output didn't match expected output!" @@ -423,23 +429,28 @@ static int testFile() bytesRead == 0) { throw std::runtime_error("ReadFile#1 failed!"); } - buffer[bytesRead - 1] = 0; + buffer[bytesRead] = 0; if (memcmp(buffer, encodedTestString.c_str(), encodedTestString.size()) == 0 && memcmp(buffer + encodedTestString.size() + 1, encodedInputTestString.c_str(), - encodedInputTestString.size() - 1) == 0) { + encodedInputTestString.size()) == 0) { bytesRead = 0; if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { throw std::runtime_error("SetFilePointer#2 failed!"); } + if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead, NULL) || bytesRead == 0) { throw std::runtime_error("ReadFile#2 failed!"); } - buffer2[bytesRead - 1] = 0; - didFail = encodedTestString.compare(buffer2) == 0 ? 0 : 1; + buffer2[bytesRead] = 0; + didFail = + encodedTestString.compare(0, encodedTestString.npos, buffer2, + encodedTestString.size()) == 0 + ? 0 + : 1; } if (didFail != 0) { std::cerr << "File's output didn't match expected output!" @@ -448,7 +459,7 @@ static int testFile() encodedTestString.size()); dumpBuffers<char>(encodedInputTestString.c_str(), buffer + encodedTestString.size() + 1, - encodedInputTestString.size() - 1); + encodedInputTestString.size()); dumpBuffers<char>(encodedTestString.c_str(), buffer2, encodedTestString.size()); } @@ -685,6 +696,7 @@ static int testConsole() throw std::runtime_error("ReadConsoleOutputCharacter failed!"); } std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString); + std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' '); std::wstring wideInputTestString = kwsys::Encoding::ToWide(encodedInputTestString); if (memcmp(outputBuffer, wideTestString.c_str(), @@ -757,8 +769,11 @@ int testConsoleBuf(int, char* []) return 1; } - encodedTestString = kwsys::Encoding::ToNarrow(UnicodeTestString); - encodedInputTestString = kwsys::Encoding::ToNarrow(UnicodeInputTestString); + encodedTestString = kwsys::Encoding::ToNarrow(std::wstring( + UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1)); + encodedInputTestString = kwsys::Encoding::ToNarrow( + std::wstring(UnicodeInputTestString, + sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1)); encodedInputTestString += "\n"; ret |= testPipe(); diff --git a/Source/kwsys/testConsoleBuf.hxx b/Source/kwsys/testConsoleBuf.hxx index 8891960..e93cb4f 100644 --- a/Source/kwsys/testConsoleBuf.hxx +++ b/Source/kwsys/testConsoleBuf.hxx @@ -11,7 +11,7 @@ static const wchar_t AfterOutputEventName[] = L"AfterOutputEvent"; // यूनिकोड είναι здорово! static const wchar_t UnicodeTestString[] = L"\u092F\u0942\u0928\u093F\u0915\u094B\u0921 " - L"\u03B5\u03AF\u03BD\u03B1\u03B9 " + L"\u03B5\u03AF\u03BD\0\u03B1\u03B9 " L"\u0437\u0434\u043E\u0440\u043E\u0432\u043E!"; #endif diff --git a/Source/kwsys/testConsoleBufChild.cxx b/Source/kwsys/testConsoleBufChild.cxx index 313323e..83bf545 100644 --- a/Source/kwsys/testConsoleBufChild.cxx +++ b/Source/kwsys/testConsoleBufChild.cxx @@ -28,7 +28,8 @@ int main(int argc, const char* argv[]) std::cout << argv[1] << std::endl; std::cerr << argv[1] << std::endl; } else { - std::string str = kwsys::Encoding::ToNarrow(UnicodeTestString); + std::string str = kwsys::Encoding::ToNarrow(std::wstring( + UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1)); std::cout << str << std::endl; std::cerr << str << std::endl; } diff --git a/Source/kwsys/testEncoding.cxx b/Source/kwsys/testEncoding.cxx index 996976f..03f2ec9 100644 --- a/Source/kwsys/testEncoding.cxx +++ b/Source/kwsys/testEncoding.cxx @@ -9,6 +9,7 @@ #include KWSYS_HEADER(Encoding.hxx) #include KWSYS_HEADER(Encoding.h) +#include <algorithm> #include <iostream> #include <locale.h> #include <stdlib.h> @@ -124,6 +125,35 @@ static int testRobustEncoding() return ret; } +static int testWithNulls() +{ + int ret = 0; + std::vector<std::string> strings; + strings.push_back(std::string("ab") + '\0' + 'c'); + strings.push_back(std::string("d") + '\0' + '\0' + 'e'); + strings.push_back(std::string() + '\0' + 'f'); + strings.push_back(std::string() + '\0' + '\0' + "gh"); + strings.push_back(std::string("ij") + '\0'); + strings.push_back(std::string("k") + '\0' + '\0'); + strings.push_back(std::string("\0\0\0\0", 4) + "lmn" + + std::string("\0\0\0\0", 4)); + for (std::vector<std::string>::iterator it = strings.begin(); + it != strings.end(); ++it) { + std::wstring wstr = kwsys::Encoding::ToWide(*it); + std::string str = kwsys::Encoding::ToNarrow(wstr); + std::string s(*it); + std::replace(s.begin(), s.end(), '\0', ' '); + std::cout << "'" << s << "' (" << it->size() << ")" << std::endl; + if (str != *it) { + std::replace(str.begin(), str.end(), '\0', ' '); + std::cout << "string with null was different: '" << str << "' (" + << str.size() << ")" << std::endl; + ret++; + } + } + return ret; +} + static int testCommandLineArguments() { int status = 0; @@ -165,6 +195,7 @@ int testEncoding(int, char* []) ret |= testHelloWorldEncoding(); ret |= testRobustEncoding(); ret |= testCommandLineArguments(); + ret |= testWithNulls(); return ret; } |