1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmConditionEvaluator.h"
14 #include <cm/string_view>
15 #include <cmext/algorithm>
17 #include "cmsys/RegularExpression.hxx"
19 #include "cmCMakePath.h"
20 #include "cmExpandedCommandArgument.h"
21 #include "cmMakefile.h"
22 #include "cmMessageType.h"
24 #include "cmStringAlgorithms.h"
25 #include "cmSystemTools.h"
30 auto const keyAND = "AND"_s;
31 auto const keyCOMMAND = "COMMAND"_s;
32 auto const keyDEFINED = "DEFINED"_s;
33 auto const keyEQUAL = "EQUAL"_s;
34 auto const keyEXISTS = "EXISTS"_s;
35 auto const keyGREATER = "GREATER"_s;
36 auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
37 auto const keyIN_LIST = "IN_LIST"_s;
38 auto const keyIS_ABSOLUTE = "IS_ABSOLUTE"_s;
39 auto const keyIS_DIRECTORY = "IS_DIRECTORY"_s;
40 auto const keyIS_NEWER_THAN = "IS_NEWER_THAN"_s;
41 auto const keyIS_SYMLINK = "IS_SYMLINK"_s;
42 auto const keyLESS = "LESS"_s;
43 auto const keyLESS_EQUAL = "LESS_EQUAL"_s;
44 auto const keyMATCHES = "MATCHES"_s;
45 auto const keyNOT = "NOT"_s;
46 auto const keyOR = "OR"_s;
47 auto const keyParenL = "("_s;
48 auto const keyParenR = ")"_s;
49 auto const keyPOLICY = "POLICY"_s;
50 auto const keySTREQUAL = "STREQUAL"_s;
51 auto const keySTRGREATER = "STRGREATER"_s;
52 auto const keySTRGREATER_EQUAL = "STRGREATER_EQUAL"_s;
53 auto const keySTRLESS = "STRLESS"_s;
54 auto const keySTRLESS_EQUAL = "STRLESS_EQUAL"_s;
55 auto const keyTARGET = "TARGET"_s;
56 auto const keyTEST = "TEST"_s;
57 auto const keyVERSION_EQUAL = "VERSION_EQUAL"_s;
58 auto const keyVERSION_GREATER = "VERSION_GREATER"_s;
59 auto const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL"_s;
60 auto const keyVERSION_LESS = "VERSION_LESS"_s;
61 auto const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL"_s;
62 auto const keyPATH_EQUAL = "PATH_EQUAL"_s;
64 cmSystemTools::CompareOp const MATCH2CMPOP[5] = {
65 cmSystemTools::OP_LESS, cmSystemTools::OP_LESS_EQUAL,
66 cmSystemTools::OP_GREATER, cmSystemTools::OP_GREATER_EQUAL,
67 cmSystemTools::OP_EQUAL
70 // Run-Time to Compile-Time template selector
71 template <template <typename> class Comp, template <typename> class... Ops>
72 struct cmRt2CtSelector
75 static bool eval(int r, T lhs, T rhs)
81 return Comp<T>()(lhs, rhs);
83 return cmRt2CtSelector<Ops...>::eval(r - 1, lhs, rhs);
88 template <template <typename> class Comp>
89 struct cmRt2CtSelector<Comp>
92 static bool eval(int r, T lhs, T rhs)
94 return r == 1 && Comp<T>()(lhs, rhs);
98 std::string bool2string(bool const value)
100 return std::string(static_cast<std::size_t>(1),
101 static_cast<char>('0' + static_cast<int>(value)));
104 bool looksLikeSpecialVariable(const std::string& var,
105 cm::static_string_view prefix,
106 const std::size_t varNameLen)
108 // NOTE Expecting a variable name at least 1 char length:
109 // <prefix> + `{` + <varname> + `}`
110 return ((prefix.size() + 3) <= varNameLen) &&
111 cmHasPrefix(var, cmStrCat(prefix, '{')) && var[varNameLen - 1] == '}';
113 } // anonymous namespace
115 #if defined(__SUNPRO_CC)
116 # define CM_INHERIT_CTOR(Class, Base, Tpl) \
117 template <typename... Args> \
118 Class(Args&&... args) \
119 : Base Tpl(std::forward<Args>(args)...) \
123 # define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base
126 // BEGIN cmConditionEvaluator::cmArgumentList
127 class cmConditionEvaluator::cmArgumentList
128 : public std::list<cmExpandedCommandArgument>
130 using base_t = std::list<cmExpandedCommandArgument>;
133 CM_INHERIT_CTOR(cmArgumentList, list, <cmExpandedCommandArgument>);
135 class CurrentAndNextIter
137 friend class cmConditionEvaluator::cmArgumentList;
140 base_t::iterator current;
141 base_t::iterator next;
143 CurrentAndNextIter advance(base_t& args)
145 this->current = std::next(this->current);
147 std::next(this->current,
148 static_cast<difference_type>(this->current != args.end()));
153 CurrentAndNextIter(base_t& args)
154 : current(args.begin())
156 std::next(this->current,
157 static_cast<difference_type>(this->current != args.end())))
162 class CurrentAndTwoMoreIter
164 friend class cmConditionEvaluator::cmArgumentList;
167 base_t::iterator current;
168 base_t::iterator next;
169 base_t::iterator nextnext;
171 CurrentAndTwoMoreIter advance(base_t& args)
173 this->current = std::next(this->current);
175 std::next(this->current,
176 static_cast<difference_type>(this->current != args.end()));
177 this->nextnext = std::next(
178 this->next, static_cast<difference_type>(this->next != args.end()));
183 CurrentAndTwoMoreIter(base_t& args)
184 : current(args.begin())
186 std::next(this->current,
187 static_cast<difference_type>(this->current != args.end())))
188 , nextnext(std::next(
189 this->next, static_cast<difference_type>(this->next != args.end())))
194 CurrentAndNextIter make2ArgsIterator() { return *this; }
195 CurrentAndTwoMoreIter make3ArgsIterator() { return *this; }
197 template <typename Iter>
198 void ReduceOneArg(const bool value, Iter args)
200 *args.current = cmExpandedCommandArgument(bool2string(value), true);
201 this->erase(args.next);
204 void ReduceTwoArgs(const bool value, CurrentAndTwoMoreIter args)
206 *args.current = cmExpandedCommandArgument(bool2string(value), true);
207 this->erase(args.nextnext);
208 this->erase(args.next);
212 // END cmConditionEvaluator::cmArgumentList
214 cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile,
215 cmListFileBacktrace bt)
217 , Backtrace(std::move(bt))
218 , Policy12Status(makefile.GetPolicyStatus(cmPolicies::CMP0012))
219 , Policy54Status(makefile.GetPolicyStatus(cmPolicies::CMP0054))
220 , Policy57Status(makefile.GetPolicyStatus(cmPolicies::CMP0057))
221 , Policy64Status(makefile.GetPolicyStatus(cmPolicies::CMP0064))
222 , Policy139Status(makefile.GetPolicyStatus(cmPolicies::CMP0139))
226 //=========================================================================
227 // order of operations,
228 // 1. ( ) -- parenthetical groups
229 // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
230 // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
234 // There is an issue on whether the arguments should be values of references,
235 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
236 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
237 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
238 // take numeric values or variable names. STRLESS and STRGREATER take
239 // variable names but if the variable name is not found it will use the name
240 // directly. AND OR take variables or the values 0 or 1.
242 bool cmConditionEvaluator::IsTrue(
243 const std::vector<cmExpandedCommandArgument>& args, std::string& errorString,
248 // handle empty invocation
253 // store the reduced args in this vector
254 cmArgumentList newArgs(args.begin(), args.end());
256 // now loop through the arguments and see if we can reduce any of them
257 // we do this multiple times. Once for each level of precedence
259 using handlerFn_t = bool (cmConditionEvaluator::*)(
260 cmArgumentList&, std::string&, MessageType&);
261 const std::array<handlerFn_t, 5> handlers = { {
262 &cmConditionEvaluator::HandleLevel0, // parenthesis
263 &cmConditionEvaluator::HandleLevel1, // predicates
264 &cmConditionEvaluator::HandleLevel2, // binary ops
265 &cmConditionEvaluator::HandleLevel3, // NOT
266 &cmConditionEvaluator::HandleLevel4 // AND OR
268 for (auto fn : handlers) {
269 // Call the reducer 'till there is anything to reduce...
270 // (i.e., if after an iteration the size becomes smaller)
271 auto levelResult = true;
272 for (auto beginSize = newArgs.size();
273 (levelResult = (this->*fn)(newArgs, errorString, status)) &&
274 newArgs.size() < beginSize;
275 beginSize = newArgs.size()) {
279 // NOTE `errorString` supposed to be set already
284 // now at the end there should only be one argument left
285 if (newArgs.size() != 1) {
286 errorString = "Unknown arguments specified";
287 status = MessageType::FATAL_ERROR;
291 return this->GetBooleanValueWithAutoDereference(newArgs.front(), errorString,
295 //=========================================================================
296 cmValue cmConditionEvaluator::GetDefinitionIfUnquoted(
297 cmExpandedCommandArgument const& argument) const
299 if ((this->Policy54Status != cmPolicies::WARN &&
300 this->Policy54Status != cmPolicies::OLD) &&
301 argument.WasQuoted()) {
305 cmValue def = this->Makefile.GetDefinition(argument.GetValue());
307 if (def && argument.WasQuoted() &&
308 this->Policy54Status == cmPolicies::WARN) {
309 if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) {
310 std::ostringstream e;
312 e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054))
314 "Quoted variables like \"" << argument.GetValue() << "\" "
315 "will no longer be dereferenced when the policy is set to NEW. "
316 "Since the policy is not set the OLD behavior will be used.";
319 this->Makefile.GetCMakeInstance()->IssueMessage(
320 MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
327 //=========================================================================
328 cmValue cmConditionEvaluator::GetVariableOrString(
329 const cmExpandedCommandArgument& argument) const
331 cmValue def = this->GetDefinitionIfUnquoted(argument);
334 def = cmValue(argument.GetValue());
340 //=========================================================================
341 bool cmConditionEvaluator::IsKeyword(
342 cm::static_string_view keyword,
343 const cmExpandedCommandArgument& argument) const
345 if ((this->Policy54Status != cmPolicies::WARN &&
346 this->Policy54Status != cmPolicies::OLD) &&
347 argument.WasQuoted()) {
351 const auto isKeyword = argument.GetValue() == keyword;
353 if (isKeyword && argument.WasQuoted() &&
354 this->Policy54Status == cmPolicies::WARN) {
355 if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) {
356 std::ostringstream e;
358 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)
360 "Quoted keywords like \"" << argument.GetValue() << "\" "
361 "will no longer be interpreted as keywords "
362 "when the policy is set to NEW. "
363 "Since the policy is not set the OLD behavior will be used.";
366 this->Makefile.GetCMakeInstance()->IssueMessage(
367 MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
374 //=========================================================================
375 bool cmConditionEvaluator::GetBooleanValue(
376 cmExpandedCommandArgument& arg) const
378 // Check basic and named constants.
379 if (cmIsOn(arg.GetValue())) {
382 if (cmIsOff(arg.GetValue())) {
386 // Check for numbers.
389 const double d = std::strtod(arg.GetValue().c_str(), &end);
391 // The whole string is a number. Use C conversion to bool.
392 return static_cast<bool>(d);
397 cmValue def = this->GetDefinitionIfUnquoted(arg);
398 return !cmIsOff(def);
401 //=========================================================================
402 // Boolean value behavior from CMake 2.6.4 and below.
403 bool cmConditionEvaluator::GetBooleanValueOld(
404 cmExpandedCommandArgument const& arg, bool const one) const
407 // Old IsTrue behavior for single argument.
414 cmValue def = this->GetDefinitionIfUnquoted(arg);
415 return !cmIsOff(def);
417 // Old GetVariableOrNumber behavior.
418 cmValue def = this->GetDefinitionIfUnquoted(arg);
419 if (!def && std::atoi(arg.GetValue().c_str())) {
420 def = cmValue(arg.GetValue());
422 return !cmIsOff(def);
425 //=========================================================================
426 // returns the resulting boolean value
427 bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
428 cmExpandedCommandArgument& newArg, std::string& errorString,
429 MessageType& status, bool const oneArg) const
431 // Use the policy if it is set.
432 if (this->Policy12Status == cmPolicies::NEW) {
433 return this->GetBooleanValue(newArg);
435 if (this->Policy12Status == cmPolicies::OLD) {
436 return this->GetBooleanValueOld(newArg, oneArg);
439 // Check policy only if old and new results differ.
440 const auto newResult = this->GetBooleanValue(newArg);
441 const auto oldResult = this->GetBooleanValueOld(newArg, oneArg);
442 if (newResult != oldResult) {
443 switch (this->Policy12Status) {
444 case cmPolicies::WARN:
445 errorString = "An argument named \"" + newArg.GetValue() +
446 "\" appears in a conditional statement. " +
447 cmPolicies::GetPolicyWarning(cmPolicies::CMP0012);
448 status = MessageType::AUTHOR_WARNING;
450 case cmPolicies::OLD:
452 case cmPolicies::REQUIRED_IF_USED:
453 case cmPolicies::REQUIRED_ALWAYS: {
454 errorString = "An argument named \"" + newArg.GetValue() +
455 "\" appears in a conditional statement. " +
456 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0012);
457 status = MessageType::FATAL_ERROR;
460 case cmPolicies::NEW:
468 inline int cmConditionEvaluator::matchKeysImpl(
469 const cmExpandedCommandArgument&)
471 // Zero means "not found"
475 template <int N, typename T, typename... Keys>
476 inline int cmConditionEvaluator::matchKeysImpl(
477 const cmExpandedCommandArgument& arg, T current, Keys... key)
479 if (this->IsKeyword(current, arg)) {
480 // Stop searching as soon as smth has found
483 return matchKeysImpl<N + 1>(arg, key...);
486 template <typename... Keys>
487 inline int cmConditionEvaluator::matchKeys(
488 const cmExpandedCommandArgument& arg, Keys... key)
490 // Get index of the matched key (1-based)
491 return matchKeysImpl<1>(arg, key...);
494 //=========================================================================
495 // level 0 processes parenthetical expressions
496 bool cmConditionEvaluator::HandleLevel0(cmArgumentList& newArgs,
497 std::string& errorString,
500 for (auto arg = newArgs.begin(); arg != newArgs.end(); ++arg) {
501 if (this->IsKeyword(keyParenL, *arg)) {
502 // search for the closing paren for this opening one
504 auto argClose = std::next(arg);
505 for (; argClose != newArgs.end() && depth; ++argClose) {
506 depth += int(this->IsKeyword(keyParenL, *argClose)) -
507 int(this->IsKeyword(keyParenR, *argClose));
510 errorString = "mismatched parenthesis in condition";
511 status = MessageType::FATAL_ERROR;
515 // store the reduced args in this vector
516 auto argOpen = std::next(arg);
517 const std::vector<cmExpandedCommandArgument> subExpr(
518 argOpen, std::prev(argClose));
520 // now recursively invoke IsTrue to handle the values inside the
521 // parenthetical expression
522 const auto value = this->IsTrue(subExpr, errorString, status);
523 *arg = cmExpandedCommandArgument(bool2string(value), true);
524 argOpen = std::next(arg);
525 // remove the now evaluated parenthetical expression
526 newArgs.erase(argOpen, argClose);
532 //=========================================================================
533 // level one handles most predicates except for NOT
534 bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
537 for (auto args = newArgs.make2ArgsIterator(); args.current != newArgs.end();
538 args.advance(newArgs)) {
540 auto policyCheck = [&, this](const cmPolicies::PolicyID id,
541 const cmPolicies::PolicyStatus status,
542 const cm::static_string_view kw) {
543 if (status == cmPolicies::WARN && this->IsKeyword(kw, *args.current)) {
544 std::ostringstream e;
545 e << cmPolicies::GetPolicyWarning(id) << "\n"
547 << " will be interpreted as an operator "
548 "when the policy is set to NEW. "
549 "Since the policy is not set the OLD behavior will be used.";
551 this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
555 // NOTE Checking policies for warnings are not require an access to the
556 // next arg. Check them first!
557 policyCheck(cmPolicies::CMP0064, this->Policy64Status, keyTEST);
559 // NOTE Fail fast: All the predicates below require the next arg to be
561 if (args.next == newArgs.end()) {
566 if (this->IsKeyword(keyEXISTS, *args.current)) {
567 newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
570 // does a directory with this name exist
571 else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
572 newArgs.ReduceOneArg(
573 cmSystemTools::FileIsDirectory(args.next->GetValue()), args);
575 // does a symlink with this name exist
576 else if (this->IsKeyword(keyIS_SYMLINK, *args.current)) {
577 newArgs.ReduceOneArg(cmSystemTools::FileIsSymlink(args.next->GetValue()),
580 // is the given path an absolute path ?
581 else if (this->IsKeyword(keyIS_ABSOLUTE, *args.current)) {
582 newArgs.ReduceOneArg(
583 cmSystemTools::FileIsFullPath(args.next->GetValue()), args);
585 // does a command exist
586 else if (this->IsKeyword(keyCOMMAND, *args.current)) {
587 newArgs.ReduceOneArg(
589 this->Makefile.GetState()->GetCommand(args.next->GetValue())),
592 // does a policy exist
593 else if (this->IsKeyword(keyPOLICY, *args.current)) {
594 cmPolicies::PolicyID pid;
595 newArgs.ReduceOneArg(
596 cmPolicies::GetPolicyID(args.next->GetValue().c_str(), pid), args);
598 // does a target exist
599 else if (this->IsKeyword(keyTARGET, *args.current)) {
600 newArgs.ReduceOneArg(static_cast<bool>(this->Makefile.FindTargetToUse(
601 args.next->GetValue())),
604 // is a variable defined
605 else if (this->IsKeyword(keyDEFINED, *args.current)) {
606 const auto& var = args.next->GetValue();
607 const auto varNameLen = var.size();
610 if (looksLikeSpecialVariable(var, "ENV"_s, varNameLen)) {
611 const auto env = args.next->GetValue().substr(4, varNameLen - 5);
612 result = cmSystemTools::HasEnv(env);
615 else if (looksLikeSpecialVariable(var, "CACHE"_s, varNameLen)) {
616 const auto cache = args.next->GetValue().substr(6, varNameLen - 7);
617 result = static_cast<bool>(
618 this->Makefile.GetState()->GetCacheEntryValue(cache));
622 result = this->Makefile.IsDefinitionSet(args.next->GetValue());
624 newArgs.ReduceOneArg(result, args);
627 else if (this->IsKeyword(keyTEST, *args.current)) {
628 if (this->Policy64Status == cmPolicies::OLD ||
629 this->Policy64Status == cmPolicies::WARN) {
632 newArgs.ReduceOneArg(
633 static_cast<bool>(this->Makefile.GetTest(args.next->GetValue())),
640 //=========================================================================
641 // level two handles most binary operations except for AND OR
642 bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs,
643 std::string& errorString,
646 for (auto args = newArgs.make3ArgsIterator(); args.current != newArgs.end();
647 args.advance(newArgs)) {
651 // NOTE Handle special case `if(... BLAH_BLAH MATCHES)`
652 // (i.e., w/o regex to match which is possibly result of
653 // variable expansion to an empty string)
654 if (args.next != newArgs.end() &&
655 this->IsKeyword(keyMATCHES, *args.current)) {
656 newArgs.ReduceOneArg(false, args);
659 // NOTE Fail fast: All the binary ops below require 2 arguments.
660 else if (args.next == newArgs.end() || args.nextnext == newArgs.end()) {
664 else if (this->IsKeyword(keyMATCHES, *args.next)) {
665 cmValue def = this->GetDefinitionIfUnquoted(*args.current);
669 def = cmValue(args.current->GetValue());
670 } else if (cmHasLiteralPrefix(args.current->GetValue(),
672 // The string to match is owned by our match result variables.
673 // Move it to our own buffer before clearing them.
675 def = cmValue(def_buf);
678 this->Makefile.ClearMatches();
680 const auto& rex = args.nextnext->GetValue();
681 cmsys::RegularExpression regEntry;
682 if (!regEntry.compile(rex)) {
683 std::ostringstream error;
684 error << "Regular expression \"" << rex << "\" cannot compile";
685 errorString = error.str();
686 status = MessageType::FATAL_ERROR;
690 const auto match = regEntry.find(*def);
692 this->Makefile.StoreMatches(regEntry);
694 newArgs.ReduceTwoArgs(match, args);
698 this->matchKeys(*args.next, keyLESS, keyLESS_EQUAL, keyGREATER,
699 keyGREATER_EQUAL, keyEQUAL))) {
701 cmValue ldef = this->GetVariableOrString(*args.current);
702 cmValue rdef = this->GetVariableOrString(*args.nextnext);
706 auto parseDoubles = [&]() {
707 return std::sscanf(ldef->c_str(), "%lg", &lhs) == 1 &&
708 std::sscanf(rdef->c_str(), "%lg", &rhs) == 1;
711 const auto result = parseDoubles() &&
713 std::less, std::less_equal,
714 std::greater, std::greater_equal,
716 >::eval(matchNo, lhs, rhs);
718 newArgs.ReduceTwoArgs(result, args);
721 else if ((matchNo = this->matchKeys(*args.next, keySTRLESS,
722 keySTRLESS_EQUAL, keySTRGREATER,
723 keySTRGREATER_EQUAL, keySTREQUAL))) {
725 const cmValue lhs = this->GetVariableOrString(*args.current);
726 const cmValue rhs = this->GetVariableOrString(*args.nextnext);
727 const auto val = (*lhs).compare(*rhs);
729 const auto result = cmRt2CtSelector<
730 std::less, std::less_equal,
731 std::greater, std::greater_equal,
733 >::eval(matchNo, val, 0);
735 newArgs.ReduceTwoArgs(result, args);
739 this->matchKeys(*args.next, keyVERSION_LESS,
740 keyVERSION_LESS_EQUAL, keyVERSION_GREATER,
741 keyVERSION_GREATER_EQUAL, keyVERSION_EQUAL))) {
742 const auto op = MATCH2CMPOP[matchNo - 1];
743 const std::string& lhs = this->GetVariableOrString(*args.current);
744 const std::string& rhs = this->GetVariableOrString(*args.nextnext);
745 const auto result = cmSystemTools::VersionCompare(op, lhs, rhs);
746 newArgs.ReduceTwoArgs(result, args);
749 // is file A newer than file B
750 else if (this->IsKeyword(keyIS_NEWER_THAN, *args.next)) {
751 auto fileIsNewer = 0;
752 cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
753 args.current->GetValue(), args.nextnext->GetValue(), &fileIsNewer);
754 newArgs.ReduceTwoArgs(
755 (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), args);
758 else if (this->IsKeyword(keyIN_LIST, *args.next)) {
760 if (this->Policy57Status != cmPolicies::OLD &&
761 this->Policy57Status != cmPolicies::WARN) {
763 cmValue lhs = this->GetVariableOrString(*args.current);
764 cmValue rhs = this->Makefile.GetDefinition(args.nextnext->GetValue());
766 newArgs.ReduceTwoArgs(
767 rhs && cm::contains(cmExpandedList(*rhs, true), *lhs), args);
770 else if (this->Policy57Status == cmPolicies::WARN) {
771 std::ostringstream e;
772 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0057)
774 "IN_LIST will be interpreted as an operator "
775 "when the policy is set to NEW. "
776 "Since the policy is not set the OLD behavior will be used.";
778 this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
782 else if (this->IsKeyword(keyPATH_EQUAL, *args.next)) {
784 if (this->Policy139Status != cmPolicies::OLD &&
785 this->Policy139Status != cmPolicies::WARN) {
787 cmValue lhs = this->GetVariableOrString(*args.current);
788 cmValue rhs = this->GetVariableOrString(*args.nextnext);
789 const auto result = cmCMakePath{ *lhs } == cmCMakePath{ *rhs };
790 newArgs.ReduceTwoArgs(result, args);
793 else if (this->Policy139Status == cmPolicies::WARN) {
794 std::ostringstream e;
795 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0139)
797 "PATH_EQUAL will be interpreted as an operator "
798 "when the policy is set to NEW. "
799 "Since the policy is not set the OLD behavior will be used.";
801 this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
808 //=========================================================================
809 // level 3 handles NOT
810 bool cmConditionEvaluator::HandleLevel3(cmArgumentList& newArgs,
811 std::string& errorString,
814 for (auto args = newArgs.make2ArgsIterator(); args.next != newArgs.end();
815 args.advance(newArgs)) {
816 if (this->IsKeyword(keyNOT, *args.current)) {
817 const auto rhs = this->GetBooleanValueWithAutoDereference(
818 *args.next, errorString, status);
819 newArgs.ReduceOneArg(!rhs, args);
825 //=========================================================================
826 // level 4 handles AND OR
827 bool cmConditionEvaluator::HandleLevel4(cmArgumentList& newArgs,
828 std::string& errorString,
831 for (auto args = newArgs.make3ArgsIterator(); args.nextnext != newArgs.end();
832 args.advance(newArgs)) {
836 if ((matchNo = this->matchKeys(*args.next, keyAND, keyOR))) {
837 const auto lhs = this->GetBooleanValueWithAutoDereference(
838 *args.current, errorString, status);
839 const auto rhs = this->GetBooleanValueWithAutoDereference(
840 *args.nextnext, errorString, status);
844 std::logical_and, std::logical_or
845 >::eval(matchNo, lhs, rhs);
847 newArgs.ReduceTwoArgs(result, args);