diff options
Diffstat (limited to 'Source')
67 files changed, 2467 insertions, 687 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index ddcdd7e..d6e0096 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -358,6 +358,8 @@ set(SRCS cmLocalCommonGenerator.h cmLocalGenerator.cxx cmLocalGenerator.h + cmPlaceholderExpander.cxx + cmPlaceholderExpander.h cmRulePlaceholderExpander.cxx cmRulePlaceholderExpander.h cmLocalUnixMakefileGenerator3.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 7cd4433..225dc49 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,8 +1,8 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 23) -set(CMake_VERSION_PATCH 0) -set(CMake_VERSION_RC 2) +set(CMake_VERSION_PATCH 20220303) +#set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) # Start with the full version number used in tags. It has no dev info. diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx index ae8acc5..b7d5be5 100644 --- a/Source/Checks/cm_cxx_filesystem.cxx +++ b/Source/Checks/cm_cxx_filesystem.cxx @@ -3,6 +3,8 @@ int main() { + return 1; + std::filesystem::path p0(L"/a/b/c"); std::filesystem::path p1("/a/b/c"); diff --git a/Source/CursesDialog/form/frm_driver.c b/Source/CursesDialog/form/frm_driver.c index 112ab08..9cbb12f 100644 --- a/Source/CursesDialog/form/frm_driver.c +++ b/Source/CursesDialog/form/frm_driver.c @@ -1456,7 +1456,7 @@ static int IFN_Beginning_Of_Field(FORM * form) | | Description : Place the cursor after the last non-pad character in | the field. If the field occupies the last position in -| the buffer, the cursos is positioned on the last +| the buffer, the cursor is positioned on the last | character. | | Return Values : E_OK - success diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index 9e0d80c..cfde37c 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx @@ -95,6 +95,19 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, , Verbose(false) , Format(format) { + // Upstream fixed an issue with their integer parsing in 3.4.0 + // which would cause spurious errors to be raised from `strtoull`. + + if (numThreads < 1) { + int upperLimit = (numThreads == 0) ? std::numeric_limits<int>::max() + : std::abs(numThreads); + + numThreads = + cm::clamp<int>(std::thread::hardware_concurrency(), 1, upperLimit); + } + + std::string sNumThreads = std::to_string(numThreads); + switch (c) { case CompressNone: if (archive_write_add_filter_none(this->Archive) != ARCHIVE_OK) { @@ -150,36 +163,23 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, return; } - { #if ARCHIVE_VERSION_NUMBER >= 3004000 - // Upstream fixed an issue with their integer parsing in 3.4.0 - // which would cause spurious errors to be raised from `strtoull`. - - if (numThreads < 1) { - int upperLimit = (numThreads == 0) ? std::numeric_limits<int>::max() - : std::abs(numThreads); - - numThreads = - cm::clamp<int>(std::thread::hardware_concurrency(), 1, upperLimit); - } # ifdef _AIX - // FIXME: Using more than 2 threads creates an empty archive. - // Enforce this limit pending further investigation. - numThreads = std::min(numThreads, 2); + // FIXME: Using more than 2 threads creates an empty archive. + // Enforce this limit pending further investigation. + if (numThreads > 2) { + numThreads = 2; + sNumThreads = std::to_string(numThreads); + } # endif - - std::string sNumThreads = std::to_string(numThreads); - - if (archive_write_set_filter_option(this->Archive, "xz", "threads", - sNumThreads.c_str()) != - ARCHIVE_OK) { - this->Error = cmStrCat("archive_compressor_xz_options: ", - cm_archive_error_string(this->Archive)); - return; - } -#endif + if (archive_write_set_filter_option(this->Archive, "xz", "threads", + sNumThreads.c_str()) != ARCHIVE_OK) { + this->Error = cmStrCat("archive_compressor_xz_options: ", + cm_archive_error_string(this->Archive)); + return; } +#endif break; case CompressZstd: @@ -188,6 +188,15 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, cm_archive_error_string(this->Archive)); return; } + +#if ARCHIVE_VERSION_NUMBER >= 3006000 + if (archive_write_set_filter_option(this->Archive, "zstd", "threads", + sNumThreads.c_str()) != ARCHIVE_OK) { + this->Error = cmStrCat("archive_compressor_zstd_options: ", + cm_archive_error_string(this->Archive)); + return; + } +#endif break; } diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index 789c78d..27d8cb8 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -84,7 +84,8 @@ bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args, for (size_t i = startArg; i < args.size(); ++i) { funcArgs.emplace_back(args[i].Value, args[i].Delim, context.Line); } - cmListFileFunction func{ callCommand, context.Line, std::move(funcArgs) }; + cmListFileFunction func{ callCommand, context.Line, context.Line, + std::move(funcArgs) }; if (defer) { if (defer->Id.empty()) { diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 1b11f20..abec968 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -432,7 +432,7 @@ static int CCONV cmExecuteCommand(void* arg, const char* name, int numArgs, lffArgs.emplace_back(args[i], cmListFileArgument::Quoted, 0); } - cmListFileFunction lff{ name, 0, std::move(lffArgs) }; + cmListFileFunction lff{ name, 0, 0, std::move(lffArgs) }; cmExecutionStatus status(*mf); return mf->ExecuteCommand(lff, status); } diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 8d5ce7e..129ef4b 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -101,7 +101,8 @@ void cmCommonTargetGenerator::AppendFortranFormatFlags( } void cmCommonTargetGenerator::AppendFortranPreprocessFlags( - std::string& flags, cmSourceFile const& source) + std::string& flags, cmSourceFile const& source, + PreprocessFlagsRequired requires_pp) { const std::string srcpp = source.GetSafeProperty("Fortran_PREPROCESS"); cmOutputConverter::FortranPreprocess preprocess = @@ -114,7 +115,9 @@ void cmCommonTargetGenerator::AppendFortranPreprocessFlags( const char* var = nullptr; switch (preprocess) { case cmOutputConverter::FortranPreprocess::Needed: - var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON"; + if (requires_pp == PreprocessFlagsRequired::YES) { + var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON"; + } break; case cmOutputConverter::FortranPreprocess::NotNeeded: var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF"; diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h index baa36c9..5aba1c6 100644 --- a/Source/cmCommonTargetGenerator.h +++ b/Source/cmCommonTargetGenerator.h @@ -46,8 +46,14 @@ protected: void AppendFortranFormatFlags(std::string& flags, cmSourceFile const& source); - void AppendFortranPreprocessFlags(std::string& flags, - cmSourceFile const& source); + enum class PreprocessFlagsRequired + { + YES, + NO + }; + void AppendFortranPreprocessFlags( + std::string& flags, cmSourceFile const& source, + PreprocessFlagsRequired requires_pp = PreprocessFlagsRequired::YES); virtual void AddIncludeFlags(std::string& flags, std::string const& lang, const std::string& config) = 0; diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index 370ddff..a4dc01b 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -11,13 +11,18 @@ #include <utility> #include <cm/memory> +#include <cm/string_view> +#include <cmext/string_view> #include "cmComputeComponentGraph.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmRange.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -174,8 +179,62 @@ items that we know the linker will re-use automatically (shared libs). */ +namespace { +// LINK_LIBRARY helpers +const auto LL_BEGIN = "<LINK_LIBRARY:"_s; +const auto LL_END = "</LINK_LIBRARY:"_s; + +inline std::string ExtractFeature(std::string const& item) +{ + return item.substr(LL_BEGIN.length(), + item.find('>', LL_BEGIN.length()) - LL_BEGIN.length()); +} + +bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage, + std::string const& feature) +{ + auto featureSupported = cmStrCat( + "CMAKE_", linkLanguage, "_LINK_LIBRARY_USING_", feature, "_SUPPORTED"); + if (makefile->GetDefinition(featureSupported).IsOn()) { + return true; + } + + featureSupported = + cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature, "_SUPPORTED"); + return makefile->GetDefinition(featureSupported).IsOn(); +} + +// LINK_GROUP helpers +const auto LG_BEGIN = "<LINK_GROUP:"_s; +const auto LG_END = "</LINK_GROUP:"_s; + +inline std::string ExtractGroupFeature(std::string const& item) +{ + return item.substr(LG_BEGIN.length(), + item.find(':', LG_BEGIN.length()) - LG_BEGIN.length()); +} + +bool IsGroupFeatureSupported(cmMakefile* makefile, + std::string const& linkLanguage, + std::string const& feature) +{ + auto featureSupported = cmStrCat( + "CMAKE_", linkLanguage, "_LINK_GROUP_USING_", feature, "_SUPPORTED"); + if (makefile->GetDefinition(featureSupported).IsOn()) { + return true; + } + + featureSupported = + cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED"); + return makefile->GetDefinition(featureSupported).IsOn(); +} +} + +const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT"; + cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, - const std::string& config) + const std::string& config, + const std::string& linkLanguage) { // Store context information. this->Target = target; @@ -183,6 +242,50 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, this->GlobalGenerator = this->Target->GetLocalGenerator()->GetGlobalGenerator(); this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance(); + this->LinkLanguage = linkLanguage; + + // target oriented feature override property takes precedence over + // global override property + cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s; + auto const& keys = this->Target->GetPropertyKeys(); + std::for_each( + keys.cbegin(), keys.cend(), + [this, &lloPrefix, &config, &linkLanguage](std::string const& key) { + if (cmHasPrefix(key, lloPrefix)) { + if (cmValue feature = this->Target->GetProperty(key)) { + if (!feature->empty() && key.length() > lloPrefix.length()) { + auto item = key.substr(lloPrefix.length()); + cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(), + this->Target, + "LINK_LIBRARY_OVERRIDE", + nullptr, nullptr }; + auto overrideFeature = cmGeneratorExpression::Evaluate( + feature, this->Target->GetLocalGenerator(), config, this->Target, + &dag, this->Target, linkLanguage); + this->LinkLibraryOverride.emplace(item, overrideFeature); + } + } + } + }); + // global override property + if (cmValue linkLibraryOverride = + this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) { + cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, + "LINK_LIBRARY_OVERRIDE", nullptr, + nullptr }; + auto overrideValue = cmGeneratorExpression::Evaluate( + linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag, + target, linkLanguage); + + auto overrideList = cmTokenize(overrideValue, ","_s); + if (overrideList.size() >= 2) { + auto const& feature = overrideList.front(); + for_each(overrideList.cbegin() + 1, overrideList.cend(), + [this, &feature](std::string const& item) { + this->LinkLibraryOverride.emplace(item, feature); + }); + } + } // The configuration being linked. this->HasConfig = !config.empty(); @@ -234,6 +337,11 @@ cmComputeLinkDepends::Compute() // Infer dependencies of targets for which they were not known. this->InferDependencies(); + // finalize groups dependencies + // All dependencies which are raw items must be replaced by the group + // it belongs to, if any. + this->UpdateGroupDependencies(); + // Cleanup the constraint graph. this->CleanConstraintGraph(); @@ -248,8 +356,21 @@ cmComputeLinkDepends::Compute() this->DisplayConstraintGraph(); } + // Compute the DAG of strongly connected components. The algorithm + // used by cmComputeComponentGraph should identify the components in + // the same order in which the items were originally discovered in + // the BFS. This should preserve the original order when no + // constraints disallow it. + this->CCG = + cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph); + this->CCG->Compute(); + + if (!this->CheckCircularDependencies()) { + return this->FinalLinkEntries; + } + // Compute the final ordering. - this->OrderLinkEntires(); + this->OrderLinkEntries(); // Compute the final set of link entries. // Iterate in reverse order so we can keep only the last occurrence @@ -273,6 +394,29 @@ cmComputeLinkDepends::Compute() // Reverse the resulting order since we iterated in reverse. std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end()); + // Expand group items + if (!this->GroupItems.empty()) { + for (const auto& group : this->GroupItems) { + const LinkEntry& groupEntry = this->EntryList[group.first]; + auto it = this->FinalLinkEntries.begin(); + while (true) { + it = std::find_if(it, this->FinalLinkEntries.end(), + [&groupEntry](const LinkEntry& entry) -> bool { + return groupEntry.Item == entry.Item; + }); + if (it == this->FinalLinkEntries.end()) { + break; + } + it->Item.Value = "</LINK_GROUP>"; + for (auto i = group.second.rbegin(); i != group.second.rend(); ++i) { + it = this->FinalLinkEntries.insert(it, this->EntryList[*i]); + } + it = this->FinalLinkEntries.insert(it, groupEntry); + it->Item.Value = "<LINK_GROUP>"; + } + } + } + // Display the final set. if (this->DebugMode) { this->DisplayFinalEntries(); @@ -281,76 +425,91 @@ cmComputeLinkDepends::Compute() return this->FinalLinkEntries; } -std::map<cmLinkItem, int>::iterator cmComputeLinkDepends::AllocateLinkEntry( - cmLinkItem const& item) +std::string const& cmComputeLinkDepends::GetCurrentFeature( + std::string const& item, std::string const& defaultFeature) const +{ + auto it = this->LinkLibraryOverride.find(item); + return it == this->LinkLibraryOverride.end() ? defaultFeature : it->second; +} + +std::pair<std::map<cmLinkItem, int>::iterator, bool> +cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item) { std::map<cmLinkItem, int>::value_type index_entry( item, static_cast<int>(this->EntryList.size())); - auto lei = this->LinkEntryIndex.insert(index_entry).first; - this->EntryList.emplace_back(); - this->InferredDependSets.emplace_back(); - this->EntryConstraintGraph.emplace_back(); + auto lei = this->LinkEntryIndex.insert(index_entry); + if (lei.second) { + this->EntryList.emplace_back(); + this->InferredDependSets.emplace_back(); + this->EntryConstraintGraph.emplace_back(); + } return lei; } -int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item) +std::pair<int, bool> cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item, + int groupIndex) { + // Allocate a spot for the item entry. + auto lei = this->AllocateLinkEntry(item); + // Check if the item entry has already been added. - auto lei = this->LinkEntryIndex.find(item); - if (lei != this->LinkEntryIndex.end()) { + if (!lei.second) { // Yes. We do not need to follow the item's dependencies again. - return lei->second; + return { lei.first->second, false }; } - // Allocate a spot for the item entry. - lei = this->AllocateLinkEntry(item); - // Initialize the item entry. - int index = lei->second; + int index = lei.first->second; LinkEntry& entry = this->EntryList[index]; entry.Item = BT<std::string>(item.AsStr(), item.Backtrace); entry.Target = item.Target; - entry.IsFlag = (!entry.Target && entry.Item.Value[0] == '-' && - entry.Item.Value[1] != 'l' && - entry.Item.Value.substr(0, 10) != "-framework"); + if (!entry.Target && entry.Item.Value[0] == '-' && + entry.Item.Value[1] != 'l' && + entry.Item.Value.substr(0, 10) != "-framework") { + entry.Kind = LinkEntry::Flag; + } else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) && + cmHasSuffix(entry.Item.Value, '>')) { + entry.Kind = LinkEntry::Group; + } - // If the item has dependencies queue it to follow them. - if (entry.Target) { - // Target dependencies are always known. Follow them. - BFSEntry qe = { index, nullptr }; - this->BFSQueue.push(qe); - } else { - // Look for an old-style <item>_LIB_DEPENDS variable. - std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS"); - if (cmValue val = this->Makefile->GetDefinition(var)) { - // The item dependencies are known. Follow them. - BFSEntry qe = { index, val->c_str() }; + if (entry.Kind != LinkEntry::Group) { + // If the item has dependencies queue it to follow them. + if (entry.Target) { + // Target dependencies are always known. Follow them. + BFSEntry qe = { index, groupIndex, nullptr }; this->BFSQueue.push(qe); - } else if (!entry.IsFlag) { - // The item dependencies are not known. We need to infer them. - this->InferredDependSets[index].Initialized = true; + } else { + // Look for an old-style <item>_LIB_DEPENDS variable. + std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS"); + if (cmValue val = this->Makefile->GetDefinition(var)) { + // The item dependencies are known. Follow them. + BFSEntry qe = { index, groupIndex, val->c_str() }; + this->BFSQueue.push(qe); + } else if (entry.Kind != LinkEntry::Flag) { + // The item dependencies are not known. We need to infer them. + this->InferredDependSets[index].Initialized = true; + } } } - return index; + return { index, true }; } void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item) { + // Allocate a spot for the item entry. + auto lei = this->AllocateLinkEntry(item); + // Check if the item entry has already been added. - auto lei = this->LinkEntryIndex.find(item); - if (lei != this->LinkEntryIndex.end()) { + if (!lei.second) { return; } - // Allocate a spot for the item entry. - lei = this->AllocateLinkEntry(item); - // Initialize the item entry. - int index = lei->second; + int index = lei.first->second; LinkEntry& entry = this->EntryList[index]; entry.Item = BT<std::string>(item.AsStr(), item.Backtrace); - entry.IsObject = true; + entry.Kind = LinkEntry::Object; // Record explicitly linked object files separately. this->ObjectEntries.emplace_back(index); @@ -359,8 +518,8 @@ void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item) void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe) { // Get this entry representation. - int depender_index = qe.Index; - LinkEntry const& entry = this->EntryList[depender_index]; + int depender_index = qe.GroupIndex == -1 ? qe.Index : qe.GroupIndex; + LinkEntry const& entry = this->EntryList[qe.Index]; // Follow the item's dependencies. if (entry.Target) { @@ -423,25 +582,24 @@ void cmComputeLinkDepends::QueueSharedDependencies( void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep) { - // Check if the target already has an entry. - auto lei = this->LinkEntryIndex.find(dep.Item); - if (lei == this->LinkEntryIndex.end()) { - // Allocate a spot for the item entry. - lei = this->AllocateLinkEntry(dep.Item); + // Allocate a spot for the item entry. + auto lei = this->AllocateLinkEntry(dep.Item); + int index = lei.first->second; + // Check if the target does not already has an entry. + if (lei.second) { // Initialize the item entry. - LinkEntry& entry = this->EntryList[lei->second]; + LinkEntry& entry = this->EntryList[index]; entry.Item = BT<std::string>(dep.Item.AsStr(), dep.Item.Backtrace); entry.Target = dep.Item.Target; // This item was added specifically because it is a dependent // shared library. It may get special treatment // in cmComputeLinkInformation. - entry.IsSharedDep = true; + entry.Kind = LinkEntry::SharedDep; } // Get the link entry for this target. - int index = lei->second; LinkEntry& entry = this->EntryList[index]; // This shared library dependency must follow the item that listed @@ -541,6 +699,11 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index, { // Track inferred dependency sets implied by this list. std::map<int, DependSet> dependSets; + std::string feature = LinkEntry::DEFAULT; + + bool inGroup = false; + std::pair<int, bool> groupIndex{ -1, false }; + std::vector<int> groupItems; // Loop over the libraries linked directly by the depender. for (T const& l : libs) { @@ -551,35 +714,233 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index, continue; } + if (cmHasPrefix(item.AsStr(), LL_BEGIN) && + cmHasSuffix(item.AsStr(), '>')) { + feature = ExtractFeature(item.AsStr()); + // emit a warning if an undefined feature is used as part of + // an imported target + if (depender_index >= 0) { + const auto& depender = this->EntryList[depender_index]; + if (depender.Target != nullptr && depender.Target->IsImported() && + !IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) { + this->CMakeInstance->IssueMessage( + MessageType::AUTHOR_ERROR, + cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(), + "' uses the generator-expression '$<LINK_LIBRARY>' with " + "the feature '", + feature, + "', which is undefined or unsupported.\nDid you miss to " + "define it by setting variables \"CMAKE_", + this->LinkLanguage, "_LINK_LIBRARY_USING_", feature, + "\" and \"CMAKE_", this->LinkLanguage, + "_LINK_LIBRARY_USING_", feature, "_SUPPORTED\"?"), + this->Target->GetBacktrace()); + } + } + continue; + } + if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) { + feature = LinkEntry::DEFAULT; + continue; + } + + if (cmHasPrefix(item.AsStr(), LG_BEGIN) && + cmHasSuffix(item.AsStr(), '>')) { + groupIndex = this->AddLinkEntry(item); + if (groupIndex.second) { + LinkEntry& entry = this->EntryList[groupIndex.first]; + entry.Feature = ExtractGroupFeature(item.AsStr()); + } + inGroup = true; + if (depender_index >= 0) { + this->EntryConstraintGraph[depender_index].emplace_back( + groupIndex.first, false, false, cmListFileBacktrace()); + } else { + // This is a direct dependency of the target being linked. + this->OriginalEntries.push_back(groupIndex.first); + } + continue; + } + + int dependee_index; + + if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) { + dependee_index = groupIndex.first; + if (groupIndex.second) { + this->GroupItems.emplace(groupIndex.first, groupItems); + } + inGroup = false; + groupIndex = std::make_pair(-1, false); + groupItems.clear(); + continue; + } + + if (depender_index >= 0 && inGroup) { + const auto& depender = this->EntryList[depender_index]; + const auto& groupFeature = this->EntryList[groupIndex.first].Feature; + if (depender.Target != nullptr && depender.Target->IsImported() && + !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage, + groupFeature)) { + this->CMakeInstance->IssueMessage( + MessageType::AUTHOR_ERROR, + cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(), + "' uses the generator-expression '$<LINK_GROUP>' with " + "the feature '", + groupFeature, + "', which is undefined or unsupported.\nDid you miss to " + "define it by setting variables \"CMAKE_", + this->LinkLanguage, "_LINK_GROUP_USING_", groupFeature, + "\" and \"CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_", + groupFeature, "_SUPPORTED\"?"), + this->Target->GetBacktrace()); + } + } + // Add a link entry for this item. - int dependee_index = this->AddLinkEntry(l); + auto ale = this->AddLinkEntry(item, groupIndex.first); + dependee_index = ale.first; + LinkEntry& entry = this->EntryList[dependee_index]; + auto const& itemFeature = + this->GetCurrentFeature(entry.Item.Value, feature); + if (inGroup && ale.second && entry.Target != nullptr && + (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY || + entry.Target->GetType() == + cmStateEnums::TargetType::INTERFACE_LIBRARY)) { + const auto& groupFeature = this->EntryList[groupIndex.first].Feature; + this->CMakeInstance->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat( + "The feature '", groupFeature, + "', specified as part of a generator-expression " + "'$", + LG_BEGIN, groupFeature, ">', will not be applied to the ", + (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY + ? "OBJECT" + : "INTERFACE"), + " library '", entry.Item.Value, "'."), + this->Target->GetBacktrace()); + } + if (itemFeature != LinkEntry::DEFAULT) { + if (ale.second) { + // current item not yet defined + if (entry.Target != nullptr && + (entry.Target->GetType() == + cmStateEnums::TargetType::OBJECT_LIBRARY || + entry.Target->GetType() == + cmStateEnums::TargetType::INTERFACE_LIBRARY)) { + this->CMakeInstance->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("The feature '", feature, + "', specified as part of a generator-expression " + "'$", + LL_BEGIN, feature, ">', will not be applied to the ", + (entry.Target->GetType() == + cmStateEnums::TargetType::OBJECT_LIBRARY + ? "OBJECT" + : "INTERFACE"), + " library '", entry.Item.Value, "'."), + this->Target->GetBacktrace()); + } else { + entry.Feature = itemFeature; + } + } + } - // The dependee must come after the depender. - if (depender_index >= 0) { - this->EntryConstraintGraph[depender_index].emplace_back( - dependee_index, false, false, cmListFileBacktrace()); - } else { - // This is a direct dependency of the target being linked. - this->OriginalEntries.push_back(dependee_index); - } - - // Update the inferred dependencies for earlier items. - for (auto& dependSet : dependSets) { - // Add this item to the inferred dependencies of other items. - // Target items are never inferred dependees because unknown - // items are outside libraries that should not be depending on - // targets. - if (!this->EntryList[dependee_index].Target && - !this->EntryList[dependee_index].IsFlag && - dependee_index != dependSet.first) { - dependSet.second.insert(dependee_index); + bool supportedItem = entry.Target == nullptr || + (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY && + entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY); + + if (supportedItem) { + if (inGroup) { + const auto& currentFeature = this->EntryList[groupIndex.first].Feature; + for (const auto& g : this->GroupItems) { + const auto& groupFeature = this->EntryList[g.first].Feature; + if (groupFeature == currentFeature) { + continue; + } + if (std::find(g.second.cbegin(), g.second.cend(), dependee_index) != + g.second.cend()) { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Impossible to link target '", this->Target->GetName(), + "' because the link item '", entry.Item.Value, + "', specified with the group feature '", currentFeature, + '\'', ", has already occurred with the feature '", + groupFeature, '\'', ", which is not allowed."), + this->Target->GetBacktrace()); + continue; + } + } + } + if (entry.Feature != itemFeature) { + // incompatibles features occurred + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Impossible to link target '", this->Target->GetName(), + "' because the link item '", entry.Item.Value, + "', specified ", + (itemFeature == LinkEntry::DEFAULT + ? "without any feature or 'DEFAULT' feature" + : cmStrCat("with the feature '", itemFeature, '\'')), + ", has already occurred ", + (entry.Feature == LinkEntry::DEFAULT + ? "without any feature or 'DEFAULT' feature" + : cmStrCat("with the feature '", entry.Feature, '\'')), + ", which is not allowed."), + this->Target->GetBacktrace()); } } - // If this item needs to have dependencies inferred, do so. - if (this->InferredDependSets[dependee_index].Initialized) { - // Make sure an entry exists to hold the set for the item. - dependSets[dependee_index]; + if (inGroup) { + // store item index for dependencies handling + groupItems.push_back(dependee_index); + } else { + std::vector<int> indexes; + bool entryHandled = false; + // search any occurrence of the library in already defined groups + for (const auto& group : this->GroupItems) { + for (auto index : group.second) { + if (entry.Item.Value == this->EntryList[index].Item.Value) { + indexes.push_back(group.first); + entryHandled = true; + break; + } + } + } + if (!entryHandled) { + indexes.push_back(dependee_index); + } + + for (auto index : indexes) { + // The dependee must come after the depender. + if (depender_index >= 0) { + this->EntryConstraintGraph[depender_index].emplace_back( + index, false, false, cmListFileBacktrace()); + } else { + // This is a direct dependency of the target being linked. + this->OriginalEntries.push_back(index); + } + + // Update the inferred dependencies for earlier items. + for (auto& dependSet : dependSets) { + // Add this item to the inferred dependencies of other items. + // Target items are never inferred dependees because unknown + // items are outside libraries that should not be depending on + // targets. + if (!this->EntryList[index].Target && + this->EntryList[index].Kind != LinkEntry::Flag && + this->EntryList[index].Kind != LinkEntry::Group && + dependee_index != dependSet.first) { + dependSet.second.insert(index); + } + } + + // If this item needs to have dependencies inferred, do so. + if (this->InferredDependSets[index].Initialized) { + // Make sure an entry exists to hold the set for the item. + dependSets[index]; + } + } } } @@ -642,6 +1003,36 @@ void cmComputeLinkDepends::InferDependencies() } } +void cmComputeLinkDepends::UpdateGroupDependencies() +{ + if (this->GroupItems.empty()) { + return; + } + + // Walks through all entries of the constraint graph to replace dependencies + // over raw items by the group it belongs to, if any. + for (auto& edgeList : this->EntryConstraintGraph) { + for (auto& edge : edgeList) { + int index = edge; + if (this->EntryList[index].Kind == LinkEntry::Group || + this->EntryList[index].Kind == LinkEntry::Flag || + this->EntryList[index].Kind == LinkEntry::Object) { + continue; + } + // search the item in the defined groups + for (const auto& groupItems : this->GroupItems) { + auto pos = std::find(groupItems.second.cbegin(), + groupItems.second.cend(), index); + if (pos != groupItems.second.cend()) { + // replace lib dependency by the group it belongs to + edge = cmGraphEdge{ groupItems.first, false, false, + cmListFileBacktrace() }; + } + } + } + } +} + void cmComputeLinkDepends::CleanConstraintGraph() { for (cmGraphEdgeList& edgeList : this->EntryConstraintGraph) { @@ -655,6 +1046,76 @@ void cmComputeLinkDepends::CleanConstraintGraph() } } +bool cmComputeLinkDepends::CheckCircularDependencies() const +{ + std::vector<NodeList> const& components = this->CCG->GetComponents(); + int nc = static_cast<int>(components.size()); + for (int c = 0; c < nc; ++c) { + // Get the current component. + NodeList const& nl = components[c]; + + // Skip trivial components. + if (nl.size() < 2) { + continue; + } + + // no group must be evolved + bool cycleDetected = false; + for (int ni : nl) { + if (this->EntryList[ni].Kind == LinkEntry::Group) { + cycleDetected = true; + break; + } + } + if (!cycleDetected) { + continue; + } + + // Construct the error message. + auto formatItem = [](LinkEntry const& entry) -> std::string { + if (entry.Kind == LinkEntry::Group) { + auto items = + entry.Item.Value.substr(entry.Item.Value.find(':', 12) + 1); + items.pop_back(); + std::replace(items.begin(), items.end(), '|', ','); + return cmStrCat("group \"", ExtractGroupFeature(entry.Item.Value), + ":{", items, "}\""); + } + return cmStrCat('"', entry.Item.Value, '"'); + }; + + std::ostringstream e; + e << "The inter-target dependency graph, for the target \"" + << this->Target->GetName() + << "\", contains the following strongly connected component " + "(cycle):\n"; + std::vector<int> const& cmap = this->CCG->GetComponentMap(); + for (int i : nl) { + // Get the depender. + LinkEntry const& depender = this->EntryList[i]; + + // Describe the depender. + e << " " << formatItem(depender) << "\n"; + + // List its dependencies that are inside the component. + EdgeList const& el = this->EntryConstraintGraph[i]; + for (cmGraphEdge const& ni : el) { + int j = ni; + if (cmap[j] == c) { + LinkEntry const& dependee = this->EntryList[j]; + e << " depends on " << formatItem(dependee) << "\n"; + } + } + } + this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(), + this->Target->GetBacktrace()); + + return false; + } + + return true; +} + void cmComputeLinkDepends::DisplayConstraintGraph() { // Display the graph nodes and their edges. @@ -667,17 +1128,8 @@ void cmComputeLinkDepends::DisplayConstraintGraph() fprintf(stderr, "%s\n", e.str().c_str()); } -void cmComputeLinkDepends::OrderLinkEntires() +void cmComputeLinkDepends::OrderLinkEntries() { - // Compute the DAG of strongly connected components. The algorithm - // used by cmComputeComponentGraph should identify the components in - // the same order in which the items were originally discovered in - // the BFS. This should preserve the original order when no - // constraints disallow it. - this->CCG = - cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph); - this->CCG->Compute(); - // The component graph is guaranteed to be acyclic. Start a DFS // from every entry to compute a topological order for the // components. @@ -867,12 +1319,23 @@ int cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl) void cmComputeLinkDepends::DisplayFinalEntries() { fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str()); + char space[] = " "; + int count = 2; for (LinkEntry const& lei : this->FinalLinkEntries) { - if (lei.Target) { - fprintf(stderr, " target [%s]\n", lei.Target->GetName().c_str()); + if (lei.Kind == LinkEntry::Group) { + fprintf(stderr, " %s group", + lei.Item.Value == "<LINK_GROUP>" ? "start" : "end"); + count = lei.Item.Value == "<LINK_GROUP>" ? 4 : 2; + } else if (lei.Target) { + fprintf(stderr, "%*starget [%s]", count, space, + lei.Target->GetName().c_str()); } else { - fprintf(stderr, " item [%s]\n", lei.Item.Value.c_str()); + fprintf(stderr, "%*sitem [%s]", count, space, lei.Item.Value.c_str()); + } + if (lei.Feature != LinkEntry::DEFAULT) { + fprintf(stderr, ", feature [%s]", lei.Feature.c_str()); } + fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index 72316f1..8cc916a 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -9,6 +9,7 @@ #include <queue> #include <set> #include <string> +#include <utility> #include <vector> #include "cmGraphAdjacencyList.h" @@ -29,7 +30,8 @@ class cmComputeLinkDepends { public: cmComputeLinkDepends(cmGeneratorTarget const* target, - const std::string& config); + const std::string& config, + const std::string& linkLanguage); ~cmComputeLinkDepends(); cmComputeLinkDepends(const cmComputeLinkDepends&) = delete; @@ -38,11 +40,32 @@ public: // Basic information about each link item. struct LinkEntry { + LinkEntry() = default; + LinkEntry(BT<std::string> item, cmGeneratorTarget const* target = nullptr) + : Item(std::move(item)) + , Target(target) + { + } + + static const std::string DEFAULT; + + enum EntryKind + { + Library, + Object, + SharedDep, + Flag, + // The following member is for the management of items specified + // through genex $<LINK_GROUP:...> + Group + }; + BT<std::string> Item; cmGeneratorTarget const* Target = nullptr; - bool IsSharedDep = false; - bool IsFlag = false; - bool IsObject = false; + EntryKind Kind = Library; + // The following member is for the management of items specified + // through genex $<LINK_LIBRARY:...> + std::string Feature = std::string(DEFAULT); }; using EntryVector = std::vector<LinkEntry>; @@ -60,12 +83,18 @@ private: cmMakefile* Makefile; cmGlobalGenerator const* GlobalGenerator; cmake* CMakeInstance; + std::string LinkLanguage; std::string Config; EntryVector FinalLinkEntries; + std::map<std::string, std::string> LinkLibraryOverride; + + std::string const& GetCurrentFeature( + std::string const& item, std::string const& defaultFeature) const; - std::map<cmLinkItem, int>::iterator AllocateLinkEntry( + std::pair<std::map<cmLinkItem, int>::iterator, bool> AllocateLinkEntry( cmLinkItem const& item); - int AddLinkEntry(cmLinkItem const& item); + std::pair<int, bool> AddLinkEntry(cmLinkItem const& item, + int groupIndex = -1); void AddLinkObject(cmLinkItem const& item); void AddVarLinkEntries(int depender_index, const char* value); void AddDirectLinkEntries(); @@ -78,10 +107,14 @@ private: std::vector<LinkEntry> EntryList; std::map<cmLinkItem, int> LinkEntryIndex; + // map storing, for each group, the list of items + std::map<int, std::vector<int>> GroupItems; + // BFS of initial dependencies. struct BFSEntry { int Index; + int GroupIndex; const char* LibDepends; }; std::queue<BFSEntry> BFSQueue; @@ -114,16 +147,20 @@ private: std::vector<DependSetList> InferredDependSets; void InferDependencies(); + // To finalize dependencies over groups in place of raw items + void UpdateGroupDependencies(); + // Ordering constraint graph adjacency list. using NodeList = cmGraphNodeList; using EdgeList = cmGraphEdgeList; using Graph = cmGraphAdjacencyList; Graph EntryConstraintGraph; void CleanConstraintGraph(); + bool CheckCircularDependencies() const; void DisplayConstraintGraph(); // Ordering algorithm. - void OrderLinkEntires(); + void OrderLinkEntries(); std::vector<char> ComponentVisited; std::vector<int> ComponentOrder; diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 2ff91fe..67214f1 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -8,7 +8,9 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmComputeLinkDepends.h" #include "cmGeneratorTarget.h" @@ -18,7 +20,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOrderDirectories.h" -#include "cmOutputConverter.h" +#include "cmPlaceholderExpander.h" #include "cmPolicies.h" #include "cmState.h" #include "cmStateTypes.h" @@ -344,6 +346,29 @@ cmComputeLinkInformation::cmComputeLinkInformation( this->LinkWithRuntimePath = this->Makefile->IsOn(var); } + // Define some Feature descriptors to handle standard library and object link + if (!this->GetLibLinkFileFlag().empty()) { + this->LibraryFeatureDescriptors.emplace( + "__CMAKE_LINK_LIBRARY", + LibraryFeatureDescriptor{ + "__CMAKE_LINK_LIBRARY", + cmStrCat(this->GetLibLinkFileFlag(), "<LIBRARY>") }); + } + if (!this->GetObjLinkFileFlag().empty()) { + this->LibraryFeatureDescriptors.emplace( + "__CMAKE_LINK_OBJECT", + LibraryFeatureDescriptor{ + "__CMAKE_LINK_OBJECT", + cmStrCat(this->GetObjLinkFileFlag(), "<LIBRARY>") }); + } + if (!this->LoaderFlag->empty()) { + // Define a Feature descriptor for the link of an executable with exports + this->LibraryFeatureDescriptors.emplace( + "__CMAKE_LINK_EXECUTABLE", + LibraryFeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE", + cmStrCat(this->LoaderFlag, "<LIBRARY>") }); + } + // Check the platform policy for missing soname case. this->NoSONameUsesPath = this->Makefile->IsOn("CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME"); @@ -409,6 +434,10 @@ cmComputeLinkInformation::cmComputeLinkInformation( cmComputeLinkInformation::~cmComputeLinkInformation() = default; +namespace { +const std::string& DEFAULT = cmComputeLinkDepends::LinkEntry::DEFAULT; +} + void cmComputeLinkInformation::AppendValues( std::string& result, std::vector<BT<std::string>>& values) { @@ -510,17 +539,67 @@ bool cmComputeLinkInformation::Compute() } // Compute the ordered link line items. - cmComputeLinkDepends cld(this->Target, this->Config); + cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage); cld.SetOldLinkDirMode(this->OldLinkDirMode); cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute(); + FeatureDescriptor const* currentFeature = nullptr; // Add the link line items. for (cmComputeLinkDepends::LinkEntry const& linkEntry : linkEntries) { - if (linkEntry.IsSharedDep) { - this->AddSharedDepItem(linkEntry.Item, linkEntry.Target); + if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::Group) { + const auto& groupFeature = this->GetGroupFeature(linkEntry.Feature); + if (groupFeature.Supported) { + this->Items.emplace_back( + BT<std::string>{ linkEntry.Item.Value == "<LINK_GROUP>" + ? groupFeature.Prefix + : groupFeature.Suffix, + linkEntry.Item.Backtrace }, + ItemIsPath::No); + } + continue; + } + + if (currentFeature != nullptr && + linkEntry.Feature != currentFeature->Name) { + // emit feature suffix, if any + if (!currentFeature->Suffix.empty()) { + this->Items.emplace_back( + BT<std::string>{ currentFeature->Suffix, + this->Items.back().Value.Backtrace }, + ItemIsPath::No); + } + currentFeature = nullptr; + } + + if (linkEntry.Feature != DEFAULT && + (currentFeature == nullptr || + linkEntry.Feature != currentFeature->Name)) { + if (!this->AddLibraryFeature(linkEntry.Feature)) { + continue; + } + currentFeature = this->FindLibraryFeature(linkEntry.Feature); + // emit feature prefix, if any + if (!currentFeature->Prefix.empty()) { + this->Items.emplace_back( + BT<std::string>{ currentFeature->Prefix, linkEntry.Item.Backtrace }, + ItemIsPath::No); + } + } + + if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::SharedDep) { + this->AddSharedDepItem(linkEntry); } else { - this->AddItem(linkEntry.Item, linkEntry.Target, - linkEntry.IsObject ? ItemIsObject::Yes : ItemIsObject::No); + this->AddItem(linkEntry); + } + } + + if (currentFeature != nullptr) { + // emit feature suffix, if any + if (!currentFeature->Suffix.empty()) { + this->Items.emplace_back( + BT<std::string>{ currentFeature->Suffix, + this->Items.back().Value.Backtrace }, + ItemIsPath::No); } } @@ -576,6 +655,373 @@ bool cmComputeLinkInformation::Compute() return true; } +namespace { +void FinalizeFeatureFormat(std::string& format, const std::string& activeTag, + const std::string& otherTag) +{ + auto pos = format.find(otherTag); + if (pos != std::string::npos) { + format.erase(pos, format.find('}', pos) - pos + 1); + } + pos = format.find(activeTag); + if (pos != std::string::npos) { + format.erase(pos, activeTag.length()); + pos = format.find('}', pos); + if (pos != std::string::npos) { + format.erase(pos, 1); + } + } +} + +bool IsValidFeatureFormat(const std::string& format) +{ + return format.find("<LIBRARY>") != std::string::npos || + format.find("<LIB_ITEM>") != std::string::npos || + format.find("<LINK_ITEM>") != std::string::npos; +} + +class FeaturePlaceHolderExpander : public cmPlaceholderExpander +{ +public: + FeaturePlaceHolderExpander(const std::string* library, + const std::string* libItem = nullptr, + const std::string* linkItem = nullptr) + : Library(library) + , LibItem(libItem) + , LinkItem(linkItem) + { + } + +private: + std::string ExpandVariable(std::string const& variable) override + { + if (this->Library != nullptr && variable == "LIBRARY") { + return *this->Library; + } + if (this->LibItem != nullptr && variable == "LIB_ITEM") { + return *this->LibItem; + } + if (this->LinkItem != nullptr && variable == "LINK_ITEM") { + return *this->LinkItem; + } + + return variable; + } + + const std::string* Library = nullptr; + const std::string* LibItem = nullptr; + const std::string* LinkItem = nullptr; +}; +} + +cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( + std::string name, std::string itemFormat) + : Name(std::move(name)) + , Supported(true) + , ItemPathFormat(std::move(itemFormat)) + , ItemNameFormat(this->ItemPathFormat) +{ +} +cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( + std::string name, std::string itemPathFormat, std::string itemNameFormat) + : Name(std::move(name)) + , Supported(true) + , ItemPathFormat(std::move(itemPathFormat)) + , ItemNameFormat(std::move(itemNameFormat)) +{ +} +cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( + std::string name, std::string prefix, std::string itemPathFormat, + std::string itemNameFormat, std::string suffix) + : Name(std::move(name)) + , Supported(true) + , Prefix(std::move(prefix)) + , Suffix(std::move(suffix)) + , ItemPathFormat(std::move(itemPathFormat)) + , ItemNameFormat(std::move(itemNameFormat)) +{ +} +cmComputeLinkInformation::FeatureDescriptor::FeatureDescriptor( + std::string name, std::string prefix, std::string suffix, bool) + : Name(std::move(name)) + , Supported(true) + , Prefix(std::move(prefix)) + , Suffix(std::move(suffix)) +{ +} + +std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem( + std::string const& library, ItemIsPath isPath) const +{ + auto format = + isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat; + + // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns with library path + FeaturePlaceHolderExpander expander(&library, &library, &library); + return expander.ExpandVariables(format); +} +std::string cmComputeLinkInformation::FeatureDescriptor::GetDecoratedItem( + std::string const& library, std::string const& libItem, + std::string const& linkItem, ItemIsPath isPath) const +{ + auto format = + isPath == ItemIsPath::Yes ? this->ItemPathFormat : this->ItemNameFormat; + + // replace <LIBRARY>, <LIB_ITEM> and <LINK_ITEM> patterns + FeaturePlaceHolderExpander expander(&library, &libItem, &linkItem); + return expander.ExpandVariables(format); +} + +cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor( + std::string name, std::string itemFormat) + : FeatureDescriptor(std::move(name), std::move(itemFormat)) +{ +} +cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor( + std::string name, std::string itemPathFormat, std::string itemNameFormat) + : FeatureDescriptor(std::move(name), std::move(itemPathFormat), + std::move(itemNameFormat)) +{ +} +cmComputeLinkInformation::LibraryFeatureDescriptor::LibraryFeatureDescriptor( + std::string name, std::string prefix, std::string itemPathFormat, + std::string itemNameFormat, std::string suffix) + : FeatureDescriptor(std::move(name), std::move(prefix), + std::move(itemPathFormat), std::move(itemNameFormat), + std::move(suffix)) +{ +} + +bool cmComputeLinkInformation::AddLibraryFeature(std::string const& feature) +{ + auto it = this->LibraryFeatureDescriptors.find(feature); + if (it != this->LibraryFeatureDescriptors.end()) { + return it->second.Supported; + } + + auto featureName = + cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_USING_", feature); + cmValue featureSupported = + this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); + if (!featureSupported.IsOn()) { + featureName = cmStrCat("CMAKE_LINK_LIBRARY_USING_", feature); + featureSupported = + this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); + } + if (!featureSupported.IsOn()) { + this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); + + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Feature '", feature, + "', specified through generator-expression '$<LINK_LIBRARY>' to " + "link target '", + this->Target->GetName(), "', is not supported for the '", + this->LinkLanguage, "' link language."), + this->Target->GetBacktrace()); + + return false; + } + + cmValue langFeature = this->Makefile->GetDefinition(featureName); + if (!langFeature) { + this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); + + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Feature '", feature, + "', specified through generator-expression '$<LINK_LIBRARY>' to " + "link target '", + this->Target->GetName(), "', is not defined for the '", + this->LinkLanguage, "' link language."), + this->Target->GetBacktrace()); + + return false; + } + + auto items = + cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true); + + if ((items.size() == 1 && !IsValidFeatureFormat(items.front().Value)) || + (items.size() == 3 && !IsValidFeatureFormat(items[1].Value))) { + this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, "', specified by variable '", featureName, + "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or " + "\"<LINK_ITEM>\" patterns " + "are missing) and cannot be used to link target '", + this->Target->GetName(), "'."), + this->Target->GetBacktrace()); + + return false; + } + + // now, handle possible "PATH{}" and "NAME{}" patterns + if (items.size() == 1) { + items.push_back(items.front()); + FinalizeFeatureFormat(items[0].Value, "PATH{", "NAME{"); + FinalizeFeatureFormat(items[1].Value, "NAME{", "PATH{"); + } else if (items.size() == 3) { + items.insert(items.begin() + 1, items[1]); + FinalizeFeatureFormat(items[1].Value, "PATH{", "NAME{"); + FinalizeFeatureFormat(items[2].Value, "NAME{", "PATH{"); + } else { + this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, "', specified by variable '", featureName, + "', is malformed (wrong number of elements) and cannot be used " + "to link target '", + this->Target->GetName(), "'."), + this->Target->GetBacktrace()); + + return false; + } + if ((items.size() == 2 && !IsValidFeatureFormat(items[0].Value)) || + (items.size() == 4 && !IsValidFeatureFormat(items[1].Value))) { + // PATH{} has wrong format + this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, "', specified by variable '", featureName, + "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or " + "\"<LINK_ITEM>\" patterns " + "are missing for \"PATH{}\" alternative) and cannot be used to " + "link target '", + this->Target->GetName(), "'."), + this->Target->GetBacktrace()); + + return false; + } + if ((items.size() == 2 && !IsValidFeatureFormat(items[1].Value)) || + (items.size() == 4 && !IsValidFeatureFormat(items[2].Value))) { + // NAME{} has wrong format + this->LibraryFeatureDescriptors.emplace(feature, FeatureDescriptor{}); + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, "', specified by variable '", featureName, + "', is malformed (\"<LIBRARY>\", \"<LIB_ITEM>\", or " + "\"<LINK_ITEM>\" patterns " + "are missing for \"NAME{}\" alternative) and cannot be used to " + "link target '", + this->Target->GetName(), "'."), + this->Target->GetBacktrace()); + + return false; + } + + // replace LINKER: pattern + this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true); + + if (items.size() == 2) { + this->LibraryFeatureDescriptors.emplace( + feature, + LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value }); + } else { + this->LibraryFeatureDescriptors.emplace( + feature, + LibraryFeatureDescriptor{ feature, items[0].Value, items[1].Value, + items[2].Value, items[3].Value }); + } + + return true; +} + +cmComputeLinkInformation::FeatureDescriptor const& +cmComputeLinkInformation::GetLibraryFeature(std::string const& feature) const +{ + return this->LibraryFeatureDescriptors.find(feature)->second; +} +cmComputeLinkInformation::FeatureDescriptor const* +cmComputeLinkInformation::FindLibraryFeature(std::string const& feature) const +{ + auto it = this->LibraryFeatureDescriptors.find(feature); + if (it == this->LibraryFeatureDescriptors.end()) { + return nullptr; + } + + return &it->second; +} + +cmComputeLinkInformation::GroupFeatureDescriptor::GroupFeatureDescriptor( + std::string name, std::string prefix, std::string suffix) + : FeatureDescriptor(std::move(name), std::move(prefix), std::move(suffix), + true) +{ +} + +cmComputeLinkInformation::FeatureDescriptor const& +cmComputeLinkInformation::GetGroupFeature(std::string const& feature) +{ + auto it = this->GroupFeatureDescriptors.find(feature); + if (it != this->GroupFeatureDescriptors.end()) { + return it->second; + } + + auto featureName = + cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_GROUP_USING_", feature); + cmValue featureSupported = + this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); + if (!featureSupported.IsOn()) { + featureName = cmStrCat("CMAKE_LINK_GROUP_USING_", feature); + featureSupported = + this->Makefile->GetDefinition(cmStrCat(featureName, "_SUPPORTED")); + } + if (!featureSupported.IsOn()) { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, + "', specified through generator-expression '$<LINK_GROUP>' to " + "link target '", + this->Target->GetName(), "', is not supported for the '", + this->LinkLanguage, "' link language."), + this->Target->GetBacktrace()); + return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{}) + .first->second; + } + + cmValue langFeature = this->Makefile->GetDefinition(featureName); + if (!langFeature) { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, + "', specified through generator-expression '$<LINK_GROUP>' to " + "link target '", + this->Target->GetName(), "', is not defined for the '", + this->LinkLanguage, "' link language."), + this->Target->GetBacktrace()); + return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{}) + .first->second; + } + + auto items = + cmExpandListWithBacktrace(langFeature, this->Target->GetBacktrace(), true); + + // replace LINKER: pattern + this->Target->ResolveLinkerWrapper(items, this->LinkLanguage, true); + + if (items.size() == 2) { + return this->GroupFeatureDescriptors + .emplace( + feature, + GroupFeatureDescriptor{ feature, items[0].Value, items[1].Value }) + .first->second; + } + + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Feature '", feature, "', specified by variable '", featureName, + "', is malformed (wrong number of elements) and cannot be used " + "to link target '", + this->Target->GetName(), "'."), + this->Target->GetBacktrace()); + return this->GroupFeatureDescriptors.emplace(feature, FeatureDescriptor{}) + .first->second; +} + void cmComputeLinkInformation::AddImplicitLinkInfo() { // The link closure lists all languages whose implicit info is needed. @@ -610,7 +1056,7 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang) std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions); for (std::string const& i : libsVec) { if (!cm::contains(this->ImplicitLinkLibs, i)) { - this->AddItem(i, nullptr); + this->AddItem({ i }); } } } @@ -625,7 +1071,7 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang) std::vector<std::string> libsVec = cmExpandedList(*libs); for (std::string const& i : libsVec) { if (!cm::contains(this->ImplicitLinkLibs, i)) { - this->AddItem(i, nullptr); + this->AddItem({ i }); } } } @@ -639,10 +1085,11 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang) } } -void cmComputeLinkInformation::AddItem(BT<std::string> const& item, - cmGeneratorTarget const* tgt, - ItemIsObject isObject) +void cmComputeLinkInformation::AddItem(LinkEntry const& entry) { + cmGeneratorTarget const* tgt = entry.Target; + BT<std::string> const& item = entry.Item; + // Compute the proper name to use to link this library. const std::string& config = this->Config; bool impexe = (tgt && tgt->IsExecutableWithExports()); @@ -657,28 +1104,27 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item, if (impexe && this->LoaderFlag) { // This link item is an executable that may provide symbols // used by this target. A special flag is needed on this - // platform. Add it now. - std::string linkItem = this->LoaderFlag; + // platform. Add it now using a special feature. cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config) ? cmStateEnums::ImportLibraryArtifact : cmStateEnums::RuntimeBinaryArtifact; - std::string exe = tgt->GetFullPath(config, artifact, true); - linkItem += exe; - this->Items.emplace_back(BT<std::string>(linkItem, item.Backtrace), - ItemIsPath::Yes, ItemIsObject::No, tgt); + this->Items.emplace_back( + BT<std::string>(exe, item.Backtrace), ItemIsPath::Yes, tgt, + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "__CMAKE_LINK_EXECUTABLE" + : entry.Feature)); this->Depends.push_back(std::move(exe)); } else if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { // Add the interface library as an item so it can be considered as part // of COMPATIBLE_INTERFACE_ enforcement. The generators will ignore // this for the actual link line. - this->Items.emplace_back(std::string(), ItemIsPath::No, ItemIsObject::No, - tgt); + this->Items.emplace_back(std::string(), ItemIsPath::No, tgt); // Also add the item the interface specifies to be used in its place. std::string const& libName = tgt->GetImportedLibName(config); if (!libName.empty()) { - this->AddItem(BT<std::string>(libName, item.Backtrace), nullptr); + this->AddItem(BT<std::string>(libName, item.Backtrace)); } } else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) { // Ignore object library! @@ -706,7 +1152,9 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item, this->Depends.push_back(lib.Value); } - this->AddTargetItem(lib, tgt); + LinkEntry libEntry{ entry }; + libEntry.Item = lib; + this->AddTargetItem(libEntry); this->AddLibraryRuntimeInfo(lib.Value, tgt); if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY && this->Target->IsDLLPlatform()) { @@ -715,30 +1163,34 @@ void cmComputeLinkInformation::AddItem(BT<std::string> const& item, } } else { // This is not a CMake target. Use the name given. - if (cmSystemTools::FileIsFullPath(item.Value)) { - if (cmSystemTools::IsPathToFramework(item.Value) && - this->Makefile->IsOn("APPLE")) { - // This is a framework. - this->AddFrameworkItem(item.Value); - } else if (cmSystemTools::FileIsDirectory(item.Value)) { + if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) || + (entry.Feature == DEFAULT && + cmSystemTools::IsPathToFramework(item.Value) && + this->Makefile->IsOn("APPLE"))) { + // This is a framework. + this->AddFrameworkItem(entry); + } else if (cmSystemTools::FileIsFullPath(item.Value)) { + if (cmSystemTools::FileIsDirectory(item.Value)) { // This is a directory. this->DropDirectoryItem(item); } else { // Use the full path given to the library file. this->Depends.push_back(item.Value); - this->AddFullItem(item, isObject); + this->AddFullItem(entry); this->AddLibraryRuntimeInfo(item.Value); } } else { // This is a library or option specified by the user. - this->AddUserItem(item, true); + this->AddUserItem(entry, true); } } } -void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item, - const cmGeneratorTarget* tgt) +void cmComputeLinkInformation::AddSharedDepItem(LinkEntry const& entry) { + BT<std::string> const& item = entry.Item; + const cmGeneratorTarget* tgt = entry.Target; + // Record dependencies on DLLs. if (tgt && tgt->GetType() == cmStateEnums::SHARED_LIBRARY && this->Target->IsDLLPlatform() && @@ -776,7 +1228,7 @@ void cmComputeLinkInformation::AddSharedDepItem(BT<std::string> const& item, // If in linking mode, just link to the shared library. if (this->SharedDependencyMode == SharedDepModeLink) { - this->AddItem(item, tgt); + this->AddItem(entry); return; } @@ -1058,8 +1510,7 @@ void cmComputeLinkInformation::SetCurrentLinkType(LinkType lt) } } -void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item, - cmGeneratorTarget const* target) +void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) { // This is called to handle a link item that is a full path to a target. // If the target is not a static library make sure the link type is @@ -1067,6 +1518,9 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item, // shared and static libraries but static-mode can handle only // static libraries. If a previous user item changed the link type // to static we need to make sure it is back to shared. + BT<std::string> const& item = entry.Item; + cmGeneratorTarget const* target = entry.Target; + if (target->GetType() != cmStateEnums::STATIC_LIBRARY) { this->SetCurrentLinkType(LinkShared); } @@ -1079,7 +1533,7 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item, // Handle case of an imported shared library with no soname. if (this->NoSONameUsesPath && target->IsImportedSharedLibWithoutSOName(this->Config)) { - this->AddSharedLibNoSOName(item.Value); + this->AddSharedLibNoSOName(entry); return; } @@ -1091,20 +1545,54 @@ void cmComputeLinkInformation::AddTargetItem(BT<std::string> const& item, this->OldLinkDirItems.push_back(item.Value); } - // Now add the full path to the library. - this->Items.emplace_back(item, ItemIsPath::Yes, ItemIsObject::No, target); + if (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode() && + entry.Feature == DEFAULT) { + // ensure FRAMEWORK feature is loaded + this->AddLibraryFeature("FRAMEWORK"); + } + + if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) && + target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) { + // Add the framework directory and the framework item itself + auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item.Value, true); + if (!fwItems) { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Could not parse framework path \"", item.Value, + "\" linked by target ", this->Target->GetName(), '.'), + item.Backtrace); + return; + } + if (!fwItems->first.empty()) { + // Add the directory portion to the framework search path. + this->AddFrameworkPath(fwItems->first); + } + this->Items.emplace_back(fwItems->second, ItemIsPath::Yes, target, + this->FindLibraryFeature(entry.Feature)); + } else { + // Now add the full path to the library. + this->Items.emplace_back( + item, ItemIsPath::Yes, target, + this->FindLibraryFeature( + entry.Feature == DEFAULT + ? (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode() + ? "FRAMEWORK" + : "__CMAKE_LINK_LIBRARY") + : entry.Feature)); + } } -void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item, - ItemIsObject isObject) +void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry) { + BT<std::string> const& item = entry.Item; + // Check for the implicit link directory special case. - if (this->CheckImplicitDirItem(item.Value)) { + if (this->CheckImplicitDirItem(entry)) { return; } // Check for case of shared library with no builtin soname. - if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(item.Value)) { + if (this->NoSONameUsesPath && this->CheckSharedLibNoSOName(entry)) { return; } @@ -1116,7 +1604,7 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item, generator.find("Xcode") != std::string::npos)) { std::string file = cmSystemTools::GetFilenameName(item.Value); if (!this->ExtractAnyLibraryName.find(file)) { - this->HandleBadFullItem(item.Value, file); + this->HandleBadFullItem(entry, file); return; } } @@ -1147,11 +1635,20 @@ void cmComputeLinkInformation::AddFullItem(BT<std::string> const& item, } // Now add the full path to the library. - this->Items.emplace_back(item, ItemIsPath::Yes, isObject); + this->Items.emplace_back( + item, ItemIsPath::Yes, nullptr, + this->FindLibraryFeature( + entry.Feature == DEFAULT + ? (entry.Kind == cmComputeLinkDepends::LinkEntry::Object + ? "__CMAKE_LINK_OBJECT" + : "__CMAKE_LINK_LIBRARY") + : entry.Feature)); } -bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item) +bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry) { + BT<std::string> const& item = entry.Item; + // We only switch to a pathless item if the link type may be // enforced. Fortunately only platforms that support link types // seem to have magic per-architecture implicit link directories. @@ -1160,7 +1657,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item) } // Check if this item is in an implicit link directory. - std::string dir = cmSystemTools::GetFilenamePath(item); + std::string dir = cmSystemTools::GetFilenamePath(item.Value); if (!cm::contains(this->ImplicitLinkDirs, dir)) { // Only libraries in implicit link directories are converted to // pathless items. @@ -1169,7 +1666,7 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item) // Only apply the policy below if the library file is one that can // be found by the linker. - std::string file = cmSystemTools::GetFilenameName(item); + std::string file = cmSystemTools::GetFilenameName(item.Value); if (!this->ExtractAnyLibraryName.find(file)) { return false; } @@ -1179,10 +1676,10 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item) case cmPolicies::WARN: if (this->CMP0060Warn) { // Print the warning at most once for this item. - std::string const& wid = "CMP0060-WARNING-GIVEN-" + item; + std::string const& wid = "CMP0060-WARNING-GIVEN-" + item.Value; if (!this->CMakeInstance->GetPropertyAsBool(wid)) { this->CMakeInstance->SetProperty(wid, "1"); - this->CMP0060WarnItems.insert(item); + this->CMP0060WarnItems.insert(item.Value); } } CM_FALLTHROUGH; @@ -1200,15 +1697,17 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(std::string const& item) // directory then just report the file name without the directory // portion. This will allow the system linker to locate the proper // library for the architecture at link time. - this->AddUserItem(file, false); + LinkEntry fileEntry{ entry }; + fileEntry.Item = file; + this->AddUserItem(fileEntry, false); // Make sure the link directory ordering will find the library. - this->OrderLinkerSearchPath->AddLinkLibrary(item); + this->OrderLinkerSearchPath->AddLinkLibrary(item.Value); return true; } -void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item, +void cmComputeLinkInformation::AddUserItem(LinkEntry const& entry, bool pathNotKnown) { // This is called to handle a link item that does not match a CMake @@ -1219,8 +1718,10 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item, // foo ==> -lfoo // libfoo.a ==> -Wl,-Bstatic -lfoo - // Pass flags through untouched. + BT<std::string> const& item = entry.Item; + if (item.Value[0] == '-' || item.Value[0] == '$' || item.Value[0] == '`') { + // Pass flags through untouched. // if this is a -l option then we might need to warn about // CMP0003 so put it in OldUserFlagItems, if it is not a -l // or -Wl,-l (-framework -pthread), then allow it without a @@ -1305,9 +1806,20 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item, } // Create an option to ask the linker to search for the library. - std::string out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix); - this->Items.emplace_back(BT<std::string>(out, item.Backtrace), - ItemIsPath::No); + auto out = cmStrCat(this->LibLinkFlag, lib, this->LibLinkSuffix); + + if (entry.Feature != DEFAULT) { + auto const& feature = this->GetLibraryFeature(entry.Feature); + this->Items.emplace_back( + BT<std::string>( + feature.GetDecoratedItem(cmStrCat(lib, this->LibLinkSuffix), + item.Value, out, ItemIsPath::No), + item.Backtrace), + ItemIsPath::No); + } else { + this->Items.emplace_back(BT<std::string>(out, item.Backtrace), + ItemIsPath::No); + } // Here we could try to find the library the linker will find and // add a runtime information entry for it. It would probably not be @@ -1315,10 +1827,14 @@ void cmComputeLinkInformation::AddUserItem(BT<std::string> const& item, // specification. } -void cmComputeLinkInformation::AddFrameworkItem(std::string const& item) +void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) { + std::string const& item = entry.Item.Value; + // Try to separate the framework name and path. - if (!this->SplitFramework.find(item)) { + auto fwItems = + this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT); + if (!fwItems) { std::ostringstream e; e << "Could not parse framework path \"" << item << "\" " << "linked by target " << this->Target->GetName() << "."; @@ -1326,26 +1842,36 @@ void cmComputeLinkInformation::AddFrameworkItem(std::string const& item) return; } - std::string fw_path = this->SplitFramework.match(1); - std::string fw = this->SplitFramework.match(2); - std::string full_fw = cmStrCat(fw_path, '/', fw, ".framework/", fw); + std::string fw_path = std::move(fwItems->first); + std::string fw = std::move(fwItems->second); + std::string full_fw = cmStrCat(fw, ".framework/", fw); - // Add the directory portion to the framework search path. - this->AddFrameworkPath(fw_path); + if (!fw_path.empty()) { + full_fw = cmStrCat(fw_path, '/', full_fw); + // Add the directory portion to the framework search path. + this->AddFrameworkPath(fw_path); + } // add runtime information this->AddLibraryRuntimeInfo(full_fw); + if (entry.Feature == DEFAULT) { + // ensure FRAMEWORK feature is loaded + this->AddLibraryFeature("FRAMEWORK"); + } + if (this->GlobalGenerator->IsXcode()) { // Add framework path - it will be handled by Xcode after it's added to // "Link Binary With Libraries" build phase - this->Items.emplace_back(item, ItemIsPath::Yes); + this->Items.emplace_back(item, ItemIsPath::Yes, nullptr, + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "FRAMEWORK" + : entry.Feature)); } else { - // Add the item using the -framework option. - this->Items.emplace_back(std::string("-framework"), ItemIsPath::No); - cmOutputConverter converter(this->Makefile->GetStateSnapshot()); - fw = converter.EscapeForShell(fw); - this->Items.emplace_back(fw, ItemIsPath::No); + this->Items.emplace_back(fw, ItemIsPath::Yes, nullptr, + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "FRAMEWORK" + : entry.Feature)); } } @@ -1355,10 +1881,10 @@ void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item) // user. this->CMakeInstance->IssueMessage( MessageType::WARNING, - cmStrCat( - "Target \"", this->Target->GetName(), - "\" requests linking to directory \"", item.Value, - "\". Targets may link only to libraries. CMake is dropping the item."), + cmStrCat("Target \"", this->Target->GetName(), + "\" requests linking to directory \"", item.Value, + "\". Targets may link only to libraries. CMake is dropping " + "the item."), item.Backtrace); } @@ -1378,9 +1904,6 @@ void cmComputeLinkInformation::ComputeFrameworkInfo() this->FrameworkPathsEmitted.insert(implicitDirVec.begin(), implicitDirVec.end()); - - // Regular expression to extract a framework path and name. - this->SplitFramework.compile("(.*)/(.*)\\.framework$"); } void cmComputeLinkInformation::AddFrameworkPath(std::string const& p) @@ -1390,42 +1913,44 @@ void cmComputeLinkInformation::AddFrameworkPath(std::string const& p) } } -bool cmComputeLinkInformation::CheckSharedLibNoSOName(std::string const& item) +bool cmComputeLinkInformation::CheckSharedLibNoSOName(LinkEntry const& entry) { // This platform will use the path to a library as its soname if the // library is given via path and was not built with an soname. If // this is a shared library that might be the case. - std::string file = cmSystemTools::GetFilenameName(item); + std::string file = cmSystemTools::GetFilenameName(entry.Item.Value); if (this->ExtractSharedLibraryName.find(file)) { // If we can guess the soname fairly reliably then assume the // library has one. Otherwise assume the library has no builtin // soname. std::string soname; - if (!cmSystemTools::GuessLibrarySOName(item, soname)) { - this->AddSharedLibNoSOName(item); + if (!cmSystemTools::GuessLibrarySOName(entry.Item.Value, soname)) { + this->AddSharedLibNoSOName(entry); return true; } } return false; } -void cmComputeLinkInformation::AddSharedLibNoSOName(std::string const& item) +void cmComputeLinkInformation::AddSharedLibNoSOName(LinkEntry const& entry) { // We have a full path to a shared library with no soname. We need // to ask the linker to locate the item because otherwise the path // we give to it will be embedded in the target linked. Then at // runtime the dynamic linker will search for the library using the // path instead of just the name. - std::string file = cmSystemTools::GetFilenameName(item); - this->AddUserItem(file, false); + LinkEntry fileEntry{ entry }; + fileEntry.Item = cmSystemTools::GetFilenameName(entry.Item.Value); + this->AddUserItem(fileEntry, false); // Make sure the link directory ordering will find the library. - this->OrderLinkerSearchPath->AddLinkLibrary(item); + this->OrderLinkerSearchPath->AddLinkLibrary(entry.Item.Value); } -void cmComputeLinkInformation::HandleBadFullItem(std::string const& item, +void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry, std::string const& file) { + std::string const& item = entry.Item.Value; // Do not depend on things that do not exist. auto i = std::find(this->Depends.begin(), this->Depends.end(), item); if (i != this->Depends.end()) { @@ -1435,7 +1960,9 @@ void cmComputeLinkInformation::HandleBadFullItem(std::string const& item, // Tell the linker to search for the item and provide the proper // path for it. Do not contribute to any CMP0003 warning (do not // put in OldLinkDirItems or OldUserFlagItems). - this->AddUserItem(file, false); + LinkEntry fileEntry{ entry }; + fileEntry.Item = file; + this->AddUserItem(fileEntry, false); this->OrderLinkerSearchPath->AddLinkLibrary(item); // Produce any needed message. @@ -1781,8 +2308,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, // Add directories explicitly specified by user std::string build_rpath; if (this->Target->GetBuildRPATH(this->Config, build_rpath)) { - // This will not resolve entries to use $ORIGIN, the user is expected to - // do that if necessary. + // This will not resolve entries to use $ORIGIN, the user is expected + // to do that if necessary. cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted); } } diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h index 0315540..a4ada1f 100644 --- a/Source/cmComputeLinkInformation.h +++ b/Source/cmComputeLinkInformation.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> +#include <map> #include <memory> #include <set> #include <string> @@ -13,6 +14,7 @@ #include "cmsys/RegularExpression.hxx" +#include "cmComputeLinkDepends.h" #include "cmListFileCache.h" #include "cmValue.h" @@ -27,6 +29,9 @@ class cmake; */ class cmComputeLinkInformation { +private: + class FeatureDescriptor; + public: cmComputeLinkInformation(cmGeneratorTarget const* target, const std::string& config); @@ -42,28 +47,38 @@ public: Yes, }; - enum class ItemIsObject - { - No, - Yes, - }; - struct Item { - Item() = default; Item(BT<std::string> v, ItemIsPath isPath, - ItemIsObject isObject = ItemIsObject::No, - cmGeneratorTarget const* target = nullptr) + cmGeneratorTarget const* target = nullptr, + FeatureDescriptor const* feature = nullptr) : Value(std::move(v)) , IsPath(isPath) - , IsObject(isObject) , Target(target) + , Feature(feature) { } BT<std::string> Value; - ItemIsPath IsPath = ItemIsPath::Yes; - ItemIsObject IsObject = ItemIsObject::No; + ItemIsPath IsPath = ItemIsPath::No; cmGeneratorTarget const* Target = nullptr; + + bool HasFeature() const { return this->Feature != nullptr; } + const std::string& GetFeatureName() const + { + return HasFeature() ? this->Feature->Name + : cmComputeLinkDepends::LinkEntry::DEFAULT; + } + + BT<std::string> GetFormattedItem(std::string const& path) const + { + return { (this->Feature != nullptr) + ? this->Feature->GetDecoratedItem(path, this->IsPath) + : path, + Value.Backtrace }; + } + + private: + FeatureDescriptor const* Feature = nullptr; }; using ItemVector = std::vector<Item>; void AppendValues(std::string& result, std::vector<BT<std::string>>& values); @@ -104,10 +119,10 @@ public: const cmGeneratorTarget* GetTarget() { return this->Target; } private: - void AddItem(BT<std::string> const& item, const cmGeneratorTarget* tgt, - ItemIsObject isObject = ItemIsObject::No); - void AddSharedDepItem(BT<std::string> const& item, - cmGeneratorTarget const* tgt); + using LinkEntry = cmComputeLinkDepends::LinkEntry; + + void AddItem(LinkEntry const& entry); + void AddSharedDepItem(LinkEntry const& entry); void AddRuntimeDLL(cmGeneratorTarget const* tgt); // Output information. @@ -181,22 +196,20 @@ private: std::string NoCaseExpression(std::string const& str); // Handling of link items. - void AddTargetItem(BT<std::string> const& item, - const cmGeneratorTarget* target); - void AddFullItem(BT<std::string> const& item, ItemIsObject isObject); - bool CheckImplicitDirItem(std::string const& item); - void AddUserItem(BT<std::string> const& item, bool pathNotKnown); - void AddFrameworkItem(std::string const& item); + void AddTargetItem(LinkEntry const& entry); + void AddFullItem(LinkEntry const& entry); + bool CheckImplicitDirItem(LinkEntry const& entry); + void AddUserItem(LinkEntry const& entry, bool pathNotKnown); + void AddFrameworkItem(LinkEntry const& entry); void DropDirectoryItem(BT<std::string> const& item); - bool CheckSharedLibNoSOName(std::string const& item); - void AddSharedLibNoSOName(std::string const& item); - void HandleBadFullItem(std::string const& item, std::string const& file); + bool CheckSharedLibNoSOName(LinkEntry const& entry); + void AddSharedLibNoSOName(LinkEntry const& entry); + void HandleBadFullItem(LinkEntry const& entry, std::string const& file); // Framework info. void ComputeFrameworkInfo(); void AddFrameworkPath(std::string const& p); std::set<std::string> FrameworkPathsEmitted; - cmsys::RegularExpression SplitFramework; // Linker search path computation. std::unique_ptr<cmOrderDirectories> OrderLinkerSearchPath; @@ -237,4 +250,61 @@ private: void AddLibraryRuntimeInfo(std::string const& fullPath, const cmGeneratorTarget* target); void AddLibraryRuntimeInfo(std::string const& fullPath); + + class FeatureDescriptor + { + public: + FeatureDescriptor() = default; + + const std::string Name; + const bool Supported = false; + const std::string Prefix; + const std::string Suffix; + std::string GetDecoratedItem(std::string const& library, + ItemIsPath isPath) const; + std::string GetDecoratedItem(std::string const& library, + std::string const& linkItem, + std::string const& defaultValue, + ItemIsPath isPath) const; + + protected: + FeatureDescriptor(std::string name, std::string itemFormat); + FeatureDescriptor(std::string name, std::string itemPathFormat, + std::string itemNameFormat); + FeatureDescriptor(std::string name, std::string prefix, + std::string itemPathFormat, std::string itemNameFormat, + std::string suffix); + + FeatureDescriptor(std::string name, std::string prefix, std::string suffix, + bool isGroup); + + private: + std::string ItemPathFormat; + std::string ItemNameFormat; + }; + + class LibraryFeatureDescriptor : public FeatureDescriptor + { + public: + LibraryFeatureDescriptor(std::string name, std::string itemFormat); + LibraryFeatureDescriptor(std::string name, std::string itemPathFormat, + std::string itemNameFormat); + LibraryFeatureDescriptor(std::string name, std::string prefix, + std::string itemPathFormat, + std::string itemNameFormat, std::string suffix); + }; + std::map<std::string, FeatureDescriptor> LibraryFeatureDescriptors; + bool AddLibraryFeature(std::string const& feature); + FeatureDescriptor const& GetLibraryFeature(std::string const& feature) const; + FeatureDescriptor const* FindLibraryFeature( + std::string const& feature) const; + + class GroupFeatureDescriptor : public FeatureDescriptor + { + public: + GroupFeatureDescriptor(std::string name, std::string prefix, + std::string suffix); + }; + std::map<std::string, FeatureDescriptor> GroupFeatureDescriptors; + FeatureDescriptor const& GetGroupFeature(std::string const& feature); }; diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index b46b933..a21e614 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -934,13 +934,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) // Isolate the file policy level. // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.21 (this upper limit may be reviewed + // policy settings for up to CMake 3.22 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. /* clang-format off */ os << "cmake_policy(PUSH)\n" - << "cmake_policy(VERSION 2.6...3.21)\n"; + << "cmake_policy(VERSION 2.6...3.22)\n"; /* clang-format on */ } diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index da2f15f..09e5015 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -15,6 +15,7 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -1778,6 +1779,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, std::string userpwd; std::vector<std::string> curl_headers; + std::vector<std::pair<std::string, cm::optional<std::string>>> curl_ranges; while (i != args.end()) { if (*i == "TIMEOUT") { @@ -1890,6 +1892,27 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, return false; } curl_headers.push_back(*i); + } else if (*i == "RANGE_START") { + ++i; + if (i == args.end()) { + status.SetError("DOWNLOAD missing value for RANGE_START."); + return false; + } + curl_ranges.emplace_back(*i, cm::nullopt); + } else if (*i == "RANGE_END") { + ++i; + if (curl_ranges.empty()) { + curl_ranges.emplace_back("0", *i); + } else { + auto& last_range = curl_ranges.back(); + if (!last_range.second.has_value()) { + last_range.second = *i; + } else { + status.SetError("Multiple RANGE_END values is provided without " + "the corresponding RANGE_START."); + return false; + } + } } else if (file.empty()) { file = *i; } else { @@ -1899,6 +1922,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, } ++i; } + // Can't calculate hash if we don't save the file. // TODO Incrementally calculate hash in the write callback as the file is // being downloaded so this check can be relaxed. @@ -1984,6 +2008,13 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify off: "); } + for (const auto& range : curl_ranges) { + std::string curl_range = range.first + '-' + + (range.second.has_value() ? range.second.value() : ""); + res = ::curl_easy_setopt(curl, CURLOPT_RANGE, curl_range.c_str()); + check_curl_result(res, "DOWNLOAD cannot set range: "); + } + // check to see if a CAINFO file has been specified // command arg comes first std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo); diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx index a0a8570..27074ff 100644 --- a/Source/cmFindPathCommand.cxx +++ b/Source/cmFindPathCommand.cxx @@ -65,7 +65,8 @@ std::string cmFindPathCommand::FindHeader() } std::string cmFindPathCommand::FindHeaderInFramework( - std::string const& file, std::string const& dir) const + std::string const& file, std::string const& dir, + cmFindBaseDebugState& debug) const { std::string fileName = file; std::string frameWorkName; @@ -88,11 +89,13 @@ std::string cmFindPathCommand::FindHeaderInFramework( std::string fpath = cmStrCat(dir, frameWorkName, ".framework"); std::string intPath = cmStrCat(fpath, "/Headers/", fileName); if (cmSystemTools::FileExists(intPath)) { + debug.FoundAt(intPath); if (this->IncludeFileInPath) { return intPath; } return fpath; } + debug.FailedAt(intPath); } } // if it is not found yet or not a framework header, then do a glob search @@ -103,12 +106,15 @@ std::string cmFindPathCommand::FindHeaderInFramework( std::vector<std::string> files = globIt.GetFiles(); if (!files.empty()) { std::string fheader = cmSystemTools::CollapseFullPath(files[0]); + debug.FoundAt(fheader); if (this->IncludeFileInPath) { return fheader; } fheader.resize(fheader.size() - file.size()); return fheader; } + + // No frameworks matched the glob, so nothing more to add to debug.FailedAt() return ""; } @@ -135,8 +141,7 @@ std::string cmFindPathCommand::FindFrameworkHeader(cmFindBaseDebugState& debug) { for (std::string const& n : this->Names) { for (std::string const& sp : this->SearchPaths) { - std::string fwPath = this->FindHeaderInFramework(n, sp); - fwPath.empty() ? debug.FailedAt(fwPath) : debug.FoundAt(fwPath); + std::string fwPath = this->FindHeaderInFramework(n, sp, debug); if (!fwPath.empty()) { return fwPath; } diff --git a/Source/cmFindPathCommand.h b/Source/cmFindPathCommand.h index c7281f1..a7746f6 100644 --- a/Source/cmFindPathCommand.h +++ b/Source/cmFindPathCommand.h @@ -30,7 +30,8 @@ public: private: std::string FindHeaderInFramework(std::string const& file, - std::string const& dir) const; + std::string const& dir, + cmFindBaseDebugState& debug) const; std::string FindHeader(); std::string FindNormalHeader(cmFindBaseDebugState& debug); std::string FindFrameworkHeader(cmFindBaseDebugState& debug); diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index d4b02a5..d35d428 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -167,7 +167,7 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const cm::string_view property(this->Top()->Property); return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s || - property == "LINK_DEPENDS"_s; + property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s; } bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 396e9c9..db043ec 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -1198,6 +1198,152 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode } } linkLanguageAndIdNode; +static const struct LinkLibraryNode : public cmGeneratorExpressionNode +{ + LinkLibraryNode() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return OneOrMoreParameters; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + if (!context->HeadTarget || !dagChecker || + !dagChecker->EvaluatingLinkLibraries()) { + reportError(context, content->GetOriginalExpression(), + "$<LINK_LIBRARY:...> may only be used with binary targets " + "to specify link libraries."); + return std::string(); + } + + std::vector<std::string> list; + cmExpandLists(parameters.begin(), parameters.end(), list); + if (list.empty()) { + reportError( + context, content->GetOriginalExpression(), + "$<LINK_LIBRARY:...> expects a feature name as first argument."); + return std::string(); + } + if (list.size() == 1) { + // no libraries specified, ignore this genex + return std::string(); + } + + static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$"); + auto const& feature = list.front(); + if (!featureNameValidator.find(feature)) { + reportError(context, content->GetOriginalExpression(), + cmStrCat("The feature name '", feature, + "' contains invalid characters.")); + return std::string(); + } + + const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>'); + const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>'); + + // filter out $<LINK_LIBRARY:..> tags with same feature + // and raise an error for any different feature + cm::erase_if(list, [&](const std::string& item) -> bool { + return item == LL_BEGIN || item == LL_END; + }); + auto it = + std::find_if(list.cbegin() + 1, list.cend(), + [&feature](const std::string& item) -> bool { + return cmHasPrefix(item, "<LINK_LIBRARY:"_s) && + item.substr(14, item.find('>', 14) - 14) != feature; + }); + if (it != list.cend()) { + reportError( + context, content->GetOriginalExpression(), + "$<LINK_LIBRARY:...> with different features cannot be nested."); + return std::string(); + } + // $<LINK_GROUP:...> must not appear as part of $<LINK_LIBRARY:...> + it = std::find_if(list.cbegin() + 1, list.cend(), + [](const std::string& item) -> bool { + return cmHasPrefix(item, "<LINK_GROUP:"_s); + }); + if (it != list.cend()) { + reportError(context, content->GetOriginalExpression(), + "$<LINK_GROUP:...> cannot be nested inside a " + "$<LINK_LIBRARY:...> expression."); + return std::string(); + } + + list.front() = LL_BEGIN; + list.push_back(LL_END); + + return cmJoin(list, ";"_s); + } +} linkLibraryNode; + +static const struct LinkGroupNode : public cmGeneratorExpressionNode +{ + LinkGroupNode() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return OneOrMoreParameters; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + if (!context->HeadTarget || !dagChecker || + !dagChecker->EvaluatingLinkLibraries()) { + reportError(context, content->GetOriginalExpression(), + "$<LINK_GROUP:...> may only be used with binary targets " + "to specify group of link libraries."); + return std::string(); + } + + std::vector<std::string> list; + cmExpandLists(parameters.begin(), parameters.end(), list); + if (list.empty()) { + reportError( + context, content->GetOriginalExpression(), + "$<LINK_GROUP:...> expects a feature name as first argument."); + return std::string(); + } + // $<LINK_GROUP:..> cannot be nested + if (std::find_if(list.cbegin(), list.cend(), + [](const std::string& item) -> bool { + return cmHasPrefix(item, "<LINK_GROUP"_s); + }) != list.cend()) { + reportError(context, content->GetOriginalExpression(), + "$<LINK_GROUP:...> cannot be nested."); + return std::string(); + } + if (list.size() == 1) { + // no libraries specified, ignore this genex + return std::string(); + } + + static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$"); + auto const& feature = list.front(); + if (!featureNameValidator.find(feature)) { + reportError(context, content->GetOriginalExpression(), + cmStrCat("The feature name '", feature, + "' contains invalid characters.")); + return std::string(); + } + + const auto LG_BEGIN = cmStrCat( + "<LINK_GROUP:", feature, ':', + cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()), + "|"_s), + '>'); + const auto LG_END = cmStrCat("</LINK_GROUP:", feature, '>'); + + list.front() = LG_BEGIN; + list.push_back(LG_END); + + return cmJoin(list, ";"_s); + } +} linkGroupNode; + static const struct HostLinkNode : public cmGeneratorExpressionNode { HostLinkNode() {} // NOLINT(modernize-use-equals-default) @@ -2668,6 +2814,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "COMPILE_LANGUAGE", &languageNode }, { "LINK_LANG_AND_ID", &linkLanguageAndIdNode }, { "LINK_LANGUAGE", &linkLanguageNode }, + { "LINK_LIBRARY", &linkLibraryNode }, + { "LINK_GROUP", &linkGroupNode }, { "HOST_LINK", &hostLinkNode }, { "DEVICE_LINK", &deviceLinkNode }, { "SHELL_PATH", &shellPathNode } diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index afcac5c..8a6cc1c 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -2047,7 +2047,11 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, } else if (ext == "appxmanifest") { kind = SourceKindAppManifest; } else if (ext == "manifest") { - kind = SourceKindManifest; + if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) { + kind = SourceKindExtra; + } else { + kind = SourceKindManifest; + } } else if (ext == "pfx") { kind = SourceKindCertificate; } else if (ext == "xaml") { @@ -3447,22 +3451,22 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); // Check for special modes: `all`, `all-major`. - if (compiler == "NVIDIA" && - cmSystemTools::VersionCompare( - cmSystemTools::OP_GREATER_EQUAL, - this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"), - "11.5")) { - if (property == "all" || property == "all-major") { + if (property == "all" || property == "all-major") { + if (compiler == "NVIDIA" && + cmSystemTools::VersionCompare( + cmSystemTools::OP_GREATER_EQUAL, + this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"), + "11.5")) { flags = cmStrCat(flags, " -arch=", property); return; } - } - - if (property == "all") { - property = *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL"); - } else if (property == "all-major") { - property = - *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR"); + if (property == "all") { + property = + *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL"); + } else if (property == "all-major") { + property = + *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR"); + } } struct CudaArchitecture @@ -4623,7 +4627,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( } std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( - std::vector<BT<std::string>>& result, const std::string& language) const + std::vector<BT<std::string>>& result, const std::string& language, + bool joinItems) const { // replace "LINKER:" prefixed elements by actual linker wrapper const std::string wrapper(this->Makefile->GetSafeDefinition( @@ -4682,7 +4687,14 @@ std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( std::vector<BT<std::string>> options = wrapOptions( linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs); - result.insert(entry, options.begin(), options.end()); + if (joinItems) { + result.insert(entry, + cmJoin(cmRange<decltype(options.cbegin())>( + options.cbegin(), options.cend()), + " "_s)); + } else { + result.insert(entry, options.begin(), options.end()); + } } return result; } @@ -6329,7 +6341,8 @@ cm::string_view missingTargetPossibleReasons = bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, cmLinkItem const& item) const { - if (item.Target || item.AsStr().find("::") == std::string::npos) { + if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || + item.AsStr().find("::") == std::string::npos) { return true; } MessageType messageType = MessageType::FATAL_ERROR; @@ -6375,7 +6388,9 @@ bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, std::string const& str = item.AsStr(); if (!str.empty() && (str[0] == '-' || str[0] == '$' || str[0] == '`' || - str.find_first_of("/\\") != std::string::npos)) { + str.find_first_of("/\\") != std::string::npos || + cmHasPrefix(str, "<LINK_LIBRARY:"_s) || + cmHasPrefix(str, "<LINK_GROUP:"_s))) { return true; } diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 45639c0..3e30913 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -513,7 +513,8 @@ public: std::string const& config, std::string const& language) const; std::vector<BT<std::string>>& ResolveLinkerWrapper( - std::vector<BT<std::string>>& result, const std::string& language) const; + std::vector<BT<std::string>>& result, const std::string& language, + bool joinItems = false) const; void GetStaticLibraryLinkOptions(std::vector<std::string>& result, const std::string& config, diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 47cefae..c853c50 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -36,11 +36,6 @@ cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator())) , Makefile(target->Target->GetMakefile()) , Name(target->GetName()) -#ifdef _WIN32 - , CmdWindowsShell(true) -#else - , CmdWindowsShell(false) -#endif { // Store the configuration name that is being used if (cmValue config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) { @@ -316,19 +311,37 @@ void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout, void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout) { - this->WriteBuildEventsHelper( - fout, this->GeneratorTarget->GetPreBuildCommands(), - std::string("prebuild"), std::string("preexecShell")); + this->WriteBuildEventsHelper(fout, + this->GeneratorTarget->GetPreBuildCommands(), + std::string("prebuild"), +#ifdef _WIN32 + std::string("preexecShell") +#else + std::string("preexec") +#endif + ); if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { - this->WriteBuildEventsHelper( - fout, this->GeneratorTarget->GetPreLinkCommands(), - std::string("prelink"), std::string("preexecShell")); + this->WriteBuildEventsHelper(fout, + this->GeneratorTarget->GetPreLinkCommands(), + std::string("prelink"), +#ifdef _WIN32 + std::string("preexecShell") +#else + std::string("preexec") +#endif + ); } - this->WriteBuildEventsHelper( - fout, this->GeneratorTarget->GetPostBuildCommands(), - std::string("postbuild"), std::string("postexecShell")); + this->WriteBuildEventsHelper(fout, + this->GeneratorTarget->GetPostBuildCommands(), + std::string("postbuild"), +#ifdef _WIN32 + std::string("postexecShell") +#else + std::string("postexec") +#endif + ); } void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( @@ -336,6 +349,13 @@ void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( std::string const& name, std::string const& cmd) { int cmdcount = 0; +#ifdef _WIN32 + std::string fext = ".bat"; + std::string shell; +#else + std::string fext = ".sh"; + std::string shell = "/bin/sh "; +#endif for (cmCustomCommand const& cc : ccv) { cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator); @@ -343,14 +363,14 @@ void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( std::string fname = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), - '/', this->Name, '_', name, cmdcount++, - this->CmdWindowsShell ? ".bat" : ".sh"); + '/', this->Name, '_', name, cmdcount++, fext); + cmGeneratedFileStream f(fname); f.SetCopyIfDifferent(true); this->WriteCustomCommandsHelper(f, ccg); f.Close(); if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { - fout << " :" << cmd << "=\"" << fname << "\"\n"; + fout << " :" << cmd << "=\"" << shell << fname << "\"\n"; } else { fout << fname << "\n :outputName=\"" << fname << ".rule\"\n"; } @@ -409,15 +429,15 @@ void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper( // bool useCall = false; - if (this->CmdWindowsShell) { - std::string suffix; - if (cmd.size() > 4) { - suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4)); - if (suffix == ".bat" || suffix == ".cmd") { - useCall = true; - } +#ifdef _WIN32 + std::string suffix; + if (cmd.size() > 4) { + suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4)); + if (suffix == ".bat" || suffix == ".cmd") { + useCall = true; } } +#endif cmSystemTools::ReplaceString(cmd, "/./", "/"); // Convert the command to a relative path only if the current @@ -645,6 +665,11 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) } } int cmdcount = 0; +#ifdef _WIN32 + std::string fext = ".bat"; +#else + std::string fext = ".sh"; +#endif for (auto& sf : customCommands) { const cmCustomCommand* cc = sf->GetCustomCommand(); cmCustomCommandGenerator ccg(*cc, this->ConfigName, @@ -655,8 +680,8 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/', this->Name, "_cc", cmdcount++, '_', - (sf->GetLocation()).GetName(), - this->CmdWindowsShell ? ".bat" : ".sh"); + (sf->GetLocation()).GetName(), fext); + cmGeneratedFileStream f(fname); f.SetCopyIfDifferent(true); this->WriteCustomCommandsHelper(f, ccg); diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index e9d7537..9289a72 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h @@ -78,6 +78,5 @@ private: std::string TargetNameReal; GhsMultiGpj::Types TagType; std::string const Name; - std::string ConfigName; /* CMAKE_BUILD_TYPE */ - bool const CmdWindowsShell; /* custom commands run in cmd.exe or /bin/sh */ + std::string ConfigName; /* CMAKE_BUILD_TYPE */ }; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 156ecce..baa54e5 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -19,6 +19,7 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" #if defined(_WIN32) && !defined(__CYGWIN__) # include <windows.h> @@ -2522,6 +2523,47 @@ bool cmGlobalGenerator::NameResolvesToFramework( return false; } +// If the file has no extension it's either a raw executable or might +// be a direct reference to a binary within a framework (bad practice!). +// This is where we change the path to point to the framework directory. +// .tbd files also can be located in SDK frameworks (they are +// placeholders for actual libraries shipped with the OS) +cm::optional<std::pair<std::string, std::string>> +cmGlobalGenerator::SplitFrameworkPath(const std::string& path, + bool extendedFormat) const +{ + // Check for framework structure: + // (/path/to/)?FwName.framework + // or (/path/to/)?FwName.framework/FwName(.tbd)? + // or (/path/to/)?FwName.framework/Versions/*/FwName(.tbd)? + static cmsys::RegularExpression frameworkPath( + "((.+)/)?(.+)\\.framework(/Versions/[^/]+)?(/(.+))?$"); + + auto ext = cmSystemTools::GetFilenameLastExtension(path); + if ((ext.empty() || ext == ".tbd" || ext == ".framework") && + frameworkPath.find(path)) { + auto name = frameworkPath.match(3); + auto libname = + cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6)); + if (!libname.empty() && name != libname) { + return cm::nullopt; + } + return std::pair<std::string, std::string>{ frameworkPath.match(2), name }; + } + + if (extendedFormat) { + // path format can be more flexible: (/path/to/)?fwName(.framework)? + auto fwDir = cmSystemTools::GetParentDirectory(path); + auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework" + ? cmSystemTools::GetFilenameWithoutExtension(path) + : cmSystemTools::GetFilenameName(path); + + return std::pair<std::string, std::string>{ fwDir, name }; + } + + return cm::nullopt; +} + bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, std::string const& reason) const { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index a43d4a6..a4b2ae3 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -367,6 +367,13 @@ public: /** Determine if a name resolves to a framework on disk or a built target that is a framework. */ bool NameResolvesToFramework(const std::string& libname) const; + /** Split a framework path to the directory and name of the framework + * returns std::nullopt if the path does not match with framework format + * when extendedFormat is true, required format is relaxed (i.e. extension + * `.framework' is optional). Used when FRAMEWORK link feature is + * specified */ + cm::optional<std::pair<std::string, std::string>> SplitFrameworkPath( + const std::string& path, bool extendedFormat = false) const; cmMakefile* FindMakefile(const std::string& start_dir) const; cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 4245037..bbc9c54 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -2495,8 +2495,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( snapshot.GetDirectory().SetCurrentBinary(dir_cur_bld); auto mfd = cm::make_unique<cmMakefile>(this, snapshot); auto lgd = this->CreateLocalGenerator(mfd.get()); - lgd->SetRelativePathTopSource(dir_top_src); - lgd->SetRelativePathTopBinary(dir_top_bld); + lgd->SetRelativePathTop(dir_top_src, dir_top_bld); this->Makefiles.push_back(std::move(mfd)); this->LocalGenerators.push_back(std::move(lgd)); } diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index db54b86..35448ee 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -1286,6 +1286,14 @@ cmGlobalVisualStudio10Generator::GenerateBuildCommand( return makeCommands; } +bool cmGlobalVisualStudio10Generator::IsInSolution( + const cmGeneratorTarget* gt) const +{ + return gt->IsInBuildSystem() && + !(this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 && + gt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET); +} + bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf) { if (this->DefaultPlatformToolset == "v100") { diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 4977a84..2203f71 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -118,6 +118,8 @@ public: return this->WindowsTargetPlatformVersion; } + bool IsInSolution(const cmGeneratorTarget* gt) const override; + /** Return true if building for WindowsCE */ bool TargetsWindowsCE() const override { return this->SystemIsWindowsCE; } diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 134937e..1c10fb3 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -359,7 +359,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetConfigurations( // loop over again and write out configurations for each target // in the solution for (cmGeneratorTarget const* target : projectTargets) { - if (!target->IsInBuildSystem()) { + if (!this->IsInSolution(target)) { continue; } cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); @@ -396,7 +396,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( VisualStudioFolders.clear(); for (cmGeneratorTarget const* target : projectTargets) { - if (!target->IsInBuildSystem()) { + if (!this->IsInSolution(target)) { continue; } bool written = false; @@ -458,22 +458,6 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( } } -void cmGlobalVisualStudio7Generator::WriteTargetDepends( - std::ostream& fout, OrderedTargetDependSet const& projectTargets) -{ - for (cmGeneratorTarget const* target : projectTargets) { - if (!target->IsInBuildSystem()) { - continue; - } - cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME"); - if (vcprojName) { - std::string dir = - target->GetLocalGenerator()->GetCurrentSourceDirectory(); - this->WriteProjectDepends(fout, *vcprojName, dir, target); - } - } -} - void cmGlobalVisualStudio7Generator::WriteFolders(std::ostream& fout) { cm::string_view const prefix = "CMAKE_FOLDER_GUID_"; diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 33f1063..a55cf45 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -148,8 +148,6 @@ protected: virtual void WriteTargetsToSolution( std::ostream& fout, cmLocalGenerator* root, OrderedTargetDependSet const& projectTargets); - virtual void WriteTargetDepends( - std::ostream& fout, OrderedTargetDependSet const& projectTargets); virtual void WriteTargetConfigurations( std::ostream& fout, std::vector<std::string> const& configs, OrderedTargetDependSet const& projectTargets); diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index dbd2de9..323ee67 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -392,7 +392,7 @@ void cmGlobalVisualStudio8Generator::WriteProjectDepends( TargetDependSet const& unordered = this->GetTargetDirectDepends(gt); OrderedTargetDependSet depends(unordered, std::string()); for (cmTargetDepend const& i : depends) { - if (!i->IsInBuildSystem()) { + if (!this->IsInSolution(i)) { continue; } std::string guid = this->GetGUID(i->GetName()); diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 141b5eb..cddaaa4 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -837,6 +837,12 @@ bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly( return languages.size() == 1 && *languages.begin() == "Fortran"; } +bool cmGlobalVisualStudioGenerator::IsInSolution( + const cmGeneratorTarget* gt) const +{ + return gt->IsInBuildSystem(); +} + bool cmGlobalVisualStudioGenerator::TargetCompare::operator()( cmGeneratorTarget const* l, cmGeneratorTarget const* r) const { diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index cb1b14b..4f5f100 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -98,6 +98,9 @@ public: // return true if target is fortran only bool TargetIsFortranOnly(const cmGeneratorTarget* gt); + // return true if target should be included in solution. + virtual bool IsInSolution(const cmGeneratorTarget* gt) const; + /** Get the top-level registry key for this VS version. */ std::string GetRegistryBase(); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 203addd..1e6624e 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1154,47 +1154,25 @@ std::string GetSourcecodeValueFromFileExtension( return sourcecode; } -// If the file has no extension it's either a raw executable or might -// be a direct reference to a binary within a framework (bad practice!). -// This is where we change the path to point to the framework directory. -// .tbd files also can be located in SDK frameworks (they are -// placeholders for actual libraries shipped with the OS) -std::string GetLibraryOrFrameworkPath(const std::string& path) +} // anonymous + +// Extracts the framework directory, if path matches the framework syntax +// otherwise returns the path untouched +std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath( + const std::string& path) const { - auto ext = cmSystemTools::GetFilenameLastExtension(path); - if (ext.empty() || ext == ".tbd") { - auto name = cmSystemTools::GetFilenameWithoutExtension(path); - // Check for iOS framework structure: - // FwName.framework/FwName (and also on macOS where FwName lib is a - // symlink) - auto parentDir = cmSystemTools::GetParentDirectory(path); - auto parentName = cmSystemTools::GetFilenameWithoutExtension(parentDir); - ext = cmSystemTools::GetFilenameLastExtension(parentDir); - if (ext == ".framework" && name == parentName) { - return parentDir; - } - // Check for macOS framework structure: - // FwName.framework/Versions/*/FwName - std::vector<std::string> components; - cmSystemTools::SplitPath(path, components); - if (components.size() > 3 && - components[components.size() - 3] == "Versions") { - ext = cmSystemTools::GetFilenameLastExtension( - components[components.size() - 4]); - parentName = cmSystemTools::GetFilenameWithoutExtension( - components[components.size() - 4]); - if (ext == ".framework" && name == parentName) { - components.erase(components.begin() + components.size() - 3, - components.end()); - return cmSystemTools::JoinPath(components); - } + auto fwItems = this->SplitFrameworkPath(path); + if (fwItems) { + if (fwItems->first.empty()) { + return cmStrCat(fwItems->second, ".framework"); + } else { + return cmStrCat(fwItems->first, '/', fwItems->second, ".framework"); } } + return path; } -} // anonymous - cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( const std::string& fullpath, cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf) @@ -1217,7 +1195,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( ext = ext.substr(1); } if (fileType.empty()) { - path = GetLibraryOrFrameworkPath(path); + path = this->GetLibraryOrFrameworkPath(path); ext = cmSystemTools::GetFilenameLastExtension(path); if (!ext.empty()) { ext = ext.substr(1); @@ -3541,13 +3519,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) } else { linkDir = libItem->Value.Value; } - linkDir = GetLibraryOrFrameworkPath(linkDir); - bool isFramework = cmSystemTools::IsPathToFramework(linkDir); - linkDir = cmSystemTools::GetParentDirectory(linkDir); - if (isFramework) { - if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), - linkDir) == frameworkSearchPaths.end()) { - frameworkSearchPaths.push_back(linkDir); + if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) { + auto fwItems = this->SplitFrameworkPath(linkDir, true); + if (fwItems && !fwItems->first.empty()) { + linkDir = std::move(fwItems->first); + if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), + linkDir) == frameworkSearchPaths.end()) { + frameworkSearchPaths.push_back(linkDir); + } } } else { if (std::find(linkSearchPaths.begin(), linkSearchPaths.end(), linkDir) == @@ -3555,7 +3534,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) linkSearchPaths.push_back(linkDir); } } - // Add target dependency + if (libItem->Target && !libItem->Target->IsImported()) { for (auto const& configName : this->CurrentConfigurationTypes) { target->AddDependTarget(configName, libItem->Target->GetName()); @@ -3729,22 +3708,27 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) if (cmSystemTools::FileIsFullPath(cleanPath)) { cleanPath = cmSystemTools::CollapseFullPath(cleanPath); } - const auto libPath = GetLibraryOrFrameworkPath(cleanPath); - if (cmSystemTools::StringEndsWith(libPath.c_str(), ".framework")) { - const auto fwName = - cmSystemTools::GetFilenameWithoutExtension(libPath); - const auto fwDir = cmSystemTools::GetParentDirectory(libPath); - if (emitted.insert(fwDir).second) { - // This is a search path we had not added before and it isn't an - // implicit search path, so we need it - libPaths.Add("-F " + this->XCodeEscapePath(fwDir)); + bool isFramework = + cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s); + if (isFramework) { + const auto fwItems = + this->SplitFrameworkPath(cleanPath, isFramework); + if (!fwItems->first.empty() && + emitted.insert(fwItems->first).second) { + // This is a search path we had not added before and it isn't + // an implicit search path, so we need it + libPaths.Add("-F " + this->XCodeEscapePath(fwItems->first)); } - libPaths.Add("-framework " + this->XCodeEscapePath(fwName)); + libPaths.Add( + libName.GetFormattedItem(this->XCodeEscapePath(fwItems->second)) + .Value); } else { - libPaths.Add(this->XCodeEscapePath(cleanPath)); + libPaths.Add( + libName.GetFormattedItem(this->XCodeEscapePath(cleanPath)) + .Value); } if ((!libName.Target || libName.Target->IsImported()) && - IsLinkPhaseLibraryExtension(libPath)) { + (isFramework || IsLinkPhaseLibraryExtension(cleanPath))) { // Create file reference for embedding auto it = this->ExternalLibRefs.find(cleanPath); if (it == this->ExternalLibRefs.end()) { @@ -3912,8 +3896,8 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target) { static const auto dstSubfolderSpec = "10"; - // Despite the name, by default Xcode uses "Embed Frameworks" build phase for - // both frameworks and dynamic libraries + // Despite the name, by default Xcode uses "Embed Frameworks" build phase + // for both frameworks and dynamic libraries this->AddEmbeddedObjects(target, "Embed Frameworks", "XCODE_EMBED_FRAMEWORKS", dstSubfolderSpec, NoActionOnCopyByDefault); @@ -4711,10 +4695,12 @@ std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake( std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p) { - // We force conversion because Xcode breakpoints do not work unless - // they are in a file named relative to the source tree. - return cmSystemTools::ForceToRelativePath( - this->CurrentRootGenerator->GetCurrentSourceDirectory(), p); + std::string const& rootSrc = + this->CurrentRootGenerator->GetCurrentSourceDirectory(); + if (cmSystemTools::IsSubDirectory(p, rootSrc)) { + return cmSystemTools::ForceToRelativePath(rootSrc, p); + } + return p; } std::string cmGlobalXCodeGenerator::RelativeToBinary(const std::string& p) diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index ff6ffe8..98cebef 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -330,6 +330,8 @@ private: { } + std::string GetLibraryOrFrameworkPath(const std::string& path) const; + std::string GetObjectsDirectory(const std::string& projName, const std::string& configName, const cmGeneratorTarget* t, diff --git a/Source/cmLinkLibrariesCommand.cxx b/Source/cmLinkLibrariesCommand.cxx index 2b8f836..ed89e91 100644 --- a/Source/cmLinkLibrariesCommand.cxx +++ b/Source/cmLinkLibrariesCommand.cxx @@ -35,5 +35,7 @@ bool cmLinkLibrariesCommand(std::vector<std::string> const& args, mf.AppendProperty("LINK_LIBRARIES", *i); } + mf.CheckProperty("LINK_LIBRARIES"); + return true; } diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx index 5646368..290642b 100644 --- a/Source/cmLinkLineComputer.cxx +++ b/Source/cmLinkLineComputer.cxx @@ -75,14 +75,8 @@ void cmLinkLineComputer::ComputeLinkLibs( BT<std::string> linkLib; if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) { - if (item.IsObject == cmComputeLinkInformation::ItemIsObject::Yes) { - linkLib.Value += cli.GetObjLinkFileFlag(); - } else { - linkLib.Value += cli.GetLibLinkFileFlag(); - } - linkLib.Value += this->ConvertToOutputFormat( - this->ConvertToLinkReference(item.Value.Value)); - linkLib.Backtrace = item.Value.Backtrace; + linkLib = item.GetFormattedItem(this->ConvertToOutputFormat( + this->ConvertToLinkReference(item.Value.Value))); } else { linkLib = item.Value; } diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 43f161b..71f9f80 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -118,8 +118,10 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries( // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'. if (cmHasLiteralSuffix(item.Value.Value, ".a") || cmHasLiteralSuffix(item.Value.Value, ".lib")) { - linkLib.Value += this->ConvertToOutputFormat( - this->ConvertToLinkReference(item.Value.Value)); + linkLib.Value = item + .GetFormattedItem(this->ConvertToOutputFormat( + this->ConvertToLinkReference(item.Value.Value))) + .Value; } } else if (item.Value == "-framework") { // This is the first part of '-framework Name' where the framework @@ -127,7 +129,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries( skipItemAfterFramework = true; continue; } else if (cmLinkItemValidForDevice(item.Value.Value)) { - linkLib.Value += item.Value.Value; + linkLib.Value = item.Value.Value; } if (emitted.insert(linkLib.Value).second) { diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 3da266d..b90af08 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -40,6 +40,7 @@ struct cmListFileParser cmListFileLexer* Lexer; std::string FunctionName; long FunctionLine; + long FunctionLineEnd; std::vector<cmListFileArgument> FunctionArguments; enum { @@ -146,7 +147,7 @@ bool cmListFileParser::Parse() if (this->ParseFunction(token->text, token->line)) { this->ListFile->Functions.emplace_back( std::move(this->FunctionName), this->FunctionLine, - std::move(this->FunctionArguments)); + this->FunctionLineEnd, std::move(this->FunctionArguments)); } else { return false; } @@ -259,6 +260,7 @@ bool cmListFileParser::ParseFunction(const char* name, long line) } } else if (token->type == cmListFileLexer_Token_ParenRight) { if (parenDepth == 0) { + this->FunctionLineEnd = token->line; return true; } parenDepth--; @@ -539,11 +541,11 @@ std::ostream& operator<<(std::ostream& os, BT<std::string> const& s) return os << s.Value; } -std::vector<BT<std::string>> ExpandListWithBacktrace( - std::string const& list, cmListFileBacktrace const& bt) +std::vector<BT<std::string>> cmExpandListWithBacktrace( + std::string const& list, cmListFileBacktrace const& bt, bool emptyArgs) { std::vector<BT<std::string>> result; - std::vector<std::string> tmp = cmExpandedList(list); + std::vector<std::string> tmp = cmExpandedList(list, emptyArgs); result.reserve(tmp.size()); for (std::string& i : tmp) { result.emplace_back(std::move(i), bt); diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 5d45027..c3da81b 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -51,9 +51,9 @@ struct cmListFileArgument class cmListFileFunction { public: - cmListFileFunction(std::string name, long line, + cmListFileFunction(std::string name, long line, long lineEnd, std::vector<cmListFileArgument> args) - : Impl{ std::make_shared<Implementation>(std::move(name), line, + : Impl{ std::make_shared<Implementation>(std::move(name), line, lineEnd, std::move(args)) } { } @@ -69,6 +69,7 @@ public: } long Line() const noexcept { return this->Impl->Line; } + long LineEnd() const noexcept { return this->Impl->LineEnd; } std::vector<cmListFileArgument> const& Arguments() const noexcept { @@ -78,11 +79,12 @@ public: private: struct Implementation { - Implementation(std::string name, long line, + Implementation(std::string name, long line, long lineEnd, std::vector<cmListFileArgument> args) : OriginalName{ std::move(name) } , LowerCaseName{ cmSystemTools::LowerCase(this->OriginalName) } , Line{ line } + , LineEnd{ lineEnd } , Arguments{ std::move(args) } { } @@ -90,6 +92,7 @@ private: std::string OriginalName; std::string LowerCaseName; long Line = 0; + long LineEnd = 0; std::vector<cmListFileArgument> Arguments; }; @@ -230,9 +233,10 @@ public: friend bool operator==(T const& l, BTs<T> const& r) { return l == r.Value; } }; -std::vector<BT<std::string>> ExpandListWithBacktrace( +std::vector<BT<std::string>> cmExpandListWithBacktrace( std::string const& list, - cmListFileBacktrace const& bt = cmListFileBacktrace()); + cmListFileBacktrace const& bt = cmListFileBacktrace(), + bool emptyArgs = false); struct cmListFile { diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 2adb232..3976c42 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -3199,7 +3199,7 @@ void cmLocalGenerator::AppendDefines(std::set<std::string>& defines, std::string const& defines_list) const { std::set<BT<std::string>> tmp; - this->AppendDefines(tmp, ExpandListWithBacktrace(defines_list)); + this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list)); for (BT<std::string> const& i : tmp) { defines.emplace(i.Value); } @@ -3214,7 +3214,7 @@ void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines, } // Expand the list of definitions. - this->AppendDefines(defines, ExpandListWithBacktrace(defines_list)); + this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list)); } void cmLocalGenerator::AppendDefines( diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 0f8cdca..7c4af7d 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1508,13 +1508,12 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies( } // Setup relative path top directories. - if (cmValue relativePathTopSource = - mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE")) { - this->SetRelativePathTopSource(*relativePathTopSource); - } - if (cmValue relativePathTopBinary = - mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY")) { - this->SetRelativePathTopBinary(*relativePathTopBinary); + cmValue relativePathTopSource = + mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE"); + cmValue relativePathTopBinary = + mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY"); + if (relativePathTopSource && relativePathTopBinary) { + this->SetRelativePathTop(*relativePathTopSource, *relativePathTopBinary); } } else { cmSystemTools::Error("Directory Information file not found"); @@ -1849,7 +1848,7 @@ void cmLocalUnixMakefileGenerator3::ClearDependencies(cmMakefile* mf, cmSystemTools::Touch(DepTimestamp.GenericString(), true); // clear the dependencies files generated by the compiler - std::vector<std::string> dependencies = cmExpandedList(depsFiles); + std::vector<std::string> dependencies = cmExpandedList(depsFiles, true); cmDependsCompiler depsManager; depsManager.SetVerbose(verbose); depsManager.ClearDependencies(dependencies); diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index ed7e888..f65add1 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -1294,7 +1294,9 @@ void cmLocalVisualStudio7GeneratorInternals::OutputLibraries( for (auto const& lib : libs) { if (lib.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) { std::string rel = lg->MaybeRelativeToCurBinDir(lib.Value.Value); - fout << lg->ConvertToXMLOutputPath(rel) << " "; + rel = lg->ConvertToXMLOutputPath(rel); + fout << (lib.HasFeature() ? lib.GetFormattedItem(rel).Value : rel) + << " "; } else if (!lib.Target || lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { fout << lib.Value.Value << " "; diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx index 93f01ed..2703c7b 100644 --- a/Source/cmLocalVisualStudioGenerator.cxx +++ b/Source/cmLocalVisualStudioGenerator.cxx @@ -252,7 +252,7 @@ std::string cmLocalVisualStudioGenerator::FinishConstructScript( // Store the script in a string. std::string script; - if (useLocal && projectType == VsProjectType::csproj) { + if (useLocal && projectType != VsProjectType::vcxproj) { // This label is not provided by MSBuild for C# projects. script += newline; script += this->GetReportErrorLabel(); diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 154df63..ef12487 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -116,7 +116,7 @@ bool cmMacroHelperCommand::operator()( newLFFArgs.push_back(std::move(arg)); } cmListFileFunction newLFF{ func.OriginalName(), func.Line(), - std::move(newLFFArgs) }; + func.LineEnd(), std::move(newLFFArgs) }; cmExecutionStatus status(makefile); if (!makefile.ExecuteCommand(newLFF, status) || status.GetNestedError()) { // The error message should have already included the call stack diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 94d3be6..be189a6 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -291,6 +291,9 @@ void cmMakefile::PrintCommandTrace( builder["indentation"] = ""; val["file"] = full_path; val["line"] = static_cast<Json::Value::Int64>(lff.Line()); + if (lff.Line() != lff.LineEnd()) { + val["line_end"] = static_cast<Json::Value::Int64>(lff.LineEnd()); + } if (deferId) { val["defer"] = *deferId; } @@ -302,6 +305,8 @@ void cmMakefile::PrintCommandTrace( val["time"] = cmSystemTools::GetTime(); val["frame"] = static_cast<Json::Value::UInt64>(this->ExecutionStatusStack.size()); + val["global_frame"] = + static_cast<Json::Value::UInt64>(this->RecursionDepth); msg << Json::writeString(builder, val); #endif break; @@ -1663,6 +1668,7 @@ void cmMakefile::Configure() this->Backtrace); cmListFileFunction project{ "project", 0, + 0, { { "Project", cmListFileArgument::Unquoted, 0 }, { "__CMAKE_INJECTED_PROJECT_COMMAND__", @@ -3976,6 +3982,31 @@ std::vector<std::string> cmMakefile::GetPropertyKeys() const return this->StateSnapshot.GetDirectory().GetPropertyKeys(); } +void cmMakefile::CheckProperty(const std::string& prop) const +{ + // Certain properties need checking. + if (prop == "LINK_LIBRARIES") { + if (cmValue value = this->GetProperty(prop)) { + // Look for <LINK_LIBRARY:> internal pattern + static cmsys::RegularExpression linkPattern( + "(^|;)(</?LINK_(LIBRARY|GROUP):[^;>]*>)(;|$)"); + if (!linkPattern.find(value)) { + return; + } + + // Report an error. + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Property ", prop, " contains the invalid item \"", + linkPattern.match(2), "\". The ", prop, + " property may contain the generator-expression \"$<LINK_", + linkPattern.match(3), + ":...>\" which may be used to specify how the libraries are " + "linked.")); + } + } +} + cmTarget* cmMakefile::FindLocalNonAliasTarget(const std::string& name) const { auto i = this->Targets.find(name); @@ -4395,12 +4426,14 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0094 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 || - id == cmPolicies::CMP0091))) { + id == cmPolicies::CMP0091)) && + (!this->IsSet("CMAKE_WARN_DEPRECATED") || + this->IsOn("CMAKE_WARN_DEPRECATED"))) { this->IssueMessage(MessageType::DEPRECATION_WARNING, cmPolicies::GetPolicyDeprecatedWarning(id)); } diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index f425697..ad8a014 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -787,6 +787,7 @@ public: cmValue GetProperty(const std::string& prop, bool chain) const; bool GetPropertyAsBool(const std::string& prop) const; std::vector<std::string> GetPropertyKeys() const; + void CheckProperty(const std::string& prop) const; //! Initialize a makefile from its parent void InitializeFromParent(cmMakefile* parent); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 191b428..dd7d244 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -216,7 +216,8 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( // Add Fortran format flags. if (language == "Fortran") { this->AppendFortranFormatFlags(flags, *source); - this->AppendFortranPreprocessFlags(flags, *source); + this->AppendFortranPreprocessFlags(flags, *source, + PreprocessFlagsRequired::NO); } // Add source file specific flags. diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index b143170..e62e0cd 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -34,6 +34,7 @@ cmOutputConverter::cmOutputConverter(cmStateSnapshot const& snapshot) assert(this->StateSnapshot.IsValid()); this->ComputeRelativePathTopSource(); this->ComputeRelativePathTopBinary(); + this->ComputeRelativePathTopRelation(); } void cmOutputConverter::ComputeRelativePathTopSource() @@ -69,6 +70,22 @@ void cmOutputConverter::ComputeRelativePathTopBinary() this->RelativePathTopBinary = snapshot.GetDirectory().GetCurrentBinary(); } +void cmOutputConverter::ComputeRelativePathTopRelation() +{ + if (cmSystemTools::ComparePath(this->RelativePathTopSource, + this->RelativePathTopBinary)) { + this->RelativePathTopRelation = TopRelation::InSource; + } else if (cmSystemTools::IsSubDirectory(this->RelativePathTopBinary, + this->RelativePathTopSource)) { + this->RelativePathTopRelation = TopRelation::BinInSrc; + } else if (cmSystemTools::IsSubDirectory(this->RelativePathTopSource, + this->RelativePathTopBinary)) { + this->RelativePathTopRelation = TopRelation::SrcInBin; + } else { + this->RelativePathTopRelation = TopRelation::Separate; + } +} + std::string const& cmOutputConverter::GetRelativePathTopSource() const { return this->RelativePathTopSource; @@ -79,27 +96,45 @@ std::string const& cmOutputConverter::GetRelativePathTopBinary() const return this->RelativePathTopBinary; } -void cmOutputConverter::SetRelativePathTopSource(std::string const& top) -{ - this->RelativePathTopSource = top; -} - -void cmOutputConverter::SetRelativePathTopBinary(std::string const& top) +void cmOutputConverter::SetRelativePathTop(std::string const& topSource, + std::string const& topBinary) { - this->RelativePathTopBinary = top; + this->RelativePathTopSource = topSource; + this->RelativePathTopBinary = topBinary; + this->ComputeRelativePathTopRelation(); } std::string cmOutputConverter::MaybeRelativeTo( std::string const& local_path, std::string const& remote_path) const { - bool bothInBinary = - PathEqOrSubDir(local_path, this->RelativePathTopBinary) && + bool localInBinary = PathEqOrSubDir(local_path, this->RelativePathTopBinary); + bool remoteInBinary = PathEqOrSubDir(remote_path, this->RelativePathTopBinary); - bool bothInSource = - PathEqOrSubDir(local_path, this->RelativePathTopSource) && + bool localInSource = PathEqOrSubDir(local_path, this->RelativePathTopSource); + bool remoteInSource = PathEqOrSubDir(remote_path, this->RelativePathTopSource); + switch (this->RelativePathTopRelation) { + case TopRelation::Separate: + // Checks are independent. + break; + case TopRelation::BinInSrc: + localInSource = localInSource && !localInBinary; + remoteInSource = remoteInSource && !remoteInBinary; + break; + case TopRelation::SrcInBin: + localInBinary = localInBinary && !localInSource; + remoteInBinary = remoteInBinary && !remoteInSource; + break; + case TopRelation::InSource: + // Checks are identical. + break; + }; + + bool const bothInBinary = localInBinary && remoteInBinary; + bool const bothInSource = localInSource && remoteInSource; + if (bothInBinary || bothInSource) { return cmSystemTools::ForceToRelativePath(local_path, remote_path); } diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index 335442d..d19bccc 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h @@ -29,8 +29,8 @@ public: std::string const& GetRelativePathTopSource() const; std::string const& GetRelativePathTopBinary() const; - void SetRelativePathTopSource(std::string const& top); - void SetRelativePathTopBinary(std::string const& top); + void SetRelativePathTop(std::string const& topSource, + std::string const& topBinary); enum OutputFormat { @@ -147,8 +147,17 @@ private: // safely by the build tools. std::string RelativePathTopSource; std::string RelativePathTopBinary; + enum class TopRelation + { + Separate, + BinInSrc, + SrcInBin, + InSource, + }; + TopRelation RelativePathTopRelation = TopRelation::Separate; void ComputeRelativePathTopSource(); void ComputeRelativePathTopBinary(); + void ComputeRelativePathTopRelation(); std::string MaybeRelativeTo(std::string const& local_path, std::string const& remote_path) const; }; diff --git a/Source/cmPlaceholderExpander.cxx b/Source/cmPlaceholderExpander.cxx new file mode 100644 index 0000000..118017e --- /dev/null +++ b/Source/cmPlaceholderExpander.cxx @@ -0,0 +1,54 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmPlaceholderExpander.h" + +#include <cctype> + +std::string& cmPlaceholderExpander::ExpandVariables(std::string& s) +{ + std::string::size_type start = s.find('<'); + // no variables to expand + if (start == std::string::npos) { + return s; + } + std::string::size_type pos = 0; + std::string expandedInput; + while (start != std::string::npos && start < s.size() - 2) { + std::string::size_type end = s.find('>', start); + // if we find a < with no > we are done + if (end == std::string::npos) { + s = expandedInput; + return s; + } + char c = s[start + 1]; + // if the next char after the < is not A-Za-z then + // skip it and try to find the next < in the string + if (!isalpha(c)) { + start = s.find('<', start + 1); + } else { + // extract the var + std::string var = s.substr(start + 1, end - start - 1); + std::string replace = this->ExpandVariable(var); + expandedInput += s.substr(pos, start - pos); + + // Prevent consecutive whitespace in the output if the rule variable + // expands to an empty string. + bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' && + end + 1 < s.size() && s[end + 1] == ' '; + if (consecutive) { + expandedInput.pop_back(); + } + + expandedInput += replace; + + // move to next one + start = s.find('<', start + var.size() + 2); + pos = end + 1; + } + } + // add the rest of the input + expandedInput += s.substr(pos, s.size() - pos); + s = expandedInput; + + return s; +} diff --git a/Source/cmPlaceholderExpander.h b/Source/cmPlaceholderExpander.h new file mode 100644 index 0000000..24225cc --- /dev/null +++ b/Source/cmPlaceholderExpander.h @@ -0,0 +1,19 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> + +class cmPlaceholderExpander +{ +public: + virtual ~cmPlaceholderExpander() = default; + + std::string& ExpandVariables(std::string& string); + +protected: + virtual std::string ExpandVariable(std::string const& variable) = 0; +}; diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx index 4cee09d..b63d11c 100644 --- a/Source/cmRulePlaceholderExpander.cxx +++ b/Source/cmRulePlaceholderExpander.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmRulePlaceholderExpander.h" -#include <cctype> #include <utility> #include "cmOutputConverter.h" @@ -20,195 +19,194 @@ cmRulePlaceholderExpander::cmRulePlaceholderExpander( { } -std::string cmRulePlaceholderExpander::ExpandRuleVariable( - cmOutputConverter* outputConverter, std::string const& variable, - const RuleVariables& replaceValues) +std::string cmRulePlaceholderExpander::ExpandVariable( + std::string const& variable) { - if (replaceValues.LinkFlags) { + if (this->ReplaceValues->LinkFlags) { if (variable == "LINK_FLAGS") { - return replaceValues.LinkFlags; + return this->ReplaceValues->LinkFlags; } } - if (replaceValues.Manifests) { + if (this->ReplaceValues->Manifests) { if (variable == "MANIFESTS") { - return replaceValues.Manifests; + return this->ReplaceValues->Manifests; } } - if (replaceValues.Flags) { + if (this->ReplaceValues->Flags) { if (variable == "FLAGS") { - return replaceValues.Flags; + return this->ReplaceValues->Flags; } } - if (replaceValues.Source) { + if (this->ReplaceValues->Source) { if (variable == "SOURCE") { - return replaceValues.Source; + return this->ReplaceValues->Source; } } - if (replaceValues.DynDepFile) { + if (this->ReplaceValues->DynDepFile) { if (variable == "DYNDEP_FILE") { - return replaceValues.DynDepFile; + return this->ReplaceValues->DynDepFile; } } - if (replaceValues.PreprocessedSource) { + if (this->ReplaceValues->PreprocessedSource) { if (variable == "PREPROCESSED_SOURCE") { - return replaceValues.PreprocessedSource; + return this->ReplaceValues->PreprocessedSource; } } - if (replaceValues.AssemblySource) { + if (this->ReplaceValues->AssemblySource) { if (variable == "ASSEMBLY_SOURCE") { - return replaceValues.AssemblySource; + return this->ReplaceValues->AssemblySource; } } - if (replaceValues.Object) { + if (this->ReplaceValues->Object) { if (variable == "OBJECT") { - return replaceValues.Object; + return this->ReplaceValues->Object; } } - if (replaceValues.ObjectDir) { + if (this->ReplaceValues->ObjectDir) { if (variable == "OBJECT_DIR") { - return replaceValues.ObjectDir; + return this->ReplaceValues->ObjectDir; } } - if (replaceValues.ObjectFileDir) { + if (this->ReplaceValues->ObjectFileDir) { if (variable == "OBJECT_FILE_DIR") { - return replaceValues.ObjectFileDir; + return this->ReplaceValues->ObjectFileDir; } } - if (replaceValues.Objects) { + if (this->ReplaceValues->Objects) { if (variable == "OBJECTS") { - return replaceValues.Objects; + return this->ReplaceValues->Objects; } } - if (replaceValues.ObjectsQuoted) { + if (this->ReplaceValues->ObjectsQuoted) { if (variable == "OBJECTS_QUOTED") { - return replaceValues.ObjectsQuoted; + return this->ReplaceValues->ObjectsQuoted; } } - if (replaceValues.CudaCompileMode) { + if (this->ReplaceValues->CudaCompileMode) { if (variable == "CUDA_COMPILE_MODE") { - return replaceValues.CudaCompileMode; + return this->ReplaceValues->CudaCompileMode; } } - if (replaceValues.AIXExports) { + if (this->ReplaceValues->AIXExports) { if (variable == "AIX_EXPORTS") { - return replaceValues.AIXExports; + return this->ReplaceValues->AIXExports; } } - if (replaceValues.ISPCHeader) { + if (this->ReplaceValues->ISPCHeader) { if (variable == "ISPC_HEADER") { - return replaceValues.ISPCHeader; + return this->ReplaceValues->ISPCHeader; } } - if (replaceValues.Defines && variable == "DEFINES") { - return replaceValues.Defines; + if (this->ReplaceValues->Defines && variable == "DEFINES") { + return this->ReplaceValues->Defines; } - if (replaceValues.Includes && variable == "INCLUDES") { - return replaceValues.Includes; + if (this->ReplaceValues->Includes && variable == "INCLUDES") { + return this->ReplaceValues->Includes; } - if (replaceValues.SwiftLibraryName) { + if (this->ReplaceValues->SwiftLibraryName) { if (variable == "SWIFT_LIBRARY_NAME") { - return replaceValues.SwiftLibraryName; + return this->ReplaceValues->SwiftLibraryName; } } - if (replaceValues.SwiftModule) { + if (this->ReplaceValues->SwiftModule) { if (variable == "SWIFT_MODULE") { - return replaceValues.SwiftModule; + return this->ReplaceValues->SwiftModule; } } - if (replaceValues.SwiftModuleName) { + if (this->ReplaceValues->SwiftModuleName) { if (variable == "SWIFT_MODULE_NAME") { - return replaceValues.SwiftModuleName; + return this->ReplaceValues->SwiftModuleName; } } - if (replaceValues.SwiftOutputFileMap) { + if (this->ReplaceValues->SwiftOutputFileMap) { if (variable == "SWIFT_OUTPUT_FILE_MAP") { - return replaceValues.SwiftOutputFileMap; + return this->ReplaceValues->SwiftOutputFileMap; } } - if (replaceValues.SwiftSources) { + if (this->ReplaceValues->SwiftSources) { if (variable == "SWIFT_SOURCES") { - return replaceValues.SwiftSources; + return this->ReplaceValues->SwiftSources; } } - if (replaceValues.TargetPDB) { + if (this->ReplaceValues->TargetPDB) { if (variable == "TARGET_PDB") { - return replaceValues.TargetPDB; + return this->ReplaceValues->TargetPDB; } } - if (replaceValues.TargetCompilePDB) { + if (this->ReplaceValues->TargetCompilePDB) { if (variable == "TARGET_COMPILE_PDB") { - return replaceValues.TargetCompilePDB; + return this->ReplaceValues->TargetCompilePDB; } } - if (replaceValues.DependencyFile) { + if (this->ReplaceValues->DependencyFile) { if (variable == "DEP_FILE") { - return replaceValues.DependencyFile; + return this->ReplaceValues->DependencyFile; } } - if (replaceValues.DependencyTarget) { + if (this->ReplaceValues->DependencyTarget) { if (variable == "DEP_TARGET") { - return replaceValues.DependencyTarget; + return this->ReplaceValues->DependencyTarget; } } - if (replaceValues.Fatbinary) { + if (this->ReplaceValues->Fatbinary) { if (variable == "FATBINARY") { - return replaceValues.Fatbinary; + return this->ReplaceValues->Fatbinary; } } - if (replaceValues.RegisterFile) { + if (this->ReplaceValues->RegisterFile) { if (variable == "REGISTER_FILE") { - return replaceValues.RegisterFile; + return this->ReplaceValues->RegisterFile; } } - if (replaceValues.Target) { + if (this->ReplaceValues->Target) { if (variable == "TARGET_QUOTED") { - std::string targetQuoted = replaceValues.Target; + std::string targetQuoted = this->ReplaceValues->Target; if (!targetQuoted.empty() && targetQuoted.front() != '\"') { targetQuoted = '\"'; - targetQuoted += replaceValues.Target; + targetQuoted += this->ReplaceValues->Target; targetQuoted += '\"'; } return targetQuoted; } if (variable == "TARGET_UNQUOTED") { - std::string unquoted = replaceValues.Target; + std::string unquoted = this->ReplaceValues->Target; std::string::size_type sz = unquoted.size(); if (sz > 2 && unquoted.front() == '\"' && unquoted.back() == '\"') { unquoted = unquoted.substr(1, sz - 2); } return unquoted; } - if (replaceValues.LanguageCompileFlags) { + if (this->ReplaceValues->LanguageCompileFlags) { if (variable == "LANGUAGE_COMPILE_FLAGS") { - return replaceValues.LanguageCompileFlags; + return this->ReplaceValues->LanguageCompileFlags; } } - if (replaceValues.Target) { + if (this->ReplaceValues->Target) { if (variable == "TARGET") { - return replaceValues.Target; + return this->ReplaceValues->Target; } } if (variable == "TARGET_IMPLIB") { return this->TargetImpLib; } if (variable == "TARGET_VERSION_MAJOR") { - if (replaceValues.TargetVersionMajor) { - return replaceValues.TargetVersionMajor; + if (this->ReplaceValues->TargetVersionMajor) { + return this->ReplaceValues->TargetVersionMajor; } return "0"; } if (variable == "TARGET_VERSION_MINOR") { - if (replaceValues.TargetVersionMinor) { - return replaceValues.TargetVersionMinor; + if (this->ReplaceValues->TargetVersionMinor) { + return this->ReplaceValues->TargetVersionMinor; } return "0"; } - if (replaceValues.Target) { + if (this->ReplaceValues->Target) { if (variable == "TARGET_BASE") { // Strip the last extension off the target name. - std::string targetBase = replaceValues.Target; + std::string targetBase = this->ReplaceValues->Target; std::string::size_type pos = targetBase.rfind('.'); if (pos != std::string::npos) { return targetBase.substr(0, pos); @@ -220,54 +218,54 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" || variable == "TARGET_INSTALLNAME_DIR") { // All these variables depend on TargetSOName - if (replaceValues.TargetSOName) { + if (this->ReplaceValues->TargetSOName) { if (variable == "TARGET_SONAME") { - return replaceValues.TargetSOName; + return this->ReplaceValues->TargetSOName; } - if (variable == "SONAME_FLAG" && replaceValues.SONameFlag) { - return replaceValues.SONameFlag; + if (variable == "SONAME_FLAG" && this->ReplaceValues->SONameFlag) { + return this->ReplaceValues->SONameFlag; } - if (replaceValues.TargetInstallNameDir && + if (this->ReplaceValues->TargetInstallNameDir && variable == "TARGET_INSTALLNAME_DIR") { - return replaceValues.TargetInstallNameDir; + return this->ReplaceValues->TargetInstallNameDir; } } return ""; } - if (replaceValues.LinkLibraries) { + if (this->ReplaceValues->LinkLibraries) { if (variable == "LINK_LIBRARIES") { - return replaceValues.LinkLibraries; + return this->ReplaceValues->LinkLibraries; } } - if (replaceValues.Language) { + if (this->ReplaceValues->Language) { if (variable == "LANGUAGE") { - return replaceValues.Language; + return this->ReplaceValues->Language; } } - if (replaceValues.CMTargetName) { + if (this->ReplaceValues->CMTargetName) { if (variable == "TARGET_NAME") { - return replaceValues.CMTargetName; + return this->ReplaceValues->CMTargetName; } } - if (replaceValues.CMTargetType) { + if (this->ReplaceValues->CMTargetType) { if (variable == "TARGET_TYPE") { - return replaceValues.CMTargetType; + return this->ReplaceValues->CMTargetType; } } - if (replaceValues.Output) { + if (this->ReplaceValues->Output) { if (variable == "OUTPUT") { - return replaceValues.Output; + return this->ReplaceValues->Output; } } if (variable == "CMAKE_COMMAND") { - return outputConverter->ConvertToOutputFormat( + return this->OutputConverter->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); } auto compIt = this->Compilers.find(variable); if (compIt != this->Compilers.end()) { - std::string ret = outputConverter->ConvertToOutputForExisting( + std::string ret = this->OutputConverter->ConvertToOutputForExisting( this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER"]); std::string const& compilerArg1 = this->VariableMappings["CMAKE_" + compIt->second + "_COMPILER_ARG1"]; @@ -286,11 +284,12 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( this->VariableMappings["CMAKE_" + compIt->second + "_COMPILE_OPTIONS_SYSROOT"]; - if (compIt->second == replaceValues.Language && replaceValues.Launcher) { + if (compIt->second == this->ReplaceValues->Language && + this->ReplaceValues->Launcher) { // Add launcher as part of expansion so that it always appears // immediately before the command itself, regardless of whether the // overall rule template contains other content at the front. - ret = cmStrCat(replaceValues.Launcher, " ", ret); + ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret); } // if there are required arguments to the compiler add it @@ -308,13 +307,14 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( !compilerOptionExternalToolchain.empty()) { ret += " "; ret += compilerOptionExternalToolchain; - ret += outputConverter->EscapeForShell(compilerExternalToolchain, true); + ret += + this->OutputConverter->EscapeForShell(compilerExternalToolchain, true); } std::string sysroot; // Some platforms may use separate sysroots for compiling and linking. // If we detect link flags, then we pass the link sysroot instead. // FIXME: Use a more robust way to detect link line expansion. - if (replaceValues.LinkFlags) { + if (this->ReplaceValues->LinkFlags) { sysroot = this->LinkerSysroot; } else { sysroot = this->CompilerSysroot; @@ -322,7 +322,7 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( if (!sysroot.empty() && !compilerOptionSysroot.empty()) { ret += " "; ret += compilerOptionSysroot; - ret += outputConverter->EscapeForShell(sysroot, true); + ret += this->OutputConverter->EscapeForShell(sysroot, true); } return ret; } @@ -331,13 +331,13 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( if (mapIt != this->VariableMappings.end()) { if (variable.find("_FLAG") == std::string::npos) { std::string ret = - outputConverter->ConvertToOutputForExisting(mapIt->second); + this->OutputConverter->ConvertToOutputForExisting(mapIt->second); - if (replaceValues.Launcher && variable == "CMAKE_LINKER") { + if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") { // Add launcher as part of expansion so that it always appears // immediately before the command itself, regardless of whether the // overall rule template contains other content at the front. - ret = cmStrCat(replaceValues.Launcher, " ", ret); + ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret); } return ret; @@ -351,47 +351,8 @@ void cmRulePlaceholderExpander::ExpandRuleVariables( cmOutputConverter* outputConverter, std::string& s, const RuleVariables& replaceValues) { - std::string::size_type start = s.find('<'); - // no variables to expand - if (start == std::string::npos) { - return; - } - std::string::size_type pos = 0; - std::string expandedInput; - while (start != std::string::npos && start < s.size() - 2) { - std::string::size_type end = s.find('>', start); - // if we find a < with no > we are done - if (end == std::string::npos) { - return; - } - char c = s[start + 1]; - // if the next char after the < is not A-Za-z then - // skip it and try to find the next < in the string - if (!isalpha(c)) { - start = s.find('<', start + 1); - } else { - // extract the var - std::string var = s.substr(start + 1, end - start - 1); - std::string replace = - this->ExpandRuleVariable(outputConverter, var, replaceValues); - expandedInput += s.substr(pos, start - pos); - - // Prevent consecutive whitespace in the output if the rule variable - // expands to an empty string. - bool consecutive = replace.empty() && start > 0 && s[start - 1] == ' ' && - end + 1 < s.size() && s[end + 1] == ' '; - if (consecutive) { - expandedInput.pop_back(); - } + this->OutputConverter = outputConverter; + this->ReplaceValues = &replaceValues; - expandedInput += replace; - - // move to next one - start = s.find('<', start + var.size() + 2); - pos = end + 1; - } - } - // add the rest of the input - expandedInput += s.substr(pos, s.size() - pos); - s = expandedInput; + this->ExpandVariables(s); } diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 852954f..23ec405 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h @@ -8,9 +8,11 @@ #include <map> #include <string> +#include "cmPlaceholderExpander.h" + class cmOutputConverter; -class cmRulePlaceholderExpander +class cmRulePlaceholderExpander : public cmPlaceholderExpander { public: cmRulePlaceholderExpander( @@ -76,16 +78,16 @@ public: std::string& string, const RuleVariables& replaceValues); - // Expand rule variables in a single string - std::string ExpandRuleVariable(cmOutputConverter* outputConverter, - std::string const& variable, - const RuleVariables& replaceValues); - private: + std::string ExpandVariable(std::string const& variable) override; + std::string TargetImpLib; std::map<std::string, std::string> Compilers; std::map<std::string, std::string> VariableMappings; std::string CompilerSysroot; std::string LinkerSysroot; + + cmOutputConverter* OutputConverter = nullptr; + RuleVariables const* ReplaceValues = nullptr; }; diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index effb837..a5dfa4c 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -3314,12 +3314,22 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName, uv_fs_t req; int flags = 0; #if defined(_WIN32) - if (cmsys::SystemTools::FileIsDirectory(origName)) { - flags |= UV_FS_SYMLINK_DIR; + bool const isDir = cmsys::SystemTools::FileIsDirectory(origName); + if (isDir) { + flags |= UV_FS_SYMLINK_JUNCTION; } #endif int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(), flags, nullptr); +#if defined(_WIN32) + if (err && uv_fs_get_system_error(&req) == ERROR_NOT_SUPPORTED && isDir) { + // Try fallback to symlink for network (requires additional permissions). + flags ^= UV_FS_SYMLINK_JUNCTION | UV_FS_SYMLINK_DIR; + err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(), + flags, nullptr); + } +#endif + cmsys::Status status; if (err) { #if defined(_WIN32) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 6059055..4ca1b9b 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1783,69 +1783,94 @@ void cmTarget::InsertPrecompileHeader(BT<std::string> const& entry) this->impl->PrecompileHeadersEntries.push_back(entry); } -static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop, - const std::string& value, - cmMakefile* context, - bool imported) +namespace { +void CheckLinkLibraryPattern(const std::string& property, + const std::string& value, cmMakefile* context) { - // Look for link-type keywords in the value. - static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)"); - if (!keys.find(value)) { + // Look for <LINK_LIBRARY:> and </LINK_LIBRARY:> internal tags + static cmsys::RegularExpression linkPattern( + "(^|;)(</?LINK_(LIBRARY|GROUP):[^;>]*>)(;|$)"); + if (!linkPattern.find(value)) { return; } + // Report an error. + context->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Property ", property, " contains the invalid item \"", + linkPattern.match(2), "\". The ", property, + " property may contain the generator-expression \"$<LINK_", + linkPattern.match(3), + ":...>\" which may be used to specify how the libraries are linked.")); +} + +void CheckLINK_INTERFACE_LIBRARIES(const std::string& prop, + const std::string& value, + cmMakefile* context, bool imported) +{ // Support imported and non-imported versions of the property. const char* base = (imported ? "IMPORTED_LINK_INTERFACE_LIBRARIES" : "LINK_INTERFACE_LIBRARIES"); - // Report an error. - std::ostringstream e; - e << "Property " << prop << " may not contain link-type keyword \"" - << keys.match(2) << "\". " - << "The " << base << " property has a per-configuration " - << "version called " << base << "_<CONFIG> which may be " - << "used to specify per-configuration rules."; - if (!imported) { - e << " " - << "Alternatively, an IMPORTED library may be created, configured " - << "with a per-configuration location, and then named in the " - << "property value. " - << "See the add_library command's IMPORTED mode for details." - << "\n" - << "If you have a list of libraries that already contains the " - << "keyword, use the target_link_libraries command with its " - << "LINK_INTERFACE_LIBRARIES mode to set the property. " - << "The command automatically recognizes link-type keywords and sets " - << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG " - << "properties accordingly."; - } - context->IssueMessage(MessageType::FATAL_ERROR, e.str()); -} - -static void cmTargetCheckINTERFACE_LINK_LIBRARIES(const std::string& value, - cmMakefile* context) -{ // Look for link-type keywords in the value. static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)"); - if (!keys.find(value)) { - return; + if (keys.find(value)) { + // Report an error. + std::ostringstream e; + e << "Property " << prop << " may not contain link-type keyword \"" + << keys.match(2) << "\". " + << "The " << base << " property has a per-configuration " + << "version called " << base << "_<CONFIG> which may be " + << "used to specify per-configuration rules."; + if (!imported) { + e << " " + << "Alternatively, an IMPORTED library may be created, configured " + << "with a per-configuration location, and then named in the " + << "property value. " + << "See the add_library command's IMPORTED mode for details." + << "\n" + << "If you have a list of libraries that already contains the " + << "keyword, use the target_link_libraries command with its " + << "LINK_INTERFACE_LIBRARIES mode to set the property. " + << "The command automatically recognizes link-type keywords and sets " + << "the LINK_INTERFACE_LIBRARIES and LINK_INTERFACE_LIBRARIES_DEBUG " + << "properties accordingly."; + } + context->IssueMessage(MessageType::FATAL_ERROR, e.str()); } - // Report an error. - std::ostringstream e; + CheckLinkLibraryPattern(base, value, context); +} + +void CheckLINK_LIBRARIES(const std::string& value, cmMakefile* context) +{ + CheckLinkLibraryPattern("LINK_LIBRARIES", value, context); +} - e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type " - "keyword \"" - << keys.match(2) - << "\". The INTERFACE_LINK_LIBRARIES " - "property may contain configuration-sensitive generator-expressions " - "which may be used to specify per-configuration rules."; +void CheckINTERFACE_LINK_LIBRARIES(const std::string& value, + cmMakefile* context) +{ + // Look for link-type keywords in the value. + static cmsys::RegularExpression keys("(^|;)(debug|optimized|general)(;|$)"); + if (keys.find(value)) { + // Report an error. + std::ostringstream e; + + e << "Property INTERFACE_LINK_LIBRARIES may not contain link-type " + "keyword \"" + << keys.match(2) + << "\". The INTERFACE_LINK_LIBRARIES " + "property may contain configuration-sensitive generator-expressions " + "which may be used to specify per-configuration rules."; + + context->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } - context->IssueMessage(MessageType::FATAL_ERROR, e.str()); + CheckLinkLibraryPattern("INTERFACE_LINK_LIBRARIES", value, context); } -static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target, - cmMakefile* context) +void CheckIMPORTED_GLOBAL(const cmTarget* target, cmMakefile* context) { const auto& targets = context->GetOwnedImportedTargets(); auto it = @@ -1861,6 +1886,7 @@ static void cmTargetCheckIMPORTED_GLOBAL(const cmTarget* target, context->IssueMessage(MessageType::FATAL_ERROR, e.str()); } } +} void cmTarget::CheckProperty(const std::string& prop, cmMakefile* context) const @@ -1868,22 +1894,23 @@ void cmTarget::CheckProperty(const std::string& prop, // Certain properties need checking. if (cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES")) { if (cmValue value = this->GetProperty(prop)) { - cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false); + CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false); } - } - if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) { + } else if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) { if (cmValue value = this->GetProperty(prop)) { - cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true); + CheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true); } - } - if (prop == "INTERFACE_LINK_LIBRARIES") { + } else if (prop == "LINK_LIBRARIES") { if (cmValue value = this->GetProperty(prop)) { - cmTargetCheckINTERFACE_LINK_LIBRARIES(*value, context); + CheckLINK_LIBRARIES(*value, context); } - } - if (prop == "IMPORTED_GLOBAL") { + } else if (prop == "INTERFACE_LINK_LIBRARIES") { + if (cmValue value = this->GetProperty(prop)) { + CheckINTERFACE_LINK_LIBRARIES(*value, context); + } + } else if (prop == "IMPORTED_GLOBAL") { if (this->IsImported()) { - cmTargetCheckIMPORTED_GLOBAL(this, context); + CheckIMPORTED_GLOBAL(this, context); } } } diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index 94fcdd9..0ba93fb 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -354,6 +354,9 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args, target->SetProperty("LINK_INTERFACE_LIBRARIES", ""); } + target->CheckProperty("LINK_LIBRARIES", &mf); + target->CheckProperty("INTERFACE_LINK_LIBRARIES", &mf); + return true; } diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index 818e271..b425acb 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -16,6 +16,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -202,6 +203,11 @@ bool TargetSourcesImpl::HandleFileSetMode( return false; } + if (this->Target->GetType() == cmStateEnums::UTILITY) { + this->SetError("FILE_SETs may not be added to custom targets"); + return false; + } + bool const isDefault = args.Type == args.FileSet || (args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z'); std::string type = isDefault ? args.FileSet : args.Type; diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx index fd5402c..24394d9 100644 --- a/Source/cmVariableWatchCommand.cxx +++ b/Source/cmVariableWatchCommand.cxx @@ -58,7 +58,7 @@ void cmVariableWatchCommandVariableAccessed(const std::string& variable, { stack, cmListFileArgument::Quoted, fakeLineNo } }; - cmListFileFunction newLFF{ data->Command, fakeLineNo, + cmListFileFunction newLFF{ data->Command, fakeLineNo, fakeLineNo, std::move(newLFFArgs) }; cmExecutionStatus status(*makefile); if (!makefile->ExecuteCommand(newLFF, status)) { diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 8ffb664..276eccf 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -50,6 +50,24 @@ #include "cmValue.h" #include "cmVisualStudioGeneratorOptions.h" +namespace { +std::string getProjectFileExtension(VsProjectType projectType) +{ + switch (projectType) { + case VsProjectType::csproj: + return ".csproj"; + case VsProjectType::proj: + return ".proj"; + case VsProjectType::vcxproj: + return ".vcxproj"; + // Valid inputs shouldn't reach here. This default is needed so that all + // paths return value (C4715). + default: + return ""; + } +} +} + struct cmIDEFlagTable; static void ConvertToWindowsSlash(std::string& s); @@ -235,29 +253,6 @@ static bool cmVS10IsTargetsFile(std::string const& path) return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0; } -static VsProjectType computeProjectType(cmGeneratorTarget const* t) -{ - if (t->IsCSharpOnly()) { - return VsProjectType::csproj; - } - return VsProjectType::vcxproj; -} - -static std::string computeProjectFileExtension(VsProjectType projectType) -{ - switch (projectType) { - case VsProjectType::csproj: - return ".csproj"; - default: - return ".vcxproj"; - } -} - -static std::string computeProjectFileExtension(cmGeneratorTarget const* t) -{ - return computeProjectFileExtension(computeProjectType(t)); -} - cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg) : GeneratorTarget(target) @@ -352,10 +347,10 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString( void cmVisualStudio10TargetGenerator::Generate() { - this->ProjectType = computeProjectType(this->GeneratorTarget); + this->ProjectType = this->ComputeProjectType(this->GeneratorTarget); this->Managed = this->ProjectType == VsProjectType::csproj; const std::string ProjectFileExtension = - computeProjectFileExtension(this->ProjectType); + getProjectFileExtension(this->ProjectType); if (this->ProjectType == VsProjectType::csproj && this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { @@ -416,10 +411,12 @@ void cmVisualStudio10TargetGenerator::Generate() char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; BuildFileStream.write(magic, 3); - if (this->ProjectType == VsProjectType::csproj && - this->GeneratorTarget->IsDotNetSdkTarget() && - this->GlobalGenerator->GetVersion() >= - cmGlobalVisualStudioGenerator::VSVersion::VS16) { + if (this->ProjectType == VsProjectType::proj) { + this->WriteZeroCheckProj(BuildFileStream); + } else if (this->ProjectType == VsProjectType::csproj && + this->GeneratorTarget->IsDotNetSdkTarget() && + this->GlobalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VSVersion::VS16) { this->WriteSdkStyleProjectFile(BuildFileStream); } else { this->WriteClassicMsBuildProjectFile(BuildFileStream); @@ -681,6 +678,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( .Attribute("Project", VS10_CSharp_DEFAULT_PROPS) .Attribute("Condition", "Exists('" VS10_CSharp_DEFAULT_PROPS "')"); break; + default: + break; } this->WriteProjectConfigurationValues(e0); @@ -737,6 +736,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( case VsProjectType::csproj: props = VS10_CSharp_USER_PROPS; break; + default: + break; } if (cmValue p = this->GeneratorTarget->GetProperty("VS_USER_PROPS")) { props = *p; @@ -779,6 +780,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( Elem(e0, "Import").Attribute("Project", VS10_CSharp_TARGETS); } break; + default: + break; } this->WriteTargetSpecificReferences(e0); @@ -944,6 +947,45 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( this->WriteProjectReferences(e0); } +void cmVisualStudio10TargetGenerator::WriteZeroCheckProj( + cmGeneratedFileStream& BuildFileStream) +{ + // ZERO_CHECK.proj is an XML file without any imports or targets. This is a + // ProjectReference for other targets and therefore, it needs to follow the + // ProjectReference protocol as documented here: + // https://github.com/dotnet/msbuild/blob/main/documentation/ProjectReference-Protocol.md + // + // We implement MSBuild target Build from WriteCustomCommand which calls + // WriteZeroCheckBuildTarget after setting up the command generator. MSBuild + // target Clean is a no-op as we do all the work for ZERO_CHECK on Build. + // MSBuild target GetTargetPath is needed and is no-op. + // MSBuild targets GetNativeManifest and GetCopyToOutputDirectoryItems are + // needed for MSBuild versions below 15.7 and are no-op. MSBuild target + // BeforeBuild is needed for supporting GLOBs. + BuildFileStream << "<?xml version=\"1.0\" encoding=\"" + << this->GlobalGenerator->Encoding() << "\"?>"; + { + Elem e0(BuildFileStream, "Project"); + e0.Attribute("DefaultTargets", "Build"); + e0.Attribute("ToolsVersion", this->GlobalGenerator->GetToolsVersion()); + e0.Attribute("xmlns", + "http://schemas.microsoft.com/developer/msbuild/2003"); + + this->WriteCustomCommands(e0); + + for (const char* targetName : + { "Clean", "GetTargetPath", "GetNativeManifest", + "GetCopyToOutputDirectoryItems" }) { + { + Elem e1(e0, "Target"); + e1.Attribute("Name", targetName); + } + } + + this->WriteZeroCheckBeforeBuildTarget(e0); + } +} + void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1) { e1.Attribute("Label", "Globals"); @@ -1659,11 +1701,16 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( } } } + if (this->ProjectType == VsProjectType::proj) { + this->WriteZeroCheckBuildTarget(e0, command, source); + return; + } + cmLocalVisualStudio7Generator* lg = this->LocalGenerator; std::unique_ptr<Elem> spe1; std::unique_ptr<Elem> spe2; - if (this->ProjectType != VsProjectType::csproj) { + if (this->ProjectType == VsProjectType::vcxproj) { spe1 = cm::make_unique<Elem>(e0, "ItemGroup"); spe2 = cm::make_unique<Elem>(*spe1, "CustomBuild"); this->WriteSource(*spe2, source); @@ -1861,7 +1908,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() // Write out group file std::string path = cmStrCat( this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->Name, - computeProjectFileExtension(this->GeneratorTarget), ".filters"); + this->ComputeProjectFileExtension(this->GeneratorTarget), ".filters"); cmGeneratedFileStream fout(path); fout.SetCopyIfDifferent(true); char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; @@ -3016,6 +3063,134 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental( } } +void cmVisualStudio10TargetGenerator::WriteZeroCheckBuildTarget( + cmVisualStudio10TargetGenerator::Elem& e0, const cmCustomCommand& command, + const cmSourceFile* source) +{ + cmLocalVisualStudio7Generator* lg = this->LocalGenerator; + + Elem e1(e0, "Target"); + e1.Attribute("Name", "Build"); + + std::string noConfig{}; + cmCustomCommandGenerator ccg{ command, noConfig, lg, true }; + std::string comment = lg->ConstructComment(ccg); + comment = cmVS10EscapeComment(comment); + std::string script = lg->ConstructScript(ccg); + bool symbolic = false; + // input files for custom command + std::stringstream additional_inputs; + { + const char* sep = ""; + if (this->ProjectType == VsProjectType::proj) { + // List explicitly the path to primary input. + std::string sourceFullPath = source->GetFullPath(); + ConvertToWindowsSlash(sourceFullPath); + additional_inputs << sourceFullPath; + sep = ";"; + } + + // Avoid listing an input more than once. + std::set<std::string> unique_inputs; + // The source is either implicitly an input or has been added above. + unique_inputs.insert(source->GetFullPath()); + + for (std::string const& d : ccg.GetDepends()) { + std::string dep; + if (lg->GetRealDependency(d, noConfig, dep)) { + if (!unique_inputs.insert(dep).second) { + // already listed + continue; + } + ConvertToWindowsSlash(dep); + additional_inputs << sep << dep; + sep = ";"; + if (!symbolic) { + if (cmSourceFile* sf = this->Makefile->GetSource( + dep, cmSourceFileLocationKind::Known)) { + symbolic = sf->GetPropertyAsBool("SYMBOLIC"); + } + } + } + } + } + // output files for custom command + std::stringstream outputs; + { + const char* sep = ""; + for (std::string const& o : ccg.GetOutputs()) { + std::string out = o; + ConvertToWindowsSlash(out); + outputs << sep << out; + sep = ";"; + if (!symbolic) { + if (cmSourceFile* sf = + this->Makefile->GetSource(o, cmSourceFileLocationKind::Known)) { + symbolic = sf->GetPropertyAsBool("SYMBOLIC"); + } + } + } + } + script += lg->FinishConstructScript(this->ProjectType); + + e1.Attribute("Inputs", cmVS10EscapeAttr(additional_inputs.str())); + e1.Attribute("Outputs", cmVS10EscapeAttr(outputs.str())); + + e1.SetHasElements(); + + if (!comment.empty()) { + Elem(e1, "Message").Attribute("Text", comment); + } + Elem(e1, "Exec").Attribute("Command", script); +} + +void cmVisualStudio10TargetGenerator::WriteZeroCheckBeforeBuildTarget( + cmVisualStudio10TargetGenerator::Elem& e0) +{ + const auto& commands = this->GeneratorTarget->GetPreBuildCommands(); + if (commands.empty()) { + return; + } + + { + Elem e1(e0, "Target"); + e1.Attribute("Name", "BeforeBuild"); + e1.Attribute("BeforeTargets", "Build"); + + cmLocalVisualStudio7Generator* lg = this->LocalGenerator; + std::string script; + const char* pre = ""; + std::string comment; + for (cmCustomCommand const& cc : commands) { + cmCustomCommandGenerator ccg(cc, std::string{}, lg); + if (!ccg.HasOnlyEmptyCommandLines()) { + comment += pre; + comment += lg->ConstructComment(ccg); + script += pre; + pre = "\n"; + script += lg->ConstructScript(ccg); + } + } + + if (script.empty()) { + return; + } + + script += lg->FinishConstructScript(this->ProjectType); + comment = cmVS10EscapeComment(comment); + std::string strippedComment = comment; + strippedComment.erase( + std::remove(strippedComment.begin(), strippedComment.end(), '\t'), + strippedComment.end()); + + e1.SetHasElements(); + if (!comment.empty() && !strippedComment.empty()) { + Elem(e1, "Message").Attribute("Text", comment); + } + Elem(e1, "Exec").Attribute("Command", script); + } +} + std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes( std::string const& config, std::string const& lang) const { @@ -3057,6 +3232,8 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( cm::make_unique<Options>(this->LocalGenerator, Options::CSharpCompiler, gg->GetCSharpFlagTable()); break; + default: + break; } Options& clOptions = *pOptions; @@ -3182,6 +3359,8 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( return def.find('=') != std::string::npos; }); break; + default: + break; } clOptions.AddDefines(targetDefines); @@ -3309,7 +3488,9 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( } else if (this->MSTools) { cmsys::RegularExpression clangToolset("v[0-9]+_clang_.*"); const char* toolset = this->GlobalGenerator->GetPlatformToolset(); - if (toolset && clangToolset.find(toolset)) { + cmValue noCompileBatching = + this->GeneratorTarget->GetProperty("VS_NO_COMPILE_BATCHING"); + if (noCompileBatching.IsOn() || (toolset && clangToolset.find(toolset))) { e2.Element("ObjectFileName", "$(IntDir)%(filename).obj"); } else { e2.Element("ObjectFileName", "$(IntDir)"); @@ -3688,7 +3869,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( this->LocalGenerator->MaybeRelativeToCurBinDir(l.Value.Value); ConvertToWindowsSlash(path); if (!cmVS10IsTargetsFile(l.Value.Value)) { - libVec.push_back(path); + libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value + : path); } } else { libVec.push_back(l.Value.Value); @@ -4305,6 +4487,9 @@ void cmVisualStudio10TargetGenerator::AddLibraries( this->AdditionalUsingDirectories[config].insert( cmSystemTools::GetFilenamePath(location)); break; + default: + // In .proj files, we wouldn't be referencing libraries. + break; } } } @@ -4326,7 +4511,8 @@ void cmVisualStudio10TargetGenerator::AddLibraries( if (cmVS10IsTargetsFile(l.Value.Value)) { vsTargetVec.push_back(path); } else { - libVec.push_back(path); + libVec.push_back(l.HasFeature() ? l.GetFormattedItem(path).Value + : path); } } else if (!l.Target || l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { @@ -4533,7 +4719,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) path = *p; } else { path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(), - computeProjectFileExtension(dt)); + this->ComputeProjectFileExtension(dt)); } ConvertToWindowsSlash(path); Elem e2(e1, "ProjectReference"); @@ -5387,6 +5573,26 @@ std::string cmVisualStudio10TargetGenerator::GetCMakeFilePath( return path; } +std::string cmVisualStudio10TargetGenerator::ComputeProjectFileExtension( + cmGeneratorTarget const* t) const +{ + return getProjectFileExtension(this->ComputeProjectType(t)); +} + +VsProjectType cmVisualStudio10TargetGenerator::ComputeProjectType( + cmGeneratorTarget const* t) const +{ + if (this->GlobalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VSVersion::VS16 && + t->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { + return VsProjectType::proj; + } + if (t->IsCSharpOnly()) { + return VsProjectType::csproj; + } + return VsProjectType::vcxproj; +} + void cmVisualStudio10TargetGenerator::WriteStdOutEncodingUtf8(Elem& e1) { if (this->GlobalGenerator->IsUtf8EncodingSupported()) { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 8d777a3..7a0b8da 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -264,6 +264,13 @@ private: void WriteClassicMsBuildProjectFile(cmGeneratedFileStream& BuildFileStream); void WriteSdkStyleProjectFile(cmGeneratedFileStream& BuildFileStream); + void WriteZeroCheckProj(cmGeneratedFileStream& BuildFileStream); + void WriteZeroCheckBuildTarget(cmVisualStudio10TargetGenerator::Elem& e0, + const cmCustomCommand& command, + const cmSourceFile* source); + void WriteZeroCheckBeforeBuildTarget( + cmVisualStudio10TargetGenerator::Elem& e0); + void WriteCommonPropertyGroupGlobals( cmVisualStudio10TargetGenerator::Elem& e1); @@ -275,4 +282,7 @@ private: void ParseSettingsProperty(const std::string& settingsPropertyValue, ConfigToSettings& toolSettings); std::string GetCMakeFilePath(const char* name) const; + + std::string ComputeProjectFileExtension(cmGeneratorTarget const* t) const; + VsProjectType ComputeProjectType(cmGeneratorTarget const* t) const; }; diff --git a/Source/cmVsProjectType.h b/Source/cmVsProjectType.h index 8899267..04053a0 100644 --- a/Source/cmVsProjectType.h +++ b/Source/cmVsProjectType.h @@ -7,5 +7,6 @@ enum class VsProjectType { vcxproj, - csproj + csproj, + proj, }; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index f9e2d6e..81d225d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1455,7 +1455,7 @@ void cmake::PrintTraceFormatVersion() Json::StreamWriterBuilder builder; builder["indentation"] = ""; version["major"] = 1; - version["minor"] = 1; + version["minor"] = 2; val["version"] = version; msg = Json::writeString(builder, val); #endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 32c01e5..f1c1bdc 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -97,7 +97,8 @@ void CMakeCommandUsage(std::string const& program) << "Available commands: \n" << " capabilities - Report capabilities built into cmake " "in JSON format\n" - << " cat <files>... - concat the files and print them to the standard output\n" + << " cat [--] <files>... - concat the files and print them to the " + "standard output\n" << " chdir dir cmd [args...] - run command in a given directory\n" << " compare_files [--ignore-eol] file1 file2\n" << " - check if file1 is same as file2\n" @@ -110,7 +111,7 @@ void CMakeCommandUsage(std::string const& program) << " echo [<string>...] - displays arguments as text\n" << " echo_append [<string>...] - displays arguments as text but no new " "line\n" - << " env [--unset=NAME]... [NAME=VALUE]... COMMAND [ARG]...\n" + << " env [--unset=NAME ...] [NAME=VALUE ...] [--] <command> [<arg>...]\n" << " - run command in a modified environment\n" << " environment - display the current environment\n" << " make_directory <dir>... - create parent and <dir> directories\n" @@ -125,8 +126,9 @@ void CMakeCommandUsage(std::string const& program) << " remove_directory <dir>... - remove directories and their contents (deprecated: use rm instead)\n" << " rename oldname newname - rename a file or directory " "(on one volume)\n" - << " rm [-rRf] <file/dir>... - remove files or directories, use -f to " - "force it, r or R to remove directories and their contents recursively\n" + << " rm [-rRf] [--] <file/dir>... - remove files or directories, use -f " + "to force it, r or R to remove directories and their contents " + "recursively\n" << " sleep <number>... - sleep for given number of seconds\n" << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n" << " - create or extract a tar or zip archive\n" @@ -793,6 +795,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, auto ae = args.cend(); for (; ai != ae; ++ai) { std::string const& a = *ai; + if (a == "--") { + // Stop parsing options/environment variables; the next argument + // should be the command. + ++ai; + break; + } if (cmHasLiteralPrefix(a, "--unset=")) { // Unset environment variable. cmSystemTools::UnPutEnv(a.substr(8)); @@ -1051,9 +1059,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, // Command to concat files into one if (args[1] == "cat" && args.size() >= 3) { int return_value = 0; + bool doing_options = true; for (auto const& arg : cmMakeRange(args).advance(2)) { - if (cmHasLiteralPrefix(arg, "-")) { - if (arg != "--") { + if (doing_options && cmHasLiteralPrefix(arg, "-")) { + if (arg == "--") { + doing_options = false; + } else { cmSystemTools::Error(arg + ": option not handled"); return_value = 1; } @@ -1275,8 +1286,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, // FIXME: With advanced add_subdirectory usage, these are // not necessarily the same as the generator originally used. // We should pass all these directories through an info file. - lgd->SetRelativePathTopSource(homeDir); - lgd->SetRelativePathTopBinary(homeOutDir); + lgd->SetRelativePathTop(homeDir, homeOutDir); // Actually scan dependencies. return lgd->UpdateDependencies(depInfo, verbose, color) ? 0 : 2; @@ -1558,8 +1568,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, // FIXME: With advanced add_subdirectory usage, these are // not necessarily the same as the generator originally used. // We should pass all these directories through an info file. - lgd->SetRelativePathTopSource(homeDir); - lgd->SetRelativePathTopBinary(homeOutDir); + lgd->SetRelativePathTop(homeDir, homeOutDir); return cmTransformDepfile(format, *lgd, args[8], args[9]) ? 0 : 2; } diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index 19bf982..22cbf06 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -1906,7 +1906,7 @@ static void kwsysProcessDestroy(kwsysProcess* cp) (errno == EINTR)) { } if (result > 0) { - /* This child has termianted. */ + /* This child has terminated. */ cp->ForkPIDs[i] = 0; if (--cp->CommandsLeft == 0) { /* All children have terminated. Close the signal pipe diff --git a/Source/kwsys/ProcessWin32.c b/Source/kwsys/ProcessWin32.c index e97973e..17e1507 100644 --- a/Source/kwsys/ProcessWin32.c +++ b/Source/kwsys/ProcessWin32.c @@ -2119,7 +2119,7 @@ static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int code, KWSYSPE_CASE(Fault, "In-page error"); break; case STATUS_INVALID_HANDLE: - KWSYSPE_CASE(Fault, "Invalid hanlde"); + KWSYSPE_CASE(Fault, "Invalid handle"); break; case STATUS_NONCONTINUABLE_EXCEPTION: KWSYSPE_CASE(Fault, "Noncontinuable exception"); diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index dcbf367..e6cc48f 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx @@ -3262,6 +3262,9 @@ bool SystemInformationImplementation::RetrieveClassicalCPUIdentity() case 0x3b: this->ChipID.ProcessorName = "Zhaoxin kx6000"; break; + case 0x5b: + this->ChipID.ProcessorName = "Zhaoxin kh40000"; + break; default: this->ChipID.ProcessorName = "Unknown IDT\\Centaur\\VIA\\Zhaoxin family"; @@ -3295,6 +3298,9 @@ bool SystemInformationImplementation::RetrieveClassicalCPUIdentity() case 0x3b: this->ChipID.ProcessorName = "Zhaoxin kx6000"; break; + case 0x5b: + this->ChipID.ProcessorName = "Zhaoxin kh40000"; + break; default: this->ChipID.ProcessorName = "Unknown Zhaoxin family"; return false; |