diff options
Diffstat (limited to 'Source')
44 files changed, 1894 insertions, 1605 deletions
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 0862ef7a41..5190ab1d51 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 24) -set(CMake_VERSION_PATCH 20220713) +set(CMake_VERSION_PATCH 20220803) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index af365ad729..be952cd26d 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -82,14 +82,13 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // Process input arguments. std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> parsedKeywords; - this->Parse(args, &unparsedArguments, /*keywordsMissingValue=*/nullptr, - &parsedKeywords); + this->Parse(args, &unparsedArguments); this->CheckArguments(); - std::sort(parsedKeywords.begin(), parsedKeywords.end()); - auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end()); - if (it != parsedKeywords.end()) { + std::sort(this->ParsedKeywords.begin(), this->ParsedKeywords.end()); + auto it = std::adjacent_find(this->ParsedKeywords.begin(), + this->ParsedKeywords.end()); + if (it != this->ParsedKeywords.end()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Called with more than one value for ", *it)); @@ -233,6 +232,7 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*) void cmCTestHandlerCommand::BindArguments() { + this->BindParsedKeywords(this->ParsedKeywords); this->Bind("APPEND"_s, this->Append); this->Bind("QUIET"_s, this->Quiet); this->Bind("RETURN_VALUE"_s, this->ReturnValue); diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index 31942c8feb..ed6d9af581 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmArgumentParser.h" #include "cmCTestCommand.h" @@ -44,6 +46,7 @@ protected: virtual void BindArguments(); virtual void CheckArguments(); + std::vector<cm::string_view> ParsedKeywords; bool Append = false; bool Quiet = false; std::string CaptureCMakeError; diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index f0c4cfcbe0..614d00d680 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -5,10 +5,13 @@ #include <algorithm> #include "cmArgumentParserTypes.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" namespace ArgumentParser { -auto ActionMap::Emplace(cm::string_view name, Action action) +auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action) -> std::pair<iterator, bool> { auto const it = @@ -21,7 +24,7 @@ auto ActionMap::Emplace(cm::string_view name, Action action) : std::make_pair(this->emplace(it, name, std::move(action)), true); } -auto ActionMap::Find(cm::string_view name) const -> const_iterator +auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator { auto const it = std::lower_bound(this->begin(), this->end(), name, @@ -31,82 +34,168 @@ auto ActionMap::Find(cm::string_view name) const -> const_iterator return (it != this->end() && it->first == name) ? it : this->end(); } +auto PositionActionMap::Emplace(std::size_t pos, PositionAction action) + -> std::pair<iterator, bool> +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) + ? std::make_pair(it, false) + : std::make_pair(this->emplace(it, pos, std::move(action)), true); +} + +auto PositionActionMap::Find(std::size_t pos) const -> const_iterator +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) ? it : this->end(); +} + +void Instance::Bind(std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect) +{ + this->KeywordValueFunc = std::move(f); + this->KeywordValuesExpected = expect.Count; +} + void Instance::Bind(bool& val) { val = true; - this->CurrentString = nullptr; - this->CurrentList = nullptr; - this->ExpectValue = false; + this->Bind(nullptr, ExpectAtLeast{ 0 }); } void Instance::Bind(std::string& val) { - this->CurrentString = &val; - this->CurrentList = nullptr; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 1 }); +} + +void Instance::Bind(NonEmpty<std::string>& val) +{ + this->Bind( + [this, &val](cm::string_view arg) -> Continue { + if (arg.empty() && this->ParseResults) { + this->ParseResults->AddKeywordError(this->Keyword, + " empty string not allowed\n"); + } + val.assign(std::string(arg)); + return Continue::No; + }, + ExpectAtLeast{ 1 }); } void Instance::Bind(Maybe<std::string>& val) { - this->CurrentString = &val; - this->CurrentList = nullptr; - this->ExpectValue = false; + this->Bind( + [&val](cm::string_view arg) -> Continue { + static_cast<std::string&>(val) = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 0 }); } void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val) { - this->CurrentString = nullptr; - this->CurrentList = &val; - this->ExpectValue = false; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); } void Instance::Bind(NonEmpty<std::vector<std::string>>& val) { - this->CurrentString = nullptr; - this->CurrentList = &val; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 1 }); } -void Instance::Bind(std::vector<std::vector<std::string>>& val) +void Instance::Bind(std::vector<std::vector<std::string>>& multiVal) { - this->CurrentString = nullptr; - this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back()); - this->ExpectValue = false; + multiVal.emplace_back(); + std::vector<std::string>& val = multiVal.back(); + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); } -void Instance::Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue, - std::vector<cm::string_view>* parsedKeywords) +void Instance::Consume(std::size_t pos, cm::string_view arg) { - auto const it = this->Bindings.Find(arg); - if (it != this->Bindings.end()) { - if (parsedKeywords != nullptr) { - parsedKeywords->emplace_back(it->first); + auto const it = this->Bindings.Keywords.Find(arg); + if (it != this->Bindings.Keywords.end()) { + this->FinishKeyword(); + this->Keyword = it->first; + this->KeywordValuesSeen = 0; + if (this->Bindings.ParsedKeyword) { + this->Bindings.ParsedKeyword(*this, it->first); } - it->second(*this, result); - if (this->ExpectValue && keywordsMissingValue != nullptr) { - keywordsMissingValue->emplace_back(it->first); + it->second(*this); + return; + } + + if (this->KeywordValueFunc) { + switch (this->KeywordValueFunc(arg)) { + case Continue::Yes: + break; + case Continue::No: + this->KeywordValueFunc = nullptr; + break; } + ++this->KeywordValuesSeen; + return; + } + + auto const pit = this->Bindings.Positions.Find(pos); + if (pit != this->Bindings.Positions.end()) { + pit->second(*this, pos, arg); return; } - if (this->CurrentString != nullptr) { - this->CurrentString->assign(std::string(arg)); - this->CurrentString = nullptr; - this->CurrentList = nullptr; - } else if (this->CurrentList != nullptr) { - this->CurrentList->emplace_back(arg); - } else if (unparsedArguments != nullptr) { - unparsedArguments->emplace_back(arg); + if (this->UnparsedArguments != nullptr) { + this->UnparsedArguments->emplace_back(arg); } +} - if (this->ExpectValue) { - if (keywordsMissingValue != nullptr) { - keywordsMissingValue->pop_back(); +void Instance::FinishKeyword() +{ + if (this->Keyword.empty()) { + return; + } + if (this->KeywordValuesSeen < this->KeywordValuesExpected) { + if (this->ParseResults != nullptr) { + this->ParseResults->AddKeywordError(this->Keyword, + " missing required value\n"); + } + if (this->Bindings.KeywordMissingValue) { + this->Bindings.KeywordMissingValue(*this, this->Keyword); } - this->ExpectValue = false; } } +bool ParseResult::MaybeReportError(cmMakefile& mf) const +{ + if (*this) { + return false; + } + std::string e; + for (auto const& ke : this->KeywordErrors) { + e = cmStrCat(e, "Error after keyword \"", ke.first, "\":\n", ke.second); + } + mf.IssueMessage(MessageType::FATAL_ERROR, e); + return true; +} + } // namespace ArgumentParser diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index 26148d9aa2..ac788724a8 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -5,40 +5,173 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <cassert> +#include <cstddef> #include <functional> +#include <map> #include <string> #include <utility> #include <vector> #include <cm/optional> #include <cm/string_view> +#include <cm/type_traits> #include <cmext/string_view> #include "cmArgumentParserTypes.h" // IWYU pragma: keep +template <typename Result> +class cmArgumentParser; // IWYU pragma: keep + +class cmMakefile; + namespace ArgumentParser { +class ParseResult +{ + std::map<cm::string_view, std::string> KeywordErrors; + +public: + explicit operator bool() const { return this->KeywordErrors.empty(); } + + void AddKeywordError(cm::string_view key, cm::string_view text) + + { + this->KeywordErrors[key] += text; + } + + std::map<cm::string_view, std::string> const& GetKeywordErrors() const + { + return this->KeywordErrors; + } + + bool MaybeReportError(cmMakefile& mf) const; +}; + +template <typename Result> +typename std::enable_if<std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result& result) +{ + return &result; +} + +template <typename Result> +typename std::enable_if<!std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result&) +{ + return nullptr; +} + +enum class Continue +{ + No, + Yes, +}; + +struct ExpectAtLeast +{ + std::size_t Count = 0; + + ExpectAtLeast(std::size_t count) + : Count(count) + { + } +}; + class Instance; -using Action = std::function<void(Instance&, void*)>; +using KeywordAction = std::function<void(Instance&)>; +using KeywordNameAction = std::function<void(Instance&, cm::string_view)>; +using PositionAction = + std::function<void(Instance&, std::size_t, cm::string_view)>; -// using ActionMap = cm::flat_map<cm::string_view, Action>; -class ActionMap : public std::vector<std::pair<cm::string_view, Action>> +// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>; +class KeywordActionMap + : public std::vector<std::pair<cm::string_view, KeywordAction>> { public: - std::pair<iterator, bool> Emplace(cm::string_view name, Action action); + std::pair<iterator, bool> Emplace(cm::string_view name, + KeywordAction action); const_iterator Find(cm::string_view name) const; }; +// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>; +class PositionActionMap + : public std::vector<std::pair<std::size_t, PositionAction>> +{ +public: + std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action); + const_iterator Find(std::size_t pos) const; +}; + +class ActionMap +{ +public: + KeywordActionMap Keywords; + KeywordNameAction KeywordMissingValue; + KeywordNameAction ParsedKeyword; + PositionActionMap Positions; +}; + +class Base +{ +public: + using ExpectAtLeast = ArgumentParser::ExpectAtLeast; + using Continue = ArgumentParser::Continue; + using Instance = ArgumentParser::Instance; + using ParseResult = ArgumentParser::ParseResult; + + ArgumentParser::ActionMap Bindings; + + bool MaybeBind(cm::string_view name, KeywordAction action) + { + return this->Bindings.Keywords.Emplace(name, std::move(action)).second; + } + + void Bind(cm::string_view name, KeywordAction action) + { + bool const inserted = this->MaybeBind(name, std::move(action)); + assert(inserted); + static_cast<void>(inserted); + } + + void BindParsedKeyword(KeywordNameAction action) + { + assert(!this->Bindings.ParsedKeyword); + this->Bindings.ParsedKeyword = std::move(action); + } + + void BindKeywordMissingValue(KeywordNameAction action) + { + assert(!this->Bindings.KeywordMissingValue); + this->Bindings.KeywordMissingValue = std::move(action); + } + + void Bind(std::size_t pos, PositionAction action) + { + bool const inserted = + this->Bindings.Positions.Emplace(pos, std::move(action)).second; + assert(inserted); + static_cast<void>(inserted); + } +}; + class Instance { public: - Instance(ActionMap const& bindings) + Instance(ActionMap const& bindings, ParseResult* parseResult, + std::vector<std::string>* unparsedArguments, void* result = nullptr) : Bindings(bindings) + , ParseResults(parseResult) + , UnparsedArguments(unparsedArguments) + , Result(result) { } + void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect); void Bind(bool& val); void Bind(std::string& val); + void Bind(NonEmpty<std::string>& val); void Bind(Maybe<std::string>& val); void Bind(MaybeEmpty<std::vector<std::string>>& val); void Bind(NonEmpty<std::vector<std::string>>& val); @@ -54,22 +187,37 @@ public: this->Bind(*optVal); } - void Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue, - std::vector<cm::string_view>* parsedKeywords); + template <typename Range> + void Parse(Range const& args, std::size_t pos = 0) + { + for (cm::string_view arg : args) { + this->Consume(pos++, arg); + } + this->FinishKeyword(); + } private: ActionMap const& Bindings; - std::string* CurrentString = nullptr; - std::vector<std::string>* CurrentList = nullptr; - bool ExpectValue = false; + ParseResult* ParseResults = nullptr; + std::vector<std::string>* UnparsedArguments = nullptr; + void* Result = nullptr; + + cm::string_view Keyword; + std::size_t KeywordValuesSeen = 0; + std::size_t KeywordValuesExpected = 0; + std::function<Continue(cm::string_view)> KeywordValueFunc; + + void Consume(std::size_t pos, cm::string_view arg); + void FinishKeyword(); + + template <typename Result> + friend class ::cmArgumentParser; }; } // namespace ArgumentParser template <typename Result> -class cmArgumentParser +class cmArgumentParser : private ArgumentParser::Base { public: // I *think* this function could be made `constexpr` when the code is @@ -77,81 +225,194 @@ public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) { - bool const inserted = - this->Bindings - .Emplace(name, - [member](ArgumentParser::Instance& instance, void* result) { - instance.Bind(static_cast<Result*>(result)->*member); - }) - .second; - assert(inserted), (void)inserted; + this->Base::Bind(name, [member](Instance& instance) { + instance.Bind(static_cast<Result*>(instance.Result)->*member); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, member](cm::string_view arg) -> Continue { + return (result->*member)(arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view, + cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, member, keyword](cm::string_view arg) -> Continue { + return (result->*member)(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(Result&, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, &f](cm::string_view arg) -> Continue { + return f(*result, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(Result&, cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, keyword, &f](cm::string_view arg) -> Continue { + return f(*result, keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, + cm::optional<std::string> Result::*member) + { + this->Base::Bind( + position, + [member](Instance& instance, std::size_t, cm::string_view arg) { + Result* result = static_cast<Result*>(instance.Result); + result->*member = arg; + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords( + std::vector<cm::string_view> Result::*member) + { + this->Base::BindParsedKeyword( + [member](Instance& instance, cm::string_view arg) { + (static_cast<Result*>(instance.Result)->*member).emplace_back(arg); + }); return *this; } template <typename Range> - void Parse(Result& result, Range const& args, + bool Parse(Result& result, Range const& args, std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue = nullptr, - std::vector<cm::string_view>* parsedKeywords = nullptr) const + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + using ArgumentParser::AsParseResultPtr; + ParseResult* parseResultPtr = AsParseResultPtr(result); + Instance instance(this->Bindings, parseResultPtr, unparsedArguments, + &result); + instance.Parse(args, pos); + return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true; } template <typename Range> Result Parse(Range const& args, std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue = nullptr, - std::vector<cm::string_view>* parsedKeywords = nullptr) const + std::size_t pos = 0) const { Result result; - this->Parse(result, args, unparsedArguments, keywordsMissingValue, - parsedKeywords); + this->Parse(result, args, unparsedArguments, pos); return result; } - -private: - ArgumentParser::ActionMap Bindings; }; template <> -class cmArgumentParser<void> +class cmArgumentParser<void> : private ArgumentParser::Base { public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T& ref) { - bool const inserted = this->Bind(cm::string_view(name), ref); - assert(inserted), (void)inserted; + this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + cm::string_view keyword = instance.Keyword; + instance.Bind( + [keyword, &f](cm::string_view arg) -> Continue { + return f(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref) + { + this->Base::Bind(position, + [&ref](Instance&, std::size_t, cm::string_view arg) { + ref = std::string(arg); + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref) + { + this->Base::BindParsedKeyword( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); return *this; } template <typename Range> - void Parse(Range const& args, std::vector<std::string>* unparsedArguments, - std::vector<cm::string_view>* keywordsMissingValue = nullptr, - std::vector<cm::string_view>* parsedKeywords = nullptr) const + ParseResult Parse(Range const& args, + std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + ParseResult parseResult; + Instance instance(this->Bindings, &parseResult, unparsedArguments); + instance.Parse(args, pos); + return parseResult; } protected: + using Base::Instance; + using Base::BindKeywordMissingValue; + template <typename T> bool Bind(cm::string_view name, T& ref) { - return this->Bindings - .Emplace(name, - [&ref](ArgumentParser::Instance& instance, void*) { - instance.Bind(ref); - }) - .second; + return this->MaybeBind(name, + [&ref](Instance& instance) { instance.Bind(ref); }); } - -private: - ArgumentParser::ActionMap Bindings; }; diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h index 9afa5c7166..7daae09744 100644 --- a/Source/cmArgumentParserTypes.h +++ b/Source/cmArgumentParserTypes.h @@ -4,21 +4,66 @@ #include "cmConfigure.h" // IWYU pragma: keep +#if defined(__SUNPRO_CC) + +# include <string> +# include <vector> + +namespace ArgumentParser { + +template <typename T> +struct Maybe; +template <> +struct Maybe<std::string> : public std::string +{ + using std::string::basic_string; +}; + +template <typename T> +struct MaybeEmpty; +template <typename T> +struct MaybeEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; + +template <typename T> +struct NonEmpty; +template <typename T> +struct NonEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; +template <> +struct NonEmpty<std::string> : public std::string +{ + using std::string::basic_string; +}; + +} // namespace ArgumentParser + +#else + namespace ArgumentParser { template <typename T> struct Maybe : public T { + using T::T; }; template <typename T> struct MaybeEmpty : public T { + using T::T; }; template <typename T> struct NonEmpty : public T { + using T::T; }; } // namespace ArgumentParser + +#endif diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 033dd6de0a..58129a06b6 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -474,7 +474,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, } std::string const& key = *args.begin(); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ValueName; bool ValueNames = false; @@ -491,19 +491,15 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, .Bind("SEPARATOR"_s, &Arguments::Separator) .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); std::vector<std::string> invalidArgs; - std::vector<cm::string_view> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs); if (!invalidArgs.empty()) { status.SetError(cmStrCat("given invalid argument(s) \"", cmJoin(invalidArgs, ", "_s), "\".")); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError(cmStrCat("missing expected value for argument(s) \"", - cmJoin(keywordsMissingValue, ", "_s), "\".")); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if ((!arguments.ValueName.empty() && (arguments.ValueNames || arguments.SubKeys)) || diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index adcffa9f68..7755082236 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCMakePathCommand.h" -#include <algorithm> #include <functional> #include <iomanip> #include <map> @@ -11,10 +10,12 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" @@ -43,15 +44,12 @@ public: } template <int Advance = 2> - Result Parse(std::vector<std::string> const& args, - std::vector<cm::string_view>* keywordsMissingValue = nullptr, - std::vector<cm::string_view>* parsedKeywords = nullptr) const + Result Parse(std::vector<std::string> const& args) const { this->Inputs.clear(); return this->cmArgumentParser<Result>::Parse( - cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue, - parsedKeywords); + cmMakeRange(args).advance(Advance), &this->Inputs); } const std::vector<std::string>& GetInputs() const { return this->Inputs; } @@ -82,52 +80,14 @@ public: template <int Advance = 2> Result Parse(std::vector<std::string> const& args) const { - this->KeywordsMissingValue.clear(); - this->ParsedKeywords.clear(); - return this->CMakePathArgumentParser<Result>::template Parse<Advance>( - args, &this->KeywordsMissingValue, &this->ParsedKeywords); - } - - const std::vector<cm::string_view>& GetKeywordsMissingValue() const - { - return this->KeywordsMissingValue; - } - const std::vector<cm::string_view>& GetParsedKeywords() const - { - return this->ParsedKeywords; - } - - bool checkOutputVariable(const Result& arguments, - cmExecutionStatus& status) const - { - if (std::find(this->GetKeywordsMissingValue().begin(), - this->GetKeywordsMissingValue().end(), - "OUTPUT_VARIABLE"_s) != - this->GetKeywordsMissingValue().end()) { - status.SetError("OUTPUT_VARIABLE requires an argument."); - return false; - } - - if (std::find(this->GetParsedKeywords().begin(), - this->GetParsedKeywords().end(), - "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() && - arguments.Output.empty()) { - status.SetError("Invalid name for output variable."); - return false; - } - - return true; + args); } - -private: - mutable std::vector<cm::string_view> KeywordsMissingValue; - mutable std::vector<cm::string_view> ParsedKeywords; }; -struct OutputVariable +struct OutputVariable : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; }; // Usable when OUTPUT_VARIABLE is the only option class OutputVariableParser @@ -297,8 +257,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1])); @@ -307,7 +267,7 @@ bool HandleAppendCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -319,8 +279,8 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } std::string inputPath; @@ -334,7 +294,7 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -346,8 +306,8 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -364,7 +324,7 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, path.RemoveFileName(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -376,8 +336,8 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -395,7 +355,7 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, parser.GetInputs().empty() ? "" : parser.GetInputs().front()); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -403,9 +363,9 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -415,8 +375,8 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -438,7 +398,7 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -446,9 +406,9 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -458,8 +418,8 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -483,7 +443,7 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -495,8 +455,8 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -512,7 +472,7 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, auto path = cmCMakePath(inputPath).Normal(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -523,10 +483,10 @@ bool HandleTransformPathCommand( const std::string& base)>& transform, bool normalizeOption = false) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string BaseDirectory; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; + cm::optional<std::string> BaseDirectory; bool Normalize = false; }; @@ -538,8 +498,8 @@ bool HandleTransformPathCommand( Arguments arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -547,17 +507,11 @@ bool HandleTransformPathCommand( return false; } - if (std::find(parser.GetKeywordsMissingValue().begin(), - parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) != - parser.GetKeywordsMissingValue().end()) { - status.SetError("BASE_DIRECTORY requires an argument."); - return false; - } - - if (std::find(parser.GetParsedKeywords().begin(), - parser.GetParsedKeywords().end(), - "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) { - arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); + std::string baseDirectory; + if (arguments.BaseDirectory) { + baseDirectory = *arguments.BaseDirectory; + } else { + baseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } std::string inputPath; @@ -565,13 +519,13 @@ bool HandleTransformPathCommand( return false; } - auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory); + auto path = transform(cmCMakePath(inputPath), baseDirectory); if (arguments.Normalize) { path = path.Normal(); } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index c6296f9f42..5e616b30d0 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -20,7 +20,6 @@ #include "cmCMakeMinimumRequired.h" #include "cmCMakePathCommand.h" #include "cmCMakePolicyCommand.h" -#include "cmCommand.h" #include "cmConfigureFileCommand.h" #include "cmContinueCommand.h" #include "cmCreateTestSourceList.h" @@ -264,9 +263,8 @@ void GetProjectCommands(cmState* state) cmTargetLinkLibrariesCommand); state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand); state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand); - state->AddBuiltinCommand("try_compile", - cm::make_unique<cmTryCompileCommand>()); - state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>()); + state->AddBuiltinCommand("try_compile", cmTryCompileCommand); + state->AddBuiltinCommand("try_run", cmTryRunCommand); state->AddBuiltinCommand("target_precompile_headers", cmTargetPrecompileHeadersCommand); diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 45afdd1650..d90b574a72 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -238,7 +238,7 @@ std::set<std::string> const ghs_platform_vars{ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, bool isTryRun) { - this->BinaryDirectory = argv[1]; + std::string const& resultVar = argv[0]; this->OutputFile.clear(); // which signature were we called with ? this->SrcFileSignature = true; @@ -264,7 +264,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - std::string sourceDirectory = argv[2]; + std::string sourceDirectory; std::string projectName; std::string targetName; std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0] @@ -283,11 +283,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::vector<std::string> linkOptions; std::string libsToLink = " "; bool useOldLinkLibs = true; - char targetNameBuf[64]; bool didOutputVariable = false; bool didCopyFile = false; bool didCopyFileError = false; - bool useSources = argv[2] == "SOURCES"; + bool useSources = false; std::vector<std::string> sources; enum Doing @@ -303,9 +302,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, DoingSources, DoingCMakeInternal }; - Doing doing = useSources ? DoingSources : DoingNone; - for (size_t i = 3; i < argv.size(); ++i) { - if (argv[i] == "CMAKE_FLAGS") { + Doing doing = DoingNone; + for (size_t i = 1; i < argv.size(); ++i) { + if (argv[i] == "SOURCES") { + useSources = true; + doing = DoingSources; + } else if (argv[i] == "CMAKE_FLAGS") { doing = DoingCMakeFlags; } else if (argv[i] == "COMPILE_DEFINITIONS") { doing = DoingCompileDefinitions; @@ -379,6 +381,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } else if (doing == DoingCMakeInternal) { cmakeInternal = argv[i]; doing = DoingNone; + } else if (i == 1) { + this->BinaryDirectory = argv[i]; + } else if (i == 2) { + sourceDirectory = argv[i]; } else if (i == 3) { this->SrcFileSignature = false; projectName = argv[i]; @@ -391,6 +397,37 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } + if (!this->BinaryDirectory.empty()) { + if (!cmSystemTools::FileIsFullPath(this->BinaryDirectory)) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("<bindir> is not an absolute path:\n '", + this->BinaryDirectory, "'")); + // Do not try to clean up the ill-specified directory. + this->BinaryDirectory.clear(); + return -1; + } + // compute the binary dir when TRY_COMPILE is called with a src file + // signature + if (this->SrcFileSignature) { + this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; + } + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "No <bindir> specified."); + return -1; + } + + if (this->SrcFileSignature) { + projectName = "CMAKE_TRY_COMPILE"; + /* Use a random file name to avoid rapid creation and deletion + of the same executable name (some filesystems fail on that). */ + char targetNameBuf[64]; + snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", + cmSystemTools::RandomSeed() & 0xFFFFF); + targetName = targetNameBuf; + } + if (didCopyFile && copyFile.empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "COPY_FILE must be followed by a file path"); @@ -425,6 +462,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, return -1; } + // only valid for srcfile signatures if (!this->SrcFileSignature) { if (!cState.Validate(this->Makefile)) { return -1; @@ -444,14 +482,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, if (!objcxxState.Validate(this->Makefile)) { return -1; } - } - // compute the binary dir when TRY_COMPILE is called with a src file - // signature - if (this->SrcFileSignature) { - this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; - } else { - // only valid for srcfile signatures if (!compileDefs.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, @@ -711,12 +742,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmJoin(compileDefs, "]==] [==[").c_str()); } - /* Use a random file name to avoid rapid creation and deletion - of the same executable name (some filesystems fail on that). */ - snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", - cmSystemTools::RandomSeed() & 0xFFFFF); - targetName = targetNameBuf; - if (!targets.empty()) { std::string fname = "/" + std::string(targetName) + "Targets.cmake"; cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile, @@ -873,7 +898,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, libsToLink.c_str()); } fclose(fout); - projectName = "CMAKE_TRY_COMPILE"; } // Forward a set of variables to the inner project cache. @@ -996,7 +1020,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } // set the result var to the return value to indicate success or failure - this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"), + this->Makefile->AddCacheDefinition(resultVar, (res == 0 ? "TRUE" : "FALSE"), "Result of TRY_COMPILE", cmStateEnums::INTERNAL); diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 594fd7f9f5..9d43899110 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -7,19 +7,24 @@ #include <string> #include <vector> -#include "cmCommand.h" #include "cmStateTypes.h" +class cmMakefile; + /** \class cmCoreTryCompile * \brief Base class for cmTryCompileCommand and cmTryRunCommand * * cmCoreTryCompile implements the functionality to build a program. * It is the base class for cmTryCompileCommand and cmTryRunCommand. */ -class cmCoreTryCompile : public cmCommand +class cmCoreTryCompile { public: -protected: + cmCoreTryCompile(cmMakefile* mf) + : Makefile(mf) + { + } + /** * This is the core code for try compile. It is here so that other * commands, such as TryRun can access the same logic without @@ -46,4 +51,5 @@ protected: std::string OutputFile; std::string FindErrorMessage; bool SrcFileSignature = false; + cmMakefile* Makefile; }; diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index af56e2dc04..7fbd826906 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -47,7 +47,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::vector<std::vector<std::string>> Commands; std::string OutputVariable; @@ -95,14 +95,10 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unparsedArguments, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unparsedArguments); - if (!keywordsMissingValue.empty()) { - status.SetError(cmStrCat(" called with no value for ", - keywordsMissingValue.front(), ".")); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { status.SetError(" given unknown argument \"" + unparsedArguments.front() + diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index d2aa63cfaa..330d9916c0 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -954,42 +954,34 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string oldRPath; - std::string newRPath; + cm::optional<std::string> oldRPath; + cm::optional<std::string> newRPath; bool removeEnvironmentRPath = false; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<cm::string_view> missingArgs; - std::vector<cm::string_view> parsedArgs; parser.Bind("FILE"_s, file) .Bind("OLD_RPATH"_s, oldRPath) .Bind("NEW_RPATH"_s, newRPath) .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHANGE not given FILE option."); return false; } - if (oldRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") == - parsedArgs.end()) { + if (!oldRPath) { status.SetError("RPATH_CHANGE not given OLD_RPATH option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_CHANGE not given NEW_RPATH option."); return false; } @@ -1003,17 +995,17 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, + if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath, removeEnvironmentRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1026,31 +1018,25 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string newRPath; + cm::optional<std::string> newRPath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<cm::string_view> missingArgs; - std::vector<cm::string_view> parsedArgs; parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"", unknownArgs.front(), "\".")); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_SET not given FILE option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_SET not given NEW_RPATH option."); return false; } @@ -1064,16 +1050,16 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) { + if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1088,18 +1074,16 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args, std::string file; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<cm::string_view> missingArgs; parser.Bind("FILE"_s, file); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_REMOVE not given FILE option."); @@ -1136,31 +1120,25 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string rpath; + cm::optional<std::string> rpath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<cm::string_view> missingArgs; - std::vector<cm::string_view> parsedArgs; parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHECK not given FILE option."); return false; } - if (rpath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") == - parsedArgs.end()) { + if (!rpath) { status.SetError("RPATH_CHECK not given RPATH option."); return false; } @@ -1169,7 +1147,7 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, // delete it. This is used during installation to re-install a file // if its RPath will change. if (cmSystemTools::FileExists(file, true) && - !cmSystemTools::CheckRPath(file, rpath)) { + !cmSystemTools::CheckRPath(file, *rpath)) { cmSystemTools::RemoveFile(file); } @@ -1252,7 +1230,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { cm::optional<std::string> BaseDirectory; bool ExpandTilde = false; @@ -1263,17 +1241,15 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde); std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValue; - auto arguments = parser.Parse(cmMakeRange(args).advance(3), - &unparsedArguments, &keywordsMissingValue); + auto arguments = + parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments); if (!unparsedArguments.empty()) { status.SetError("REAL_PATH called with unexpected arguments"); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError("BASE_DIRECTORY requires a value"); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!arguments.BaseDirectory) { @@ -2495,7 +2471,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { cm::optional<std::string> Output; cm::optional<std::string> Input; @@ -2506,6 +2482,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, bool NoSourcePermissions = false; bool UseSourcePermissions = false; ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; + std::vector<cm::string_view> ParsedKeywords; }; static auto const parser = @@ -2518,20 +2495,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions) .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions) .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions) - .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle); + .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle) + .BindParsedKeywords(&Arguments::ParsedKeywords); std::vector<std::string> unparsedArguments; - std::vector<cm::string_view> keywordsMissingValues; - std::vector<cm::string_view> parsedKeywords; Arguments const arguments = - parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments, - &keywordsMissingValues, &parsedKeywords); + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("GENERATE keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { @@ -2539,7 +2511,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - if (!arguments.Output || parsedKeywords[0] != "OUTPUT"_s) { + if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) { status.SetError("GENERATE requires OUTPUT as first option."); return false; } @@ -2549,8 +2521,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, status.SetError("GENERATE requires INPUT or CONTENT option."); return false; } - const bool inputIsContent = parsedKeywords[1] == "CONTENT"_s; - if (!inputIsContent && parsedKeywords[1] == "INPUT") { + const bool inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s; + if (!inputIsContent && arguments.ParsedKeywords[1] == "INPUT") { status.SetError("Unknown argument to GENERATE subcommand."); } std::string const& input = @@ -3045,7 +3017,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, "\n ]])"); } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ResolvedDependenciesVar; std::string UnresolvedDependenciesVar; @@ -3088,10 +3060,8 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3099,12 +3069,9 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } cmRuntimeDependencyArchive archive( @@ -3204,7 +3171,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, bool HandleConfigureCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { cm::optional<std::string> Output; cm::optional<std::string> Content; @@ -3223,10 +3190,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { @@ -3236,12 +3201,9 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("CONFIGURE keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } if (!parsedArgs.Output) { @@ -3336,7 +3298,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, bool HandleArchiveCreateCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Output; std::string Format; @@ -3362,10 +3324,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, .Bind("PATHS"_s, &Arguments::Paths); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3373,12 +3333,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } const char* knownFormats[] = { @@ -3467,7 +3424,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, bool HandleArchiveExtractCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Input; bool Verbose = false; @@ -3486,10 +3443,8 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, .Bind("TOUCH"_s, &Arguments::Touch); std::vector<std::string> unrecognizedArguments; - std::vector<cm::string_view> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3497,12 +3452,9 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return false; } - if (!keywordsMissingValues.empty()) { - status.SetError( - cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(keywordsMissingValues), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } std::string inFile = parsedArgs.Input; @@ -3557,10 +3509,15 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return true; } -bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions, - mode_t& perms, cmExecutionStatus& status) +bool ValidateAndConvertPermissions( + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const& + permissions, + mode_t& perms, cmExecutionStatus& status) { - for (const auto& i : permissions) { + if (!permissions) { + return true; + } + for (const auto& i : *permissions) { if (!cmFSPermissions::stringToModeT(i, perms)) { status.SetError(i + " is an invalid permission specifier"); cmSystemTools::SetFatalErrorOccurred(); @@ -3592,11 +3549,14 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, globber.SetRecurse(recurse); globber.SetRecurseListDirs(recurse); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - ArgumentParser::NonEmpty<std::vector<std::string>> Permissions; - ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; - ArgumentParser::NonEmpty<std::vector<std::string>> DirectoryPermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + Permissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + FilePermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + DirectoryPermissions; }; static auto const parser = @@ -3606,21 +3566,20 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions); std::vector<std::string> pathEntries; - std::vector<cm::string_view> keywordsMissingValues; - Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1), - &pathEntries, &keywordsMissingValues); + Arguments parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &pathEntries); // check validity of arguments - if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() && - parsedArgs.DirectoryPermissions.empty()) // no permissions given + if (!parsedArgs.Permissions && !parsedArgs.FilePermissions && + !parsedArgs.DirectoryPermissions) // no permissions given { status.SetError("No permissions given"); cmSystemTools::SetFatalErrorOccurred(); return false; } - if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() && - !parsedArgs.DirectoryPermissions.empty()) // all keywords are used + if (parsedArgs.Permissions && parsedArgs.FilePermissions && + parsedArgs.DirectoryPermissions) // all keywords are used { status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or " "DIRECTORY_PERMISSIONS from the invocation"); @@ -3628,12 +3587,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, return false; } - if (!keywordsMissingValues.empty()) { - for (const auto& i : keywordsMissingValues) { - status.SetError(cmStrCat(i, " is not given any arguments")); - cmSystemTools::SetFatalErrorOccurred(); - } - return false; + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; } // validate permissions @@ -3677,7 +3633,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, if (cmSystemTools::FileExists(i, true)) { bool success = true; const mode_t& filePermissions = - parsedArgs.FilePermissions.empty() ? perms : fperms; + parsedArgs.FilePermissions ? fperms : perms; if (filePermissions) { success = SetPermissions(i, filePermissions, status); } @@ -3689,7 +3645,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, else if (cmSystemTools::FileIsDirectory(i)) { bool success = true; const mode_t& directoryPermissions = - parsedArgs.DirectoryPermissions.empty() ? perms : dperms; + parsedArgs.DirectoryPermissions ? dperms : perms; if (directoryPermissions) { success = SetPermissions(i, directoryPermissions, status); } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 4ad9124f74..f260ec7431 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cassert> #include <cstdio> -#include <cstring> #include <deque> #include <functional> #include <iterator> @@ -43,8 +42,403 @@ # include <StorageDefs.h> #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> +// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx +# if !defined(KEY_WOW64_32KEY) +# define KEY_WOW64_32KEY 0x0200 +# endif +# if !defined(KEY_WOW64_64KEY) +# define KEY_WOW64_64KEY 0x0100 +# endif +#endif + class cmExecutionStatus; -class cmFileList; + +namespace { + +template <template <typename> class Op> +struct StrverscmpOp +{ + bool operator()(const std::string& lhs, const std::string& rhs) const + { + return Op<int>()(cmSystemTools::strverscmp(lhs, rhs), 0); + } +}; + +std::size_t collectPathsForDebug(std::string& buffer, + cmSearchPath const& searchPath, + std::size_t const startIndex = 0) +{ + const auto& paths = searchPath.GetPaths(); + if (paths.empty()) { + buffer += " none\n"; + return 0; + } + for (auto i = startIndex; i < paths.size(); i++) { + buffer += " " + paths[i].Path + "\n"; + } + return paths.size(); +} + +#if !(defined(_WIN32) && !defined(__CYGWIN__)) +class cmFindPackageCommandHoldFile +{ + const char* File; + +public: + cmFindPackageCommandHoldFile(const char* const f) + : File(f) + { + } + ~cmFindPackageCommandHoldFile() + { + if (this->File) { + cmSystemTools::RemoveFile(this->File); + } + } + cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; + cmFindPackageCommandHoldFile& operator=( + const cmFindPackageCommandHoldFile&) = delete; + void Release() { this->File = nullptr; } +}; +#endif + +bool isDirentryToIgnore(const char* const fname) +{ + assert(fname != nullptr); + assert(fname[0] != 0); + return fname[0] == '.' && + (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0)); +} + +class cmAppendPathSegmentGenerator +{ +public: + cmAppendPathSegmentGenerator(cm::string_view dirName) + : DirName{ dirName } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->NeedReset) { + return {}; + } + this->NeedReset = true; + return cmStrCat(parent, '/', this->DirName); + } + + void Reset() { this->NeedReset = false; } + +private: + const cm::string_view DirName; + bool NeedReset = false; +}; + +class cmEnumPathSegmentsGenerator +{ +public: + cmEnumPathSegmentsGenerator(const std::vector<cm::string_view>& init) + : Names{ init } + , Current{ this->Names.get().cbegin() } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Current != this->Names.get().cend()) { + return cmStrCat(parent, '/', *this->Current++); + } + return {}; + } + + void Reset() { this->Current = this->Names.get().cbegin(); } + +private: + std::reference_wrapper<const std::vector<cm::string_view>> Names; + std::vector<cm::string_view>::const_iterator Current; +}; + +class cmCaseInsensitiveDirectoryListGenerator +{ +public: + cmCaseInsensitiveDirectoryListGenerator(cm::string_view name) + : DirectoryLister{} + , DirName{ name } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (!this->Loaded) { + this->CurrentIdx = 0ul; + this->Loaded = true; + if (!this->DirectoryLister.Load(parent)) { + return {}; + } + } + + while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) { + const char* const fname = + this->DirectoryLister.GetFile(this->CurrentIdx++); + if (isDirentryToIgnore(fname)) { + continue; + } + if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) { + auto candidate = cmStrCat(parent, '/', fname); + if (cmSystemTools::FileIsDirectory(candidate)) { + return candidate; + } + } + } + return {}; + } + + void Reset() { this->Loaded = false; } + +private: + cmsys::Directory DirectoryLister; + const cm::string_view DirName; + unsigned long CurrentIdx = 0ul; + bool Loaded = false; +}; + +class cmDirectoryListGenerator +{ +public: + cmDirectoryListGenerator(std::vector<std::string> const& names) + : Names{ names } + , Matches{} + , Current{ this->Matches.cbegin() } + { + } + virtual ~cmDirectoryListGenerator() = default; + + std::string GetNextCandidate(const std::string& parent) + { + // Construct a list of matches if not yet + if (this->Matches.empty()) { + cmsys::Directory directoryLister; + // ALERT `Directory::Load()` keeps only names + // internally and LOST entry type from `dirent`. + // So, `Directory::FileIsDirectory` gonna use + // `SystemTools::FileIsDirectory()` and waste a syscall. + // TODO Need to enhance the `Directory` class. + directoryLister.Load(parent); + + // ATTENTION Is it guaranteed that first two entries are + // `.` and `..`? + // TODO If so, just start with index 2 and drop the + // `isDirentryToIgnore(i)` condition to check. + for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) { + const char* const fname = directoryLister.GetFile(i); + if (isDirentryToIgnore(fname)) { + continue; + } + + for (const auto& n : this->Names.get()) { + // NOTE Customization point for `cmMacProjectDirectoryListGenerator` + const auto name = this->TransformNameBeforeCmp(n); + // Skip entries that don't match and non-directories. + // ATTENTION BTW, original code also didn't check if it's a symlink + // to a directory! + const auto equal = + (cmsysString_strncasecmp(fname, name.c_str(), name.length()) == 0); + if (equal && directoryLister.FileIsDirectory(i)) { + this->Matches.emplace_back(fname); + } + } + } + // NOTE Customization point for `cmProjectDirectoryListGenerator` + this->OnMatchesLoaded(); + + this->Current = this->Matches.cbegin(); + } + + if (this->Current != this->Matches.cend()) { + auto candidate = cmStrCat(parent, '/', *this->Current++); + return candidate; + } + + return {}; + } + + void Reset() + { + this->Matches.clear(); + this->Current = this->Matches.cbegin(); + } + +protected: + virtual void OnMatchesLoaded() {} + virtual std::string TransformNameBeforeCmp(std::string same) { return same; } + + std::reference_wrapper<const std::vector<std::string>> Names; + std::vector<std::string> Matches; + std::vector<std::string>::const_iterator Current; +}; + +class cmProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmProjectDirectoryListGenerator(std::vector<std::string> const& names, + cmFindPackageCommand::SortOrderType so, + cmFindPackageCommand::SortDirectionType sd) + : cmDirectoryListGenerator{ names } + , SortOrder{ so } + , SortDirection{ sd } + { + } + + void OnMatchesLoaded() override + { + // check if there is a specific sorting order to perform + if (this->SortOrder != cmFindPackageCommand::None) { + cmFindPackageCommand::Sort(this->Matches.begin(), this->Matches.end(), + this->SortOrder, this->SortDirection); + } + } + +private: + // sort parameters + const cmFindPackageCommand::SortOrderType SortOrder; + const cmFindPackageCommand::SortDirectionType SortDirection; +}; + +class cmMacProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmMacProjectDirectoryListGenerator(const std::vector<std::string>& names, + cm::string_view ext) + : cmDirectoryListGenerator{ names } + , Extension{ ext } + { + } + + std::string TransformNameBeforeCmp(std::string name) override + { + return cmStrCat(name, this->Extension); + } + +private: + const cm::string_view Extension; +}; + +class cmFileListGeneratorGlob +{ +public: + cmFileListGeneratorGlob(cm::string_view pattern) + : Pattern(pattern) + , Files{} + , Current{} + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Files.empty()) { + // Glob the set of matching files. + std::string expr = cmStrCat(parent, this->Pattern); + cmsys::Glob g; + if (!g.FindFiles(expr)) { + return {}; + } + this->Files = g.GetFiles(); + this->Current = this->Files.cbegin(); + } + + // Skip non-directories + for (; this->Current != this->Files.cend() && + !cmSystemTools::FileIsDirectory(*this->Current); + ++this->Current) { + } + + return (this->Current != this->Files.cend()) ? *this->Current++ + : std::string{}; + } + + void Reset() + { + this->Files.clear(); + this->Current = this->Files.cbegin(); + } + +private: + cm::string_view Pattern; + std::vector<std::string> Files; + std::vector<std::string>::const_iterator Current; +}; + +#if defined(__LCC__) +# define CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_suppress 1222 // invalid error number (3288, but works anyway) +# define CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_suppress 3288 // parameter was declared but never referenced +#endif + +void ResetGenerator() +{ +} + +template <typename Generator> +void ResetGenerator(Generator&& generator) +{ + std::forward<Generator&&>(generator).Reset(); +} + +template <typename Generator, typename... Generators> +void ResetGenerator(Generator&& generator, Generators&&... generators) +{ + ResetGenerator(std::forward<Generator&&>(generator)); + ResetGenerator(std::forward<Generators&&>(generators)...); +} + +template <typename CallbackFn> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& fullPath) +{ + assert(!fullPath.empty() && fullPath.back() != '/'); + return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/'); +} + +template <typename CallbackFn, typename Generator, typename... Rest> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& startPath, Generator&& gen, + Rest&&... tail) +{ + ResetGenerator(std::forward<Generator&&>(gen)); + for (auto path = gen.GetNextCandidate(startPath); !path.empty(); + path = gen.GetNextCandidate(startPath)) { + ResetGenerator(std::forward<Rest&&>(tail)...); + if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path, + std::forward<Rest&&>(tail)...)) { + return true; + } + } + return false; +} + +#ifdef CM_LCC_DIAG_SUPPRESS_3288 +# undef CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_default 3288 +#endif + +#ifdef CM_LCC_DIAG_SUPPRESS_1222 +# undef CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_default 1222 +#endif + +// Parse the version number and store the results that were +// successfully parsed. +int parseVersion(const std::string& version, unsigned int& major, + unsigned int& minor, unsigned int& patch, unsigned int& tweak) +{ + return std::sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, + &tweak); +} + +} // anonymous namespace cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT"); @@ -60,25 +454,10 @@ const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED( const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED( "EXCLUDE"); -struct StrverscmpGreater -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) > 0; - } -}; - -struct StrverscmpLesser -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) < 0; - } -}; - void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, std::vector<std::string>::iterator end, - SortOrderType order, SortDirectionType dir) + SortOrderType const order, + SortDirectionType const dir) { if (order == Name_order) { if (dir == Dec) { @@ -86,14 +465,13 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, } else { std::sort(begin, end); } - } else if (order == Natural) - // natural order uses letters and numbers (contiguous numbers digit are - // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 - { + } else if (order == Natural) { + // natural order uses letters and numbers (contiguous numbers digit are + // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 if (dir == Dec) { - std::sort(begin, end, StrverscmpGreater()); + std::sort(begin, end, StrverscmpOp<std::greater>()); } else { - std::sort(begin, end, StrverscmpLesser()); + std::sort(begin, end, StrverscmpOp<std::less>()); } } // else do not sort @@ -113,11 +491,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status) void cmFindPackageCommand::AppendSearchPathGroups() { - std::vector<cmFindCommon::PathLabel>* labels; - // Update the All group with new paths. Note that package redirection must // take precedence over everything else, so it has to be first in the array. - labels = &this->PathGroupLabelMap[PathGroup::All]; + std::vector<cmFindCommon::PathLabel>* const labels = + &this->PathGroupLabelMap[PathGroup::All]; labels->insert(labels->begin(), PathLabel::PackageRedirect); labels->insert( std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem), @@ -147,15 +524,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Lookup required version of CMake. - if (cmValue rv = + if (cmValue const rv = this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { unsigned int v[3] = { 0, 0, 0 }; - sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); + std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]); } // Lookup target architecture, if any. - if (cmValue arch = + if (cmValue const arch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { this->LibraryArchitecture = *arch; } @@ -184,7 +561,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if User Package Registry should be disabled // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY - if (cmValue def = + if (cmValue const def = this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) { this->NoUserRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) { @@ -194,7 +571,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if System Package Registry should be disabled // The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY - if (cmValue def = this->Makefile->GetDefinition( + if (cmValue const def = this->Makefile->GetDefinition( "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) { this->NoSystemRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn( @@ -208,7 +585,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Check if Sorting should be enabled - if (cmValue so = + if (cmValue const so = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) { if (*so == "NAME") { @@ -219,7 +596,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->SortOrder = None; } } - if (cmValue sd = + if (cmValue const sd = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) { this->SortDirection = (*sd == "ASC") ? Asc : Dec; } @@ -265,9 +642,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) cmsys::RegularExpression versionRegex( R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V"); bool haveVersion = false; - std::set<unsigned int> configArgs; - std::set<unsigned int> moduleArgs; - for (unsigned int i = 1; i < args.size(); ++i) { + std::vector<std::size_t> configArgs; + std::vector<std::size_t> moduleArgs; + for (std::size_t i = 1u; i < args.size(); ++i) { if (args[i] == "QUIET") { this->Quiet = true; doing = DoingNone; @@ -281,17 +658,17 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->GlobalScope = true; doing = DoingNone; } else if (args[i] == "MODULE") { - moduleArgs.insert(i); + moduleArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "CONFIG") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_MODULE") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REQUIRED") { this->Required = true; @@ -301,36 +678,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } else if (args[i] == "OPTIONAL_COMPONENTS") { doing = DoingOptionalComponents; } else if (args[i] == "NAMES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNames; } else if (args[i] == "PATHS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPaths; } else if (args[i] == "HINTS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingHints; } else if (args[i] == "PATH_SUFFIXES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPathSuffixes; } else if (args[i] == "CONFIGS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingConfigs; } else if (args[i] == "NO_POLICY_SCOPE") { this->PolicyScope = false; doing = DoingNone; } else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") { this->NoUserRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") { this->NoSystemRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_CMAKE_BUILDS_PATH") { // Ignore legacy option. - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REGISTRY_VIEW") { if (++i == args.size()) { @@ -347,7 +724,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } } else if (this->CheckCommonArgument(args[i])) { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if ((doing == DoingComponents) || (doing == DoingOptionalComponents)) { @@ -361,8 +738,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) requiredComponents.insert(args[i]); } - std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i]; - componentVarDefs.emplace_back(req_var, isRequired); + componentVarDefs.emplace_back(this->Name + "_FIND_REQUIRED_" + args[i], + isRequired); // Append to the list of required components. components += components_sep; @@ -420,11 +797,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (!this->UseFindModules && !this->UseConfigFiles) { std::ostringstream e; e << "given options exclusive to Module mode:\n"; - for (unsigned int si : moduleArgs) { + for (auto si : moduleArgs) { e << " " << args[si] << "\n"; } e << "and options exclusive to Config mode:\n"; - for (unsigned int si : configArgs) { + for (auto si : configArgs) { e << " " << args[si] << "\n"; } e << "The options are incompatible."; @@ -443,20 +820,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (this->VersionComplete.empty() || components.empty()) { // Check whether we are recursing inside "Find<name>.cmake" within // another find_package(<name>) call. - std::string mod = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const mod = cmStrCat(this->Name, "_FIND_MODULE"); if (this->Makefile->IsOn(mod)) { if (this->VersionComplete.empty()) { // Get version information from the outer call if necessary. // Requested version string. - std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); + std::string const ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); this->VersionComplete = this->Makefile->GetSafeDefinition(ver); // Whether an exact version is required. - std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); + std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); this->VersionExact = this->Makefile->IsOn(exact); } if (components.empty()) { - std::string components_var = this->Name + "_FIND_COMPONENTS"; + std::string const components_var = this->Name + "_FIND_COMPONENTS"; components = this->Makefile->GetSafeDefinition(components_var); } } @@ -497,15 +874,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } - // Parse the version number and store the results that were - // successfully parsed. - auto parseVersion = [](const std::string& version, unsigned int& major, - unsigned int& minor, unsigned int& patch, - unsigned int& tweak) -> unsigned int { - return sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, - &tweak); - }; - if (!this->Version.empty()) { this->VersionCount = parseVersion(this->Version, this->VersionMajor, this->VersionMinor, @@ -533,7 +901,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } - std::string disableFindPackageVar = + std::string const disableFindPackageVar = cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name); if (this->Makefile->IsOn(disableFindPackageVar)) { if (this->Required) { @@ -557,8 +925,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // A dependency provider (if set) gets first look before other methods. // We do this before modifying the package root path stack because a // provider might use methods that ignore that. - cmState* state = this->Makefile->GetState(); - cmState::Command providerCommand = state->GetDependencyProviderCommand( + cmState* const state = this->Makefile->GetState(); + cmState::Command const providerCommand = state->GetDependencyProviderCommand( cmDependencyProvider::Method::FindPackage); if (bypassProvider) { if (this->DebugMode && providerCommand) { @@ -725,11 +1093,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) "package configuration file provided by " << this->Name << " (" << this->Name << "Config.cmake or " << cmSystemTools::LowerCase(this->Name) - << "-config.cmake). " - "Otherwise make Find" - << this->Name - << ".cmake available in " - "CMAKE_MODULE_PATH."; + << "-config.cmake). Otherwise make Find" << this->Name + << ".cmake available in CMAKE_MODULE_PATH."; } aw << "\n" "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this " @@ -813,9 +1178,9 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode() void cmFindPackageCommand::SetVersionVariables( const std::function<void(const std::string&, cm::string_view)>& addDefinition, - const std::string& prefix, const std::string& version, unsigned int count, - unsigned int major, unsigned int minor, unsigned int patch, - unsigned int tweak) + const std::string& prefix, const std::string& version, + const unsigned int count, const unsigned int major, const unsigned int minor, + const unsigned int patch, const unsigned int tweak) { addDefinition(prefix, version); @@ -910,7 +1275,7 @@ void cmFindPackageCommand::SetModuleVariables( } void cmFindPackageCommand::AddFindDefinition(const std::string& var, - cm::string_view value) + const cm::string_view value) { if (cmValue old = this->Makefile->GetDefinition(var)) { this->OriginalDefs[var].exists = true; @@ -954,7 +1319,7 @@ bool cmFindPackageCommand::FindModule(bool& found) if (!mfile.empty()) { if (system) { - auto it = this->DeprecatedFindModules.find(this->Name); + auto const it = this->DeprecatedFindModules.find(this->Name); if (it != this->DeprecatedFindModules.end()) { cmPolicies::PolicyStatus status = this->Makefile->GetPolicyStatus(it->second); @@ -978,13 +1343,13 @@ bool cmFindPackageCommand::FindModule(bool& found) // Load the module we found, and set "<name>_FIND_MODULE" to true // while inside it. found = true; - std::string var = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const var = cmStrCat(this->Name, "_FIND_MODULE"); this->Makefile->AddDefinition(var, "1"); bool result = this->ReadListFile(mfile, DoPolicyScope); this->Makefile->RemoveDefinition(var); if (this->DebugMode) { - std::string foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); if (this->Makefile->IsDefinitionSet(foundVar) && !this->Makefile->IsOn(foundVar)) { @@ -999,7 +1364,7 @@ bool cmFindPackageCommand::FindModule(bool& found) } bool cmFindPackageCommand::HandlePackageMode( - HandlePackageModeType handlePackageModeType) + const HandlePackageModeType handlePackageModeType) { this->ConsideredConfigs.clear(); @@ -1042,8 +1407,9 @@ bool cmFindPackageCommand::HandlePackageMode( } } - std::string foundVar = cmStrCat(this->Name, "_FOUND"); - std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const notFoundMessageVar = + cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); std::string notFoundMessage; // If the directory for the config file was found, try to read the file. @@ -1123,8 +1489,9 @@ bool cmFindPackageCommand::HandlePackageMode( << (this->VersionExact ? "exactly matches" : "is compatible with") << " requested version " << (this->VersionRange.empty() ? "" : "range ") << "\"" - << this->VersionComplete << "\".\n" - << "The following configuration files were considered but not " + << this->VersionComplete + << "\".\n" + "The following configuration files were considered but not " "accepted:\n"; for (ConfigFileInfo const& info : @@ -1172,8 +1539,9 @@ bool cmFindPackageCommand::HandlePackageMode( "package or SDK, be sure it has been installed."; } else // if(!this->UseFindModules && !this->UseConfigFiles) { - e << "No \"Find" << this->Name << ".cmake\" found in " - << "CMAKE_MODULE_PATH."; + e << "No \"Find" << this->Name + << ".cmake\" found in " + "CMAKE_MODULE_PATH."; aw << "Find" << this->Name @@ -1217,16 +1585,16 @@ bool cmFindPackageCommand::HandlePackageMode( this->Makefile->AddDefinition(foundVar, found ? "1" : "0"); // Set a variable naming the configuration file that was found. - std::string fileVar = cmStrCat(this->Name, "_CONFIG"); + std::string const fileVar = cmStrCat(this->Name, "_CONFIG"); if (found) { this->Makefile->AddDefinition(fileVar, this->FileFound); } else { this->Makefile->RemoveDefinition(fileVar); } - std::string consideredConfigsVar = + std::string const consideredConfigsVar = cmStrCat(this->Name, "_CONSIDERED_CONFIGS"); - std::string consideredVersionsVar = + std::string const consideredVersionsVar = cmStrCat(this->Name, "_CONSIDERED_VERSIONS"); std::string consideredConfigFiles; @@ -1312,7 +1680,7 @@ bool cmFindPackageCommand::FindConfig() void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value) { - std::string help = + std::string const help = cmStrCat("The directory containing a CMake configuration file for ", this->Name, '.'); this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(), @@ -1351,7 +1719,7 @@ bool cmFindPackageCommand::FindAppBundleConfig() } bool cmFindPackageCommand::ReadListFile(const std::string& f, - PolicyScopeRule psr) + const PolicyScopeRule psr) { const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope; @@ -1362,12 +1730,12 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f, if (this->Makefile->ReadDependentFile(f, noPolicyScope)) { return true; } - std::string e = cmStrCat("Error reading CMake code from \"", f, "\"."); + std::string const e = cmStrCat("Error reading CMake code from \"", f, "\"."); this->SetError(e); return false; } -void cmFindPackageCommand::AppendToFoundProperty(bool found) +void cmFindPackageCommand::AppendToFoundProperty(const bool found) { std::vector<std::string> foundContents; cmValue foundProp = @@ -1410,27 +1778,28 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found) void cmFindPackageCommand::AppendSuccessInformation() { { - std::string transitivePropName = + std::string const transitivePropName = cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY"); this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False"); } - std::string found = cmStrCat(this->Name, "_FOUND"); - std::string upperFound = cmSystemTools::UpperCase(found); + std::string const found = cmStrCat(this->Name, "_FOUND"); + std::string const upperFound = cmSystemTools::UpperCase(found); - bool upperResult = this->Makefile->IsOn(upperFound); - bool result = this->Makefile->IsOn(found); - bool packageFound = (result || upperResult); + bool const upperResult = this->Makefile->IsOn(upperFound); + bool const result = this->Makefile->IsOn(found); + bool const packageFound = (result || upperResult); this->AppendToFoundProperty(packageFound); // Record whether the find was quiet or not, so this can be used // e.g. in FeatureSummary.cmake - std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET"); + std::string const quietInfoPropName = + cmStrCat("_CMAKE_", this->Name, "_QUIET"); this->Makefile->GetState()->SetGlobalProperty( quietInfoPropName, this->Quiet ? "TRUE" : "FALSE"); // set a global property to record the required version of this package - std::string versionInfoPropName = + std::string const versionInfoPropName = cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION"); std::string versionInfo; if (!this->VersionRange.empty()) { @@ -1442,28 +1811,13 @@ void cmFindPackageCommand::AppendSuccessInformation() this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName, versionInfo.c_str()); if (this->Required) { - std::string requiredInfoPropName = + std::string const requiredInfoPropName = cmStrCat("_CMAKE_", this->Name, "_TYPE"); this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName, "REQUIRED"); } } -inline std::size_t collectPathsForDebug(std::string& buffer, - cmSearchPath const& searchPath, - std::size_t startIndex = 0) -{ - const auto& paths = searchPath.GetPaths(); - if (paths.empty()) { - buffer += " none\n"; - return 0; - } - for (std::size_t i = startIndex; i < paths.size(); i++) { - buffer += " " + paths[i].Path + "\n"; - } - return paths.size(); -} - void cmFindPackageCommand::ComputePrefixes() { this->FillPrefixesPackageRedirect(); @@ -1674,14 +2028,6 @@ void cmFindPackageCommand::FillPrefixesSystemRegistry() } #if defined(_WIN32) && !defined(__CYGWIN__) -# include <windows.h> -// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx -# if !defined(KEY_WOW64_32KEY) -# define KEY_WOW64_32KEY 0x0200 -# endif -# if !defined(KEY_WOW64_64KEY) -# define KEY_WOW64_64KEY 0x0100 -# endif void cmFindPackageCommand::LoadPackageRegistryWinUser() { // HKEY_CURRENT_USER\\Software shares 32-bit and 64-bit views. @@ -1704,7 +2050,8 @@ void cmFindPackageCommand::LoadPackageRegistryWinSystem() } } -void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, +void cmFindPackageCommand::LoadPackageRegistryWin(const bool user, + const unsigned int view, cmSearchPath& outPaths) { std::wstring key = L"Software\\Kitware\\CMake\\Packages\\"; @@ -1756,28 +2103,8 @@ void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, RegCloseKey(hKey); } } -#else -class cmFindPackageCommandHoldFile -{ - const char* File; - -public: - cmFindPackageCommandHoldFile(const char* f) - : File(f) - { - } - ~cmFindPackageCommandHoldFile() - { - if (this->File) { - cmSystemTools::RemoveFile(this->File); - } - } - cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; - cmFindPackageCommandHoldFile& operator=( - const cmFindPackageCommandHoldFile&) = delete; - void Release() { this->File = nullptr; } -}; +#else void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir, cmSearchPath& outPaths) { @@ -1877,7 +2204,7 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable() std::vector<std::string> expanded = cmExpandedList(*prefix_paths); long count = 0; for (const auto& path : expanded) { - bool to_add = + bool const to_add = !(path == install_path_to_remove && ++count == install_prefix_count); if (to_add) { paths.AddPath(path); @@ -1941,7 +2268,7 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir) std::string d = dir; if (!s.empty()) { d += s; - d += "/"; + d += '/'; } if (this->CheckDirectory(d)) { return true; @@ -1955,7 +2282,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir) assert(!dir.empty() && dir.back() == '/'); // Look for the file in this directory. - std::string d = dir.substr(0, dir.size() - 1); + std::string const d = dir.substr(0, dir.size() - 1); if (this->FindConfigFile(d, this->FileFound)) { // Remove duplicate slashes. cmSystemTools::ConvertToUnixSlashes(this->FileFound); @@ -2028,8 +2355,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, std::string& result_version) { // The version file will be loaded in an isolated scope. - cmMakefile::ScopePushPop varScope(this->Makefile); - cmMakefile::PolicyPushPop polScope(this->Makefile); + cmMakefile::ScopePushPop const varScope(this->Makefile); + cmMakefile::PolicyPushPop const polScope(this->Makefile); static_cast<void>(varScope); static_cast<void>(polScope); @@ -2076,7 +2403,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, if (this->ReadListFile(version_file, NoPolicyScope)) { // Check the output variables. bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT"); - bool unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); + bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); if (!okay && !this->VersionExact) { okay = this->Makefile->IsOn("PACKAGE_VERSION_COMPATIBLE"); } @@ -2096,8 +2423,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, unsigned int parsed_patch; unsigned int parsed_tweak; this->VersionFoundCount = - sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", &parsed_major, - &parsed_minor, &parsed_patch, &parsed_tweak); + parseVersion(this->VersionFound, parsed_major, parsed_minor, + parsed_patch, parsed_tweak); switch (this->VersionFoundCount) { case 4: this->VersionFoundTweak = parsed_tweak; @@ -2129,7 +2456,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, void cmFindPackageCommand::StoreVersionFound() { // Store the whole version string. - std::string ver = cmStrCat(this->Name, "_VERSION"); + std::string const ver = cmStrCat(this->Name, "_VERSION"); auto addDefinition = [this](const std::string& variable, cm::string_view value) { this->Makefile->AddDefinition(variable, value); @@ -2145,357 +2472,6 @@ void cmFindPackageCommand::StoreVersionFound() } } -class cmFileListGeneratorBase -{ -public: - virtual ~cmFileListGeneratorBase() = default; - -protected: - bool Consider(std::string const& fullPath, cmFileList& listing); - -private: - bool Search(cmFileList&); - virtual bool Search(std::string const& parent, cmFileList&) = 0; - virtual std::unique_ptr<cmFileListGeneratorBase> Clone() const = 0; - friend class cmFileList; - cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next); - std::unique_ptr<cmFileListGeneratorBase> Next; -}; - -class cmFileList -{ -public: - virtual ~cmFileList() = default; - cmFileList& operator/(cmFileListGeneratorBase const& rhs) - { - if (this->Last) { - this->Last = this->Last->SetNext(rhs); - } else { - this->First = rhs.Clone(); - this->Last = this->First.get(); - } - return *this; - } - bool Search() - { - if (this->First) { - return this->First->Search(*this); - } - return false; - } - -private: - virtual bool Visit(std::string const& fullPath) = 0; - friend class cmFileListGeneratorBase; - std::unique_ptr<cmFileListGeneratorBase> First; - cmFileListGeneratorBase* Last = nullptr; -}; - -class cmFindPackageFileList : public cmFileList -{ -public: - cmFindPackageFileList(cmFindPackageCommand* fpc, bool use_suffixes = true) - : FPC(fpc) - , UseSuffixes(use_suffixes) - { - } - -private: - bool Visit(std::string const& fullPath) override - { - if (this->UseSuffixes) { - return this->FPC->SearchDirectory(fullPath); - } - return this->FPC->CheckDirectory(fullPath); - } - cmFindPackageCommand* FPC; - bool UseSuffixes; -}; - -bool cmFileListGeneratorBase::Search(cmFileList& listing) -{ - return this->Search("", listing); -} - -cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext( - cmFileListGeneratorBase const& next) -{ - this->Next = next.Clone(); - return this->Next.get(); -} - -bool cmFileListGeneratorBase::Consider(std::string const& fullPath, - cmFileList& listing) -{ - if (!fullPath.empty() && !cmSystemTools::FileIsDirectory(fullPath)) { - return false; - } - if (this->Next) { - return this->Next->Search(fullPath + "/", listing); - } - return listing.Visit(fullPath + "/"); -} - -class cmFileListGeneratorFixed : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorFixed(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - std::string fullPath = parent + this->String; - return this->Consider(fullPath, lister); - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorFixed(*this)); - return g; - } -}; - -class cmFileListGeneratorEnumerate : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorEnumerate(std::vector<std::string> const& v) - : Vector(v) - { - } - cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r) - : Vector(r.Vector) - { - } - -private: - std::vector<std::string> const& Vector; - bool Search(std::string const& parent, cmFileList& lister) override - { - for (std::string const& i : this->Vector) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorEnumerate(*this)); - return g; - } -}; - -class cmFileListGeneratorProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorProject(std::vector<std::string> const& names, - cmFindPackageCommand::SortOrderType so, - cmFindPackageCommand::SortDirectionType sd) - : Names(names) - { - this->SetSort(so, sd); - } - cmFileListGeneratorProject(cmFileListGeneratorProject const& r) - : Names(r.Names) - { - this->SetSort(r.SortOrder, r.SortDirection); - } - - void SetSort(cmFindPackageCommand::SortOrderType o, - cmFindPackageCommand::SortDirectionType d) - { - this->SortOrder = o; - this->SortDirection = d; - } - -protected: - // sort parameters - cmFindPackageCommand::SortOrderType SortOrder; - cmFindPackageCommand::SortDirectionType SortDirection; - -private: - std::vector<std::string> const& Names; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string const& n : this->Names) { - if (cmsysString_strncasecmp(fname, n.c_str(), n.length()) == 0) { - matches.emplace_back(fname); - } - } - } - - // before testing the matches check if there is a specific sorting order to - // perform - if (this->SortOrder != cmFindPackageCommand::None) { - cmFindPackageCommand::Sort(matches.begin(), matches.end(), - this->SortOrder, this->SortDirection); - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorProject(*this)); - return g; - } -}; - -class cmFileListGeneratorMacProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorMacProject(std::vector<std::string> const& names, - const char* ext) - : Names(names) - , Extension(ext) - { - } - cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r) - : Names(r.Names) - , Extension(r.Extension) - { - } - -private: - std::vector<std::string> const& Names; - std::string Extension; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string name : this->Names) { - name += this->Extension; - if (cmsysString_strcasecmp(fname, name.c_str()) == 0) { - matches.emplace_back(fname); - } - } - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorMacProject(*this)); - return g; - } -}; - -class cmFileListGeneratorCaseInsensitive : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorCaseInsensitive(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorCaseInsensitive( - cmFileListGeneratorCaseInsensitive const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Look for matching files. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - if (cmsysString_strcasecmp(fname, this->String.c_str()) == 0) { - if (this->Consider(parent + fname, lister)) { - return true; - } - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorCaseInsensitive(*this)); - return g; - } -}; - -class cmFileListGeneratorGlob : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorGlob(std::string str) - : Pattern(std::move(str)) - { - } - cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r) - : Pattern(r.Pattern) - { - } - -private: - std::string Pattern; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Glob the set of matching files. - std::string expr = cmStrCat(parent, this->Pattern); - cmsys::Glob g; - if (!g.FindFiles(expr)) { - return false; - } - std::vector<std::string> const& files = g.GetFiles(); - - // Look for directories among the matches. - for (std::string const& f : files) { - if (this->Consider(f, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - return cm::make_unique<cmFileListGeneratorGlob>(*this); - } -}; - bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) { assert(!prefix_in.empty() && prefix_in.back() == '/'); @@ -2515,148 +2491,95 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) return false; } - // PREFIX/ (useful on windows or in build trees) + // PREFIX/ (useful on windows or in build trees) if (this->SearchDirectory(prefix_in)) { return true; } // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); - // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto firstPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + + // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) + if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) { + return true; } // Construct list of common install locations (lib and share). - std::vector<std::string> common; + std::vector<cm::string_view> common; + std::string libArch; if (!this->LibraryArchitecture.empty()) { - common.push_back("lib/" + this->LibraryArchitecture); + libArch = "lib/" + this->LibraryArchitecture; + common.emplace_back(libArch); } if (this->UseLib32Paths) { - common.emplace_back("lib32"); + common.emplace_back("lib32"_s); } if (this->UseLib64Paths) { - common.emplace_back("lib64"); + common.emplace_back("lib64"_s); } if (this->UseLibx32Paths) { - common.emplace_back("libx32"); + common.emplace_back("libx32"_s); } - common.emplace_back("lib"); - common.emplace_back("share"); + common.emplace_back("lib"_s); + common.emplace_back("share"_s); - // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + auto cmnGen = cmEnumPathSegmentsGenerator{ common }; + auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s }; + + // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) { + return true; } + auto secondPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen, iCMakeGen); } bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) @@ -2665,56 +2588,36 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto fwGen = + cmMacProjectDirectoryListGenerator{ this->Names, ".framework"_s }; + auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s }; + auto vGen = cmAppendPathSegmentGenerator{ "Versions"_s }; + auto grGen = cmFileListGeneratorGlob{ "/*/Resources"_s }; // <prefix>/Foo.framework/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) { + return true; } + // <prefix>/Foo.framework/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen, iCMakeGen); } bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) @@ -2723,32 +2626,24 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto appGen = cmMacProjectDirectoryListGenerator{ this->Names, ".app"_s }; + auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s }; // <prefix>/Foo.app/Contents/Resources - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) { + return true; } // <prefix>/Foo.app/Contents/Resources/CMake - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths( + searchFn, prefix, appGen, crGen, + cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }); } // TODO: Debug cmsys::Glob double slash problem. diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 80fd8f8cae..28e00a109d 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -153,8 +153,6 @@ private: bool SearchFrameworkPrefix(std::string const& prefix_in); bool SearchAppBundlePrefix(std::string const& prefix_in); - friend class cmFindPackageFileList; - struct OriginalDef { bool exists; diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx index b529b8f04c..6212bbde2b 100644 --- a/Source/cmGeneratedFileStream.cxx +++ b/Source/cmGeneratedFileStream.cxx @@ -124,10 +124,10 @@ cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase() void cmGeneratedFileStreamBase::Open(std::string const& name) { // Save the original name of the file. - this->Name = name; + this->Name = cmSystemTools::CollapseFullPath(name); // Create the name of the temporary file. - this->TempName = name; + this->TempName = this->Name; #if defined(__VMS) this->TempName += "_"; #else @@ -231,7 +231,7 @@ int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname, void cmGeneratedFileStream::SetName(const std::string& fname) { - this->Name = fname; + this->Name = cmSystemTools::CollapseFullPath(fname); } void cmGeneratedFileStream::SetTempExt(std::string const& ext) diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index dace0556b6..d59ac2b3a0 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -16,6 +16,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -8660,6 +8661,11 @@ bool cmGeneratorTarget::AddHeaderSetVerification() verifyTarget->SetProperty("AUTOUIC", "OFF"); verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON"); verifyTarget->SetProperty("UNITY_BUILD", "OFF"); + cm::optional<std::map<std::string, cmValue>> + perConfigCompileDefinitions; + verifyTarget->FinalizeTargetCompileInfo( + this->Makefile->GetCompileDefinitionsEntries(), + perConfigCompileDefinitions); } if (fileCgesContextSensitive) { @@ -8706,6 +8712,12 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile( languages->insert("C"); } } + + if (languages->empty()) { + std::vector<std::string> languagesVector; + this->GlobalGenerator->GetEnabledLanguages(languagesVector); + languages->insert(languagesVector.begin(), languagesVector.end()); + } } if (languages->count("CXX")) { diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4cb541a752..edc4118499 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -14,6 +14,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cmext/algorithm> #include <cmext/string_view> @@ -49,6 +50,7 @@ #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateTypes.h" +#include "cmSystemTools.h" #include "cmValue.h" #include "cmVersion.h" #include "cmWorkingDirectory.h" @@ -62,10 +64,6 @@ # include "cmQtAutoGenGlobalInitializer.h" #endif -#if defined(_MSC_VER) && _MSC_VER >= 1800 -# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx -#endif - const std::string kCMAKE_PLATFORM_INFO_INITIALIZED = "CMAKE_PLATFORM_INFO_INITIALIZED"; @@ -616,34 +614,12 @@ void cmGlobalGenerator::EnableLanguage( // what platform we are running on if (!mf->GetDefinition("CMAKE_SYSTEM")) { #if defined(_WIN32) && !defined(__CYGWIN__) - /* Windows version number data. */ - OSVERSIONINFOEXW osviex; - ZeroMemory(&osviex, sizeof(osviex)); - osviex.dwOSVersionInfoSize = sizeof(osviex); - -# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx -# pragma warning(push) -# ifdef __INTEL_COMPILER -# pragma warning(disable : 1478) -# elif defined __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -# else -# pragma warning(disable : 4996) -# endif -# endif - GetVersionExW((OSVERSIONINFOW*)&osviex); -# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx -# ifdef __clang__ -# pragma clang diagnostic pop -# else -# pragma warning(pop) -# endif -# endif + cmSystemTools::WindowsVersion windowsVersion = + cmSystemTools::GetWindowsVersion(); std::ostringstream windowsVersionString; - windowsVersionString << osviex.dwMajorVersion << "." - << osviex.dwMinorVersion << "." - << osviex.dwBuildNumber; + windowsVersionString << windowsVersion.dwMajorVersion << "." + << windowsVersion.dwMinorVersion << "." + << windowsVersion.dwBuildNumber; mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str()); #endif // Read the DetermineSystem file @@ -1399,7 +1375,9 @@ void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes) this->CheckTargetProperties(); } this->CreateGeneratorTargets(targetTypes); - this->ComputeBuildFileGenerators(); + if (targetTypes == TargetTypes::AllTargets) { + this->ComputeBuildFileGenerators(); + } } void cmGlobalGenerator::CreateImportedGenerationObjects( @@ -1846,39 +1824,14 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo() // Construct per-target generator information. for (const auto& mf : this->Makefiles) { - const cmBTStringRange noconfig_compile_definitions = + const cmBTStringRange noConfigCompileDefinitions = mf->GetCompileDefinitionsEntries(); + cm::optional<std::map<std::string, cmValue>> perConfigCompileDefinitions; for (auto& target : mf->GetTargets()) { cmTarget* t = &target.second; - if (t->GetType() == cmStateEnums::GLOBAL_TARGET) { - continue; - } - - t->AppendBuildInterfaceIncludes(); - - if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - - for (auto const& def : noconfig_compile_definitions) { - t->InsertCompileDefinition(def); - } - - cmPolicies::PolicyStatus polSt = - mf->GetPolicyStatus(cmPolicies::CMP0043); - if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { - std::vector<std::string> configs = - mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); - - for (std::string const& c : configs) { - std::string defPropName = - cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c)); - if (cmValue val = mf->GetProperty(defPropName)) { - t->AppendProperty(defPropName, *val); - } - } - } + t->FinalizeTargetCompileInfo(noConfigCompileDefinitions, + perConfigCompileDefinitions); } // The standard include directories for each language diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 155efde70d..29eeb5a917 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -1286,14 +1286,6 @@ 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 2203f7110d..4977a845d0 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -118,8 +118,6 @@ 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/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index ce943a2c97..758ce83813 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -180,7 +180,7 @@ void cmGlobalVisualStudio71Generator::WriteExternalProject( fout << "\tProjectSection(ProjectDependencies) = postProject\n"; for (BT<std::pair<std::string, bool>> const& it : depends) { std::string const& dep = it.Value.first; - if (!dep.empty()) { + if (this->IsDepInSolution(dep)) { fout << "\t\t{" << this->GetGUID(dep) << "} = {" << this->GetGUID(dep) << "}\n"; } diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index cddaaa4339..c6af20ad64 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -843,6 +843,12 @@ bool cmGlobalVisualStudioGenerator::IsInSolution( return gt->IsInBuildSystem(); } +bool cmGlobalVisualStudioGenerator::IsDepInSolution( + const std::string& targetName) const +{ + return !targetName.empty(); +} + bool cmGlobalVisualStudioGenerator::TargetCompare::operator()( cmGeneratorTarget const* l, cmGeneratorTarget const* r) const { diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 4f5f1006e7..f45b4d49a2 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -101,6 +101,9 @@ public: // return true if target should be included in solution. virtual bool IsInSolution(const cmGeneratorTarget* gt) const; + // return true if project dependency should be included in solution. + virtual bool IsDepInSolution(const std::string& targetName) const; + /** Get the top-level registry key for this VS version. */ std::string GetRegistryBase(); diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index b72fc4e80f..7e36881935 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -25,53 +25,100 @@ #include "cmVSSetupHelper.h" #include "cmake.h" -#if defined(_M_ARM64) -# define HOST_PLATFORM_NAME "ARM64" -# define HOST_TOOLS_ARCH(v) \ - (v >= cmGlobalVisualStudioGenerator::VSVersion::VS17) ? "ARM64" : "" -#elif defined(_M_ARM) -# define HOST_PLATFORM_NAME "ARM" -# define HOST_TOOLS_ARCH(v) "" -#elif defined(_M_IA64) -# define HOST_PLATFORM_NAME "Itanium" -# define HOST_TOOLS_ARCH(v) "" -#elif defined(_WIN64) -# define HOST_PLATFORM_NAME "x64" -# define HOST_TOOLS_ARCH(v) "x64" -#else +#ifndef IMAGE_FILE_MACHINE_ARM64 +# define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian +#endif + static bool VSIsWow64() { BOOL isWow64 = false; return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64; } + +static bool VSIsArm64Host() +{ + typedef BOOL(WINAPI * CM_ISWOW64PROCESS2)( + HANDLE hProcess, USHORT * pProcessMachine, USHORT * pNativeMachine); + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# define CM_VS_GCC_DIAGNOSTIC_PUSHED +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" #endif + static const CM_ISWOW64PROCESS2 s_IsWow64Process2Impl = + (CM_ISWOW64PROCESS2)GetProcAddress( + GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"), + "IsWow64Process2"); +#ifdef CM_VS_GCC_DIAGNOSTIC_PUSHED +# pragma GCC diagnostic pop +# undef CM_VS_GCC_DIAGNOSTIC_PUSHED +#endif + + USHORT processMachine, nativeMachine; + + return s_IsWow64Process2Impl != nullptr && + s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine, + &nativeMachine) && + nativeMachine == IMAGE_FILE_MACHINE_ARM64; +} + +static bool VSHasDotNETFrameworkArm64() +{ + std::string dotNetArm64; + return cmSystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRootArm64", + dotNetArm64, cmSystemTools::KeyWOW64_64); +} + +static bool VSIsWindows11OrGreater() +{ + cmSystemTools::WindowsVersion const windowsVersion = + cmSystemTools::GetWindowsVersion(); + return (windowsVersion.dwMajorVersion > 10 || + (windowsVersion.dwMajorVersion == 10 && + windowsVersion.dwMinorVersion > 0) || + (windowsVersion.dwMajorVersion == 10 && + windowsVersion.dwMinorVersion == 0 && + windowsVersion.dwBuildNumber >= 22000)); +} static std::string VSHostPlatformName() { -#ifdef HOST_PLATFORM_NAME - return HOST_PLATFORM_NAME; -#else - if (VSIsWow64()) { + if (VSIsArm64Host()) { + return "ARM64"; + } else if (VSIsWow64()) { return "x64"; } else { +#if defined(_M_ARM) + return "ARM"; +#elif defined(_M_IA64) + return "Itanium"; +#elif defined(_WIN64) + return "x64"; +#else return "Win32"; - } #endif + } } static std::string VSHostArchitecture( cmGlobalVisualStudioGenerator::VSVersion v) { - static_cast<void>(v); -#ifdef HOST_TOOLS_ARCH - return HOST_TOOLS_ARCH(v); -#else - if (VSIsWow64()) { + if (VSIsArm64Host()) { + return v >= cmGlobalVisualStudioGenerator::VSVersion::VS17 ? "ARM64" : ""; + } else if (VSIsWow64()) { return "x64"; } else { +#if defined(_M_ARM) + return ""; +#elif defined(_M_IA64) + return ""; +#elif defined(_WIN64) + return "x64"; +#else return "x86"; - } #endif + } } static unsigned int VSVersionToMajor( @@ -899,17 +946,24 @@ std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand() std::string vs; if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) { if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) { -#if defined(_M_ARM64) - std::string msbuild_arm64 = - vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe"; - if (cmSystemTools::FileExists(msbuild_arm64)) { - return msbuild_arm64; - } -#endif - - msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe"; - if (cmSystemTools::FileExists(msbuild)) { - return msbuild; + if (VSIsArm64Host()) { + if (VSHasDotNETFrameworkArm64()) { + msbuild = vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe"; + if (cmSystemTools::FileExists(msbuild)) { + return msbuild; + } + } + if (VSIsWindows11OrGreater()) { + msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe"; + if (cmSystemTools::FileExists(msbuild)) { + return msbuild; + } + } + } else { + msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe"; + if (cmSystemTools::FileExists(msbuild)) { + return msbuild; + } } } msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe"; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index b1044a8c36..82adca83ca 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2160,11 +2160,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, // These generic args also contain the runtime dependency set std::string runtimeDependencySetArg; std::vector<std::string> runtimeDependencyArgVector; - std::vector<cm::string_view> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr, - &parsedArgs); + genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector); bool success = genericArgs.Finalize(); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 86ae45db95..a4080d80df 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -1343,9 +1343,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } - this->ExportObjectCompileCommand( - language, sourceFilePath, objectDir, objectFileName, objectFileDir, - vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, objectFileName, objectFileDir, + vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + } objBuild.Outputs.push_back(objectFileName); if (firstForConfig) { diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index 31538b6f5b..7e195664e2 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -48,6 +48,12 @@ using options_set = std::set<cm::string_view>; struct UserArgumentParser : public cmArgumentParser<void> { + void BindKeywordsMissingValue(std::vector<cm::string_view>& ref) + { + this->cmArgumentParser<void>::BindKeywordMissingValue( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); + } + template <typename T, typename H> void Bind(std::vector<std::string> const& names, std::map<std::string, T>& ref, H duplicateKey) @@ -211,8 +217,9 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, } std::vector<cm::string_view> keywordsMissingValues; + parser.BindKeywordsMissingValue(keywordsMissingValues); - parser.Parse(list, &unparsed, &keywordsMissingValues); + parser.Parse(list, &unparsed); PassParsedArguments( prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 1514a8aced..40f3ab58d1 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -909,7 +909,6 @@ bool cmQtAutoGenInitializer::InitScanFiles() // The reason is that their file names might be discovered from source files // at generation time. if (this->MocOrUicEnabled()) { - std::unordered_set<std::string> addedFiles; for (const auto& sf : this->Makefile->GetSourceFiles()) { // sf->GetExtension() is only valid after sf->ResolveFullPath() ... // Since we're iterating over source files that might be not in the @@ -951,28 +950,25 @@ bool cmQtAutoGenInitializer::InitScanFiles() cmExpandedList(uicOpts)); } - auto uiHeaderFileName = cmStrCat( - "ui_"_s, cmSystemTools::GetFilenameWithoutLastExtension(fullPath), - ".h"_s); + auto uiHeaderRelativePath = cmSystemTools::RelativePath( + this->LocalGen->GetCurrentSourceDirectory(), + cmSystemTools::GetFilenamePath(fullPath)); - // .ui files with the same base name will conflict. Yield an error. - { - auto insertResult = addedFiles.insert(uiHeaderFileName); - if (!insertResult.second) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("More than one .ui file with the name "_s, - cmSystemTools::GetFilenameName(fullPath), - " was found in the sources for target "_s, - this->GenTarget->GetName(), ".")); - } + // Avoid creating a path containing adjacent slashes + if (!uiHeaderRelativePath.empty() && + uiHeaderRelativePath.back() != '/') { + uiHeaderRelativePath += '/'; } + auto uiHeaderFilePath = cmStrCat( + '/', uiHeaderRelativePath, "ui_"_s, + cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s); + ConfigString uiHeader; std::string uiHeaderGenex; this->ConfigFileNamesAndGenex( uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s), - cmStrCat("/"_s, uiHeaderFileName)); + uiHeaderFilePath); this->Uic.UiHeaders.emplace_back( std::make_pair(uiHeader, uiHeaderGenex)); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 3de45bc7d7..672cdc79b8 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -107,6 +107,10 @@ # include <sys/utsname.h> #endif +#if defined(_MSC_VER) && _MSC_VER >= 1800 +# define CM_WINDOWS_DEPRECATED_GetVersionEx +#endif + namespace { cmSystemTools::InterruptCallback s_InterruptCallback; @@ -904,6 +908,40 @@ cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry() InitWindowsDirectoryRetry().Retry; return retry; } + +cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion() +{ + /* Windows version number data. */ + OSVERSIONINFOEXW osviex; + ZeroMemory(&osviex, sizeof(osviex)); + osviex.dwOSVersionInfoSize = sizeof(osviex); + +# ifdef CM_WINDOWS_DEPRECATED_GetVersionEx +# pragma warning(push) +# ifdef __INTEL_COMPILER +# pragma warning(disable : 1478) +# elif defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# else +# pragma warning(disable : 4996) +# endif +# endif + GetVersionExW((OSVERSIONINFOW*)&osviex); +# ifdef CM_WINDOWS_DEPRECATED_GetVersionEx +# ifdef __clang__ +# pragma clang diagnostic pop +# else +# pragma warning(pop) +# endif +# endif + + WindowsVersion result; + result.dwMajorVersion = osviex.dwMajorVersion; + result.dwMinorVersion = osviex.dwMinorVersion; + result.dwBuildNumber = osviex.dwBuildNumber; + return result; +} #endif std::string cmSystemTools::GetRealPathResolvingWindowsSubst( @@ -3319,22 +3357,12 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName, uv_fs_t req; int flags = 0; #if defined(_WIN32) - bool const isDir = cmsys::SystemTools::FileIsDirectory(origName); - if (isDir) { - flags |= UV_FS_SYMLINK_JUNCTION; + if (cmsys::SystemTools::FileIsDirectory(origName)) { + flags |= UV_FS_SYMLINK_DIR; } #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/cmSystemTools.h b/Source/cmSystemTools.h index 5f7a5ecbb0..ec650f7431 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -509,6 +509,14 @@ public: }; static WindowsFileRetry GetWindowsFileRetry(); static WindowsFileRetry GetWindowsDirectoryRetry(); + + struct WindowsVersion + { + unsigned int dwMajorVersion; + unsigned int dwMinorVersion; + unsigned int dwBuildNumber; + }; + static WindowsVersion GetWindowsVersion(); #endif /** Get the real path for a given path, removing all symlinks. diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index c76e2cb02a..eafea059e1 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1935,6 +1935,51 @@ void cmTarget::AppendBuildInterfaceIncludes() } } +void cmTarget::FinalizeTargetCompileInfo( + const cmBTStringRange& noConfigCompileDefinitions, + cm::optional<std::map<std::string, cmValue>>& perConfigCompileDefinitions) +{ + if (this->GetType() == cmStateEnums::GLOBAL_TARGET) { + return; + } + + this->AppendBuildInterfaceIncludes(); + + if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return; + } + + for (auto const& def : noConfigCompileDefinitions) { + this->InsertCompileDefinition(def); + } + + auto* mf = this->GetMakefile(); + cmPolicies::PolicyStatus polSt = mf->GetPolicyStatus(cmPolicies::CMP0043); + if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { + if (perConfigCompileDefinitions) { + for (auto const& it : *perConfigCompileDefinitions) { + if (cmValue val = it.second) { + this->AppendProperty(it.first, *val); + } + } + } else { + perConfigCompileDefinitions.emplace(); + std::vector<std::string> configs = + mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + + for (std::string const& c : configs) { + std::string defPropName = + cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c)); + cmValue val = mf->GetProperty(defPropName); + (*perConfigCompileDefinitions)[defPropName] = val; + if (val) { + this->AppendProperty(defPropName, *val); + } + } + } + } +} + void cmTarget::InsertInclude(BT<std::string> const& entry, bool before) { auto position = before ? this->impl->IncludeDirectoriesEntries.begin() diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 94d66885a7..3d0a06be0f 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -5,12 +5,15 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> +#include <map> #include <memory> #include <set> #include <string> #include <utility> #include <vector> +#include <cm/optional> + #include "cmAlgorithms.h" #include "cmFileSet.h" #include "cmPolicies.h" @@ -233,6 +236,9 @@ public: void InsertPrecompileHeader(BT<std::string> const& entry); void AppendBuildInterfaceIncludes(); + void FinalizeTargetCompileInfo( + const cmBTStringRange& noConfigCompileDefinitions, + cm::optional<std::map<std::string, cmValue>>& perConfigCompileDefinitions); std::string GetDebugGeneratorExpressions(const std::string& value, cmTargetLinkLibraryType llt) const; diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index 130c2288a8..7514a23769 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -2,34 +2,35 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTryCompileCommand.h" +#include "cmCoreTryCompile.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmake.h" -class cmExecutionStatus; - -// cmTryCompileCommand -bool cmTryCompileCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - if (argv.size() < 3) { + if (args.size() < 3) { return false; } - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( + cmMakefile& mf = status.GetMakefile(); + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The try_compile() command is not supported in --find-package mode."); return false; } - this->TryCompileCode(argv, false); + cmCoreTryCompile tc(&mf); + tc.TryCompileCode(args, false); // if They specified clean then we clean up what we can - if (this->SrcFileSignature) { - if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { - this->CleanupFiles(this->BinaryDirectory); + if (tc.SrcFileSignature) { + if (!mf.GetCMakeInstance()->GetDebugTryCompile()) { + tc.CleanupFiles(tc.BinaryDirectory); } } return true; diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h index d8cc16e59a..6a3430b6b6 100644 --- a/Source/cmTryCompileCommand.h +++ b/Source/cmTryCompileCommand.h @@ -7,33 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryCompileCommand - * \brief Specifies where to install some files - * - * cmTryCompileCommand is used to test if source code can be compiled - */ -class cmTryCompileCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryCompileCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; -}; +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index c82ac6429d..98cacdc65a 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -6,7 +6,9 @@ #include "cmsys/FStream.hxx" +#include "cmCoreTryCompile.h" #include "cmDuration.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmRange.h" @@ -17,24 +19,40 @@ #include "cmValue.h" #include "cmake.h" -class cmExecutionStatus; +namespace { -// cmTryRunCommand -bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +class TryRunCommandImpl : public cmCoreTryCompile { - if (argv.size() < 4) { - return false; - } - - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "The try_run() command is not supported in --find-package mode."); - return false; +public: + TryRunCommandImpl(cmMakefile* mf) + : cmCoreTryCompile(mf) + { } + bool TryRunCode(std::vector<std::string> const& args); + + void RunExecutable(const std::string& runArgs, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + void DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + + std::string CompileResultVariable; + std::string RunResultVariable; + std::string OutputVariable; + std::string RunOutputVariable; + std::string RunOutputStdOutVariable; + std::string RunOutputStdErrVariable; + std::string CompileOutputVariable; + std::string WorkingDirectory; +}; + +bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) +{ // build an arg list for TryCompile and extract the runArgs, std::vector<std::string> tryCompile; @@ -42,6 +60,8 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, this->RunResultVariable.clear(); this->OutputVariable.clear(); this->RunOutputVariable.clear(); + this->RunOutputStdOutVariable.clear(); + this->RunOutputStdErrVariable.clear(); this->CompileOutputVariable.clear(); std::string runArgs; @@ -76,6 +96,22 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, } i++; this->RunOutputVariable = argv[i]; + } else if (argv[i] == "RUN_OUTPUT_STDOUT_VARIABLE") { + if (argv.size() <= (i + 1)) { + cmSystemTools::Error( + "RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable"); + return false; + } + i++; + this->RunOutputStdOutVariable = argv[i]; + } else if (argv[i] == "RUN_OUTPUT_STDERR_VARIABLE") { + if (argv.size() <= (i + 1)) { + cmSystemTools::Error( + "RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable"); + return false; + } + i++; + this->RunOutputStdErrVariable = argv[i]; } else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") { if (argv.size() <= (i + 1)) { cmSystemTools::Error( @@ -102,11 +138,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, // using OUTPUT_VARIABLE makes crosscompiling harder if (!this->OutputVariable.empty() && (!this->RunOutputVariable.empty() || - !this->CompileOutputVariable.empty())) { + !this->CompileOutputVariable.empty() || + !this->RunOutputStdOutVariable.empty() || + !this->RunOutputStdErrVariable.empty())) { cmSystemTools::Error( "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE " - "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or " - "RUN_OUTPUT_VARIABLE."); + ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE. " + "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, " + "RUN_OUTPUT_STDOUT_VARIABLE " + "and/or RUN_OUTPUT_STDERR_VARIABLE."); + return false; + } + + if ((!this->RunOutputStdOutVariable.empty() || + !RunOutputStdErrVariable.empty()) && + !this->RunOutputVariable.empty()) { + cmSystemTools::Error( + "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE together " + "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or " + "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE."); return false; } @@ -119,6 +171,7 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, } bool captureRunOutput = false; + bool captureRunOutputStdOutErr = false; if (!this->OutputVariable.empty()) { captureRunOutput = true; tryCompile.emplace_back("OUTPUT_VARIABLE"); @@ -128,7 +181,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, tryCompile.emplace_back("OUTPUT_VARIABLE"); tryCompile.push_back(this->CompileOutputVariable); } - if (!this->RunOutputVariable.empty()) { + if (!this->RunOutputStdOutVariable.empty() || + !RunOutputStdErrVariable.empty()) { + captureRunOutputStdOutErr = true; + } else if (!this->RunOutputVariable.empty()) { captureRunOutput = true; } @@ -145,12 +201,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, } else { // "run" it and capture the output std::string runOutputContents; + std::string runOutputStdOutContents; + std::string runOutputStdErrContents; if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") && !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) { this->DoNotRunExecutable( - runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr); + runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty() + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty() + ? &runOutputStdErrContents + : nullptr); } else { - this->RunExecutable(runArgs, &runOutputContents); + this->RunExecutable( + runArgs, captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty() + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty() + ? &runOutputStdErrContents + : nullptr); } // now put the output into the variables @@ -158,6 +229,14 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, this->Makefile->AddDefinition(this->RunOutputVariable, runOutputContents); } + if (!this->RunOutputStdOutVariable.empty()) { + this->Makefile->AddDefinition(this->RunOutputStdOutVariable, + runOutputStdOutContents); + } + if (!this->RunOutputStdErrVariable.empty()) { + this->Makefile->AddDefinition(this->RunOutputStdErrVariable, + runOutputStdErrContents); + } if (!this->OutputVariable.empty()) { // if the TryCompileCore saved output in this outputVariable then @@ -179,8 +258,9 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, return true; } -void cmTryRunCommand::RunExecutable(const std::string& runArgs, - std::string* out) +void TryRunCommandImpl::RunExecutable(const std::string& runArgs, + std::string* out, std::string* stdOut, + std::string* stdErr) { int retVal = -1; @@ -204,7 +284,8 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, finalCommand += runArgs; } bool worked = cmSystemTools::RunSingleCommand( - finalCommand, out, out, &retVal, + finalCommand, stdOut || stdErr ? stdOut : out, + stdOut || stdErr ? stdErr : out, &retVal, this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(), cmSystemTools::OUTPUT_NONE, cmDuration::zero()); // set the run var @@ -225,9 +306,11 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, executable, two cache variables are created which will hold the results the executable would have produced. */ -void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* out) +void TryRunCommandImpl::DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string* out, + std::string* stdOut, + std::string* stdErr) { // copy the executable out of the CMakeFiles/ directory, so it is not // removed at the end of try_run() and the user can run it manually @@ -246,6 +329,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, std::string internalRunOutputName = this->RunResultVariable + "__TRYRUN_OUTPUT"; + std::string internalRunOutputStdOutName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT"; + std::string internalRunOutputStdErrName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR"; bool error = false; if (!this->Makefile->GetDefinition(this->RunResultVariable)) { @@ -269,7 +356,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, } // is the output from the executable used ? - if (out) { + if (stdOut || stdErr) { + if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stdout on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdOutName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED", + "1"); + } + + error = true; + } + + if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stderr on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdErrName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED", + "1"); + } + + error = true; + } + } else if (out) { if (!this->Makefile->GetDefinition(internalRunOutputName)) { // if the variables doesn't exist, create it with a helpful error text // and mark it as advanced @@ -317,7 +448,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " to\n" " the exit code (in many cases 0 for success), otherwise " "enter \"FAILED_TO_RUN\".\n"); - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + comment += internalRunOutputStdOutName; + comment += + "\n contains the text the executable " + "would have printed on stdout.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdOutName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + if (stdErr) { + comment += internalRunOutputStdErrName; + comment += + "\n contains the text the executable " + "would have printed on stderr.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdErrName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + } else if (out) { comment += internalRunOutputName; comment += "\n contains the text the executable " @@ -330,6 +488,7 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " check what the source file would have printed when " "called with the given arguments.\n"; } + comment += "The "; comment += this->CompileResultVariable; comment += " variable holds the build result for this try_run().\n\n" @@ -370,7 +529,35 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, return; } - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName); + } + if (stdErr) { + (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName); + } + } else if (out) { (*out) = *this->Makefile->GetDefinition(internalRunOutputName); } } +} + +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 4) { + return false; + } + + cmMakefile& mf = status.GetMakefile(); + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( + MessageType::FATAL_ERROR, + "The try_run() command is not supported in --find-package mode."); + return false; + } + + TryRunCommandImpl tr(&mf); + return tr.TryRunCode(args); +} diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h index d45acd85ec..38e36380e4 100644 --- a/Source/cmTryRunCommand.h +++ b/Source/cmTryRunCommand.h @@ -7,47 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryRunCommand - * \brief Specifies where to install some files - * - * cmTryRunCommand is used to test if source code can be compiled - */ -class cmTryRunCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryRunCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void RunExecutable(const std::string& runArgs, - std::string* runOutputContents); - void DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* runOutputContents); - - std::string CompileResultVariable; - std::string RunResultVariable; - std::string OutputVariable; - std::string RunOutputVariable; - std::string CompileOutputVariable; - std::string WorkingDirectory; -}; +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 020691d059..4c1fa0107f 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -50,24 +50,6 @@ #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); @@ -253,6 +235,31 @@ 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"; + case VsProjectType::proj: + return ".proj"; + default: + return ".vcxproj"; + } +} + +static std::string computeProjectFileExtension(cmGeneratorTarget const* t) +{ + return computeProjectFileExtension(computeProjectType(t)); +} + cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg) : GeneratorTarget(target) @@ -359,10 +366,10 @@ void cmVisualStudio10TargetGenerator::Generate() "by the generator")); } - this->ProjectType = this->ComputeProjectType(this->GeneratorTarget); + this->ProjectType = computeProjectType(this->GeneratorTarget); this->Managed = this->ProjectType == VsProjectType::csproj; const std::string ProjectFileExtension = - getProjectFileExtension(this->ProjectType); + computeProjectFileExtension(this->ProjectType); if (this->ProjectType == VsProjectType::csproj && this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { @@ -423,12 +430,10 @@ void cmVisualStudio10TargetGenerator::Generate() char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; BuildFileStream.write(magic, 3); - if (this->ProjectType == VsProjectType::proj) { - this->WriteZeroCheckProj(BuildFileStream); - } else if (this->ProjectType == VsProjectType::csproj && - this->GeneratorTarget->IsDotNetSdkTarget() && - this->GlobalGenerator->GetVersion() >= - cmGlobalVisualStudioGenerator::VSVersion::VS16) { + if (this->ProjectType == VsProjectType::csproj && + this->GeneratorTarget->IsDotNetSdkTarget() && + this->GlobalGenerator->GetVersion() >= + cmGlobalVisualStudioGenerator::VSVersion::VS16) { this->WriteSdkStyleProjectFile(BuildFileStream); } else { this->WriteClassicMsBuildProjectFile(BuildFileStream); @@ -977,45 +982,6 @@ 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"); @@ -1557,6 +1523,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( this->ASanEnabledConfigurations.end()) { e1.Element("EnableAsan", "true"); } + if (this->FuzzerEnabledConfigurations.find(config) != + this->FuzzerEnabledConfigurations.end()) { + e1.Element("EnableFuzzer", "true"); + } { auto s = this->SpectreMitigation.find(config); if (s != this->SpectreMitigation.end()) { @@ -1735,16 +1705,11 @@ 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::vcxproj) { + if (this->ProjectType != VsProjectType::csproj) { spe1 = cm::make_unique<Elem>(e0, "ItemGroup"); spe2 = cm::make_unique<Elem>(*spe1, "CustomBuild"); this->WriteSource(*spe2, source); @@ -1942,7 +1907,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() // Write out group file std::string path = cmStrCat( this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->Name, - this->ComputeProjectFileExtension(this->GeneratorTarget), ".filters"); + computeProjectFileExtension(this->GeneratorTarget), ".filters"); cmGeneratedFileStream fout(path); fout.SetCopyIfDifferent(true); char magic[] = { char(0xEF), char(0xBB), char(0xBF) }; @@ -2930,7 +2895,7 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( Elem& e0) { cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType(); - if (ttype > cmStateEnums::GLOBAL_TARGET) { + if (ttype > cmStateEnums::INTERFACE_LIBRARY) { return; } if (this->ProjectType == VsProjectType::csproj) { @@ -3097,134 +3062,6 @@ 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 { @@ -3312,10 +3149,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } // Check if ASan is enabled. - if (flags.find("/fsanitize=address") != std::string::npos) { + if (flags.find("/fsanitize=address") != std::string::npos || + flags.find("-fsanitize=address") != std::string::npos) { this->ASanEnabledConfigurations.insert(configName); } + // Check if (lib)Fuzzer is enabled. + if (flags.find("/fsanitize=fuzzer") != std::string::npos || + flags.find("-fsanitize=fuzzer") != std::string::npos) { + this->FuzzerEnabledConfigurations.insert(configName); + } + // Precompile Headers std::string pchHeader = this->GeneratorTarget->GetPchHeader(configName, linkLanguage); @@ -3357,7 +3201,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( // anymore, because cmGeneratorTarget may not be aware that the // target uses C++/CLI. if (flags.find("/clr") != std::string::npos || - defineFlags.find("/clr") != std::string::npos) { + flags.find("-clr") != std::string::npos || + defineFlags.find("/clr") != std::string::npos || + defineFlags.find("-clr") != std::string::npos) { if (configName == this->Configurations[0]) { std::string message = "For the target \"" + this->GeneratorTarget->GetName() + @@ -4746,7 +4592,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) path = *p; } else { path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(), - this->ComputeProjectFileExtension(dt)); + computeProjectFileExtension(dt)); } ConvertToWindowsSlash(path); Elem e2(e1, "ProjectReference"); @@ -5600,26 +5446,6 @@ 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 7a0b8da728..17dcecdb13 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -231,6 +231,7 @@ private: bool TargetCompileAsWinRT; std::set<std::string> IPOEnabledConfigurations; std::set<std::string> ASanEnabledConfigurations; + std::set<std::string> FuzzerEnabledConfigurations; std::map<std::string, std::string> SpectreMitigation; cmGlobalVisualStudio10Generator* const GlobalGenerator; cmLocalVisualStudio10Generator* const LocalGenerator; @@ -264,13 +265,6 @@ 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); @@ -282,7 +276,4 @@ 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/cmake.cxx b/Source/cmake.cxx index efb2520683..12d42b230c 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -406,9 +406,6 @@ void cmake::PrintPresetEnvironment() // Parse the args bool cmake::SetCacheArgs(const std::vector<std::string>& args) { - auto findPackageMode = false; - auto seenScriptOption = false; - auto DefineLambda = [](std::string const& entry, cmake* state) -> bool { std::string var; std::string value; @@ -499,10 +496,10 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) GetProjectCommandsInScriptMode(state->GetState()); // Documented behavior of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be // set to $PWD for -P mode. + state->SetWorkingMode(SCRIPT_MODE); state->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory()); state->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory()); state->ReadListFile(args, path); - seenScriptOption = true; return true; }; @@ -566,12 +563,12 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) "No install directory specified for --install-prefix", CommandArgument::Values::One, PrefixLambda }, CommandArgument{ "--find-package", CommandArgument::Values::Zero, - CommandArgument::setToTrue(findPackageMode) }, + IgnoreAndTrueLambda }, }; for (decltype(args.size()) i = 1; i < args.size(); ++i) { std::string const& arg = args[i]; - if (arg == "--" && seenScriptOption) { + if (arg == "--" && this->GetWorkingMode() == SCRIPT_MODE) { // Stop processing CMake args and avoid possible errors // when arbitrary args are given to CMake script. break; @@ -586,7 +583,7 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) } } - if (findPackageMode) { + if (this->GetWorkingMode() == FIND_PACKAGE_MODE) { return this->FindPackage(args); } @@ -791,7 +788,6 @@ void cmake::SetArgs(const std::vector<std::string>& args) bool haveToolset = false; bool havePlatform = false; bool haveBArg = false; - bool scriptMode = false; std::string possibleUnknownArg; std::string extraProvidedPath; #if !defined(CMAKE_BOOTSTRAP) @@ -874,7 +870,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument{ "-P", "-P must be followed by a file name.", CommandArgument::Values::One, CommandArgument::RequiresSeparator::No, - CommandArgument::setToTrue(scriptMode) }, + IgnoreAndTrueLambda }, CommandArgument{ "-D", "-D must be followed with VAR=VALUE.", CommandArgument::Values::One, CommandArgument::RequiresSeparator::No, @@ -1166,7 +1162,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) // iterate each argument std::string const& arg = args[i]; - if (scriptMode && arg == "--") { + if (this->GetWorkingMode() == SCRIPT_MODE && arg == "--") { // Stop processing CMake args and avoid possible errors // when arbitrary args are given to CMake script. break; @@ -1212,12 +1208,12 @@ void cmake::SetArgs(const std::vector<std::string>& args) } } - if (!extraProvidedPath.empty() && !scriptMode) { + if (!extraProvidedPath.empty() && this->GetWorkingMode() == NORMAL_MODE) { this->IssueMessage(MessageType::WARNING, cmStrCat("Ignoring extra path from command line:\n \"", extraProvidedPath, "\"")); } - if (!possibleUnknownArg.empty() && !scriptMode) { + if (!possibleUnknownArg.empty() && this->GetWorkingMode() != SCRIPT_MODE) { cmSystemTools::Error(cmStrCat("Unknown argument ", possibleUnknownArg)); cmSystemTools::Error("Run 'cmake --help' for all supported options."); exit(1); @@ -1830,7 +1826,8 @@ void cmake::SetHomeDirectoryViaCommandLine(std::string const& path) } auto prev_path = this->GetHomeDirectory(); - if (prev_path != path && !prev_path.empty()) { + if (prev_path != path && !prev_path.empty() && + this->GetWorkingMode() == NORMAL_MODE) { this->IssueMessage(MessageType::WARNING, cmStrCat("Ignoring extra path from command line:\n \"", prev_path, "\"")); diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 97c275efc2..f931e9dbcd 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -207,7 +207,7 @@ int do_cmake(int ac, char const* const* av) #ifndef CMAKE_BOOTSTRAP cmDocumentation doc; doc.addCMakeStandardDocSections(); - if (doc.CheckOptions(ac, av)) { + if (doc.CheckOptions(ac, av, "--")) { // Construct and print requested documentation. cmake hcm(cmake::RoleInternal, cmState::Unknown); hcm.SetHomeDirectory(""); diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 8921aa0ca1..69eb19e2f0 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -273,6 +273,7 @@ int main() std::string clrest = rest; // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj clrest = replace(clrest, "/fo ", "/out:"); + clrest = replace(clrest, "-fo ", "-out:"); clrest = replace(clrest, objfile, objfile + ".dep.obj "); cl = "\"" + cl + "\" /P /DRC_INVOKED /TC "; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9ab39f1ebe..b1f1bcfe3c 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -2242,13 +2242,18 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, // Parse the link command to extract information we need. for (; arg != argEnd; ++arg) { if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0 || - cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) { + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:YES") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) { this->Incremental = true; - } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) { + } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) { this->LinkGeneratesManifest = false; - } else if (cmHasLiteralPrefix(*arg, "/Fe")) { + } else if (cmHasLiteralPrefix(*arg, "/Fe") || + cmHasLiteralPrefix(*arg, "-Fe")) { this->TargetFile = arg->substr(3); - } else if (cmHasLiteralPrefix(*arg, "/out:")) { + } else if (cmHasLiteralPrefix(*arg, "/out:") || + cmHasLiteralPrefix(*arg, "-out:")) { this->TargetFile = arg->substr(5); } } diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index d520c14404..f239576efe 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -43,12 +43,12 @@ public: { std::string Name; #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t FindData; + WIN32_FIND_DATAW FindData; #endif FileData(std::string name #if defined(_WIN32) && !defined(__CYGWIN__) , - _wfinddata_t data + WIN32_FIND_DATAW data #endif ) : Name(std::move(name)) @@ -115,8 +115,8 @@ std::string Directory::GetFilePath(std::size_t i) const bool Directory::FileIsDirectory(std::size_t i) const { #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; - return (data.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + auto const& data = this->Internal->Files[i].FindData; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; #else std::string const& path = this->GetFilePath(i); return kwsys::SystemTools::FileIsDirectory(path); @@ -127,9 +127,9 @@ bool Directory::FileIsSymlink(std::size_t i) const { std::string const& path = this->GetFilePath(i); #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; + auto const& data = this->Internal->Files[i].FindData; return kwsys::SystemTools::FileIsSymlinkWithAttr( - Encoding::ToWindowsExtendedPath(path), data.attrib); + Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes); #else return kwsys::SystemTools::FileIsSymlink(path); #endif @@ -157,7 +157,7 @@ namespace KWSYS_NAMESPACE { Status Directory::Load(std::string const& name, std::string* errorMessage) { this->Clear(); - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -176,14 +176,14 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) snprintf(buf, bufLength, "%s/*", name.c_str()); } } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array srchHandle = - _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data); + FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -193,10 +193,11 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) // Loop through names do { - this->Internal->Files.emplace_back(Encoding::ToNarrow(data.name), data); - } while (_wfindnext(srchHandle, &data) != -1); + this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName), + data); + } while (FindNextFileW(srchHandle, &data)); this->Internal->Path = name; - if (_findclose(srchHandle) == -1) { + if (!FindClose(srchHandle)) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -209,7 +210,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, std::string* errorMessage) { - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -222,13 +223,13 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, buf = new char[n + 2 + 1]; snprintf(buf, bufLength, "%s/*", name.c_str()); } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array - srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data); + srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { if (errorMessage) { if (unsigned int errorId = GetLastError()) { LPSTR message = nullptr; @@ -250,8 +251,8 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, unsigned long count = 0; do { count++; - } while (_wfindnext(srchHandle, &data) != -1); - _findclose(srchHandle); + } while (FindNextFileW(srchHandle, &data)); + FindClose(srchHandle); return count; } diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 89a3cc789f..a20901cb29 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -536,9 +536,11 @@ public: StringMap TranslationMap; #endif #ifdef _WIN32 - static std::string GetCasePathName(std::string const& pathIn); + static std::string GetCasePathName(std::string const& pathIn, + bool const cache); static std::string GetActualCaseForPathCached(std::string const& path); static const char* GetEnvBuffered(const char* key); + std::map<std::string, std::string, SystemToolsPathCaseCmp> FindFileMap; std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap; std::map<std::string, std::string> EnvMap; #endif @@ -571,7 +573,8 @@ public: static SystemToolsStatic* SystemToolsStatics; #ifdef _WIN32 -std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, + bool const cache) { std::string casePath; @@ -623,14 +626,31 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) } else { std::string test_str = casePath; test_str += path_components[idx]; - WIN32_FIND_DATAW findData; - HANDLE hFind = - ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - path_components[idx] = Encoding::ToNarrow(findData.cFileName); - ::FindClose(hFind); - } else { - converting = false; + + bool found_in_cache = false; + if (cache) { + auto const it = SystemToolsStatics->FindFileMap.find(test_str); + if (it != SystemToolsStatics->FindFileMap.end()) { + path_components[idx] = it->second; + found_in_cache = true; + } + } + + if (!found_in_cache) { + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + auto case_file_name = Encoding::ToNarrow(findData.cFileName); + if (cache) { + SystemToolsStatics->FindFileMap.emplace(test_str, + case_file_name); + } + path_components[idx] = std::move(case_file_name); + ::FindClose(hFind); + } else { + converting = false; + } } } } @@ -642,19 +662,16 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) { - // Check to see if actual case has already been called - // for this path, and the result is stored in the PathCaseMap - auto& pcm = SystemToolsStatics->PathCaseMap; - { - auto itr = pcm.find(p); - if (itr != pcm.end()) { - return itr->second; - } - } - std::string casePath = SystemToolsStatic::GetCasePathName(p); - if (casePath.size() <= MAX_PATH) { - pcm[p] = casePath; + std::string casePath; + + auto it = SystemToolsStatics->PathCaseMap.find(p); + if (it != SystemToolsStatics->PathCaseMap.end()) { + casePath = it->second; + } else { + casePath = SystemToolsStatic::GetCasePathName(p, true); + SystemToolsStatics->PathCaseMap.emplace(p, casePath); } + return casePath; } #endif @@ -3677,7 +3694,7 @@ std::string SystemTools::RelativePath(const std::string& local, std::string SystemTools::GetActualCaseForPath(const std::string& p) { #ifdef _WIN32 - return SystemToolsStatic::GetCasePathName(p); + return SystemToolsStatic::GetCasePathName(p, false); #else return p; #endif |