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"
22 #include "cmMakefile.h"
23 #include "cmMessageType.h"
25 #include "cmStringAlgorithms.h"
26 #include "cmSystemTools.h"
31 auto const keyAND
= "AND"_s
;
32 auto const keyCOMMAND
= "COMMAND"_s
;
33 auto const keyDEFINED
= "DEFINED"_s
;
34 auto const keyEQUAL
= "EQUAL"_s
;
35 auto const keyEXISTS
= "EXISTS"_s
;
36 auto const keyIS_READABLE
= "IS_READABLE"_s
;
37 auto const keyIS_WRITABLE
= "IS_WRITABLE"_s
;
38 auto const keyIS_EXECUTABLE
= "IS_EXECUTABLE"_s
;
39 auto const keyGREATER
= "GREATER"_s
;
40 auto const keyGREATER_EQUAL
= "GREATER_EQUAL"_s
;
41 auto const keyIN_LIST
= "IN_LIST"_s
;
42 auto const keyIS_ABSOLUTE
= "IS_ABSOLUTE"_s
;
43 auto const keyIS_DIRECTORY
= "IS_DIRECTORY"_s
;
44 auto const keyIS_NEWER_THAN
= "IS_NEWER_THAN"_s
;
45 auto const keyIS_SYMLINK
= "IS_SYMLINK"_s
;
46 auto const keyLESS
= "LESS"_s
;
47 auto const keyLESS_EQUAL
= "LESS_EQUAL"_s
;
48 auto const keyMATCHES
= "MATCHES"_s
;
49 auto const keyNOT
= "NOT"_s
;
50 auto const keyOR
= "OR"_s
;
51 auto const keyParenL
= "("_s
;
52 auto const keyParenR
= ")"_s
;
53 auto const keyPOLICY
= "POLICY"_s
;
54 auto const keySTREQUAL
= "STREQUAL"_s
;
55 auto const keySTRGREATER
= "STRGREATER"_s
;
56 auto const keySTRGREATER_EQUAL
= "STRGREATER_EQUAL"_s
;
57 auto const keySTRLESS
= "STRLESS"_s
;
58 auto const keySTRLESS_EQUAL
= "STRLESS_EQUAL"_s
;
59 auto const keyTARGET
= "TARGET"_s
;
60 auto const keyTEST
= "TEST"_s
;
61 auto const keyVERSION_EQUAL
= "VERSION_EQUAL"_s
;
62 auto const keyVERSION_GREATER
= "VERSION_GREATER"_s
;
63 auto const keyVERSION_GREATER_EQUAL
= "VERSION_GREATER_EQUAL"_s
;
64 auto const keyVERSION_LESS
= "VERSION_LESS"_s
;
65 auto const keyVERSION_LESS_EQUAL
= "VERSION_LESS_EQUAL"_s
;
66 auto const keyPATH_EQUAL
= "PATH_EQUAL"_s
;
68 cmSystemTools::CompareOp
const MATCH2CMPOP
[5] = {
69 cmSystemTools::OP_LESS
, cmSystemTools::OP_LESS_EQUAL
,
70 cmSystemTools::OP_GREATER
, cmSystemTools::OP_GREATER_EQUAL
,
71 cmSystemTools::OP_EQUAL
74 // Run-Time to Compile-Time template selector
75 template <template <typename
> class Comp
, template <typename
> class... Ops
>
76 struct cmRt2CtSelector
79 static bool eval(int r
, T lhs
, T rhs
)
85 return Comp
<T
>()(lhs
, rhs
);
87 return cmRt2CtSelector
<Ops
...>::eval(r
- 1, lhs
, rhs
);
92 template <template <typename
> class Comp
>
93 struct cmRt2CtSelector
<Comp
>
96 static bool eval(int r
, T lhs
, T rhs
)
98 return r
== 1 && Comp
<T
>()(lhs
, rhs
);
102 std::string
bool2string(bool const value
)
104 return std::string(static_cast<std::size_t>(1),
105 static_cast<char>('0' + static_cast<int>(value
)));
108 bool looksLikeSpecialVariable(const std::string
& var
,
109 cm::static_string_view prefix
,
110 const std::size_t varNameLen
)
112 // NOTE Expecting a variable name at least 1 char length:
113 // <prefix> + `{` + <varname> + `}`
114 return ((prefix
.size() + 3) <= varNameLen
) &&
115 cmHasPrefix(var
, cmStrCat(prefix
, '{')) && var
[varNameLen
- 1] == '}';
117 } // anonymous namespace
119 #if defined(__SUNPRO_CC)
120 # define CM_INHERIT_CTOR(Class, Base, Tpl) \
121 template <typename... Args> \
122 Class(Args&&... args) \
123 : Base Tpl(std::forward<Args>(args)...) \
127 # define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base
130 // BEGIN cmConditionEvaluator::cmArgumentList
131 class cmConditionEvaluator::cmArgumentList
132 : public std::list
<cmExpandedCommandArgument
>
134 using base_t
= std::list
<cmExpandedCommandArgument
>;
137 CM_INHERIT_CTOR(cmArgumentList
, list
, <cmExpandedCommandArgument
>);
139 class CurrentAndNextIter
141 friend class cmConditionEvaluator::cmArgumentList
;
144 base_t::iterator current
;
145 base_t::iterator next
;
147 CurrentAndNextIter
advance(base_t
& args
)
149 this->current
= std::next(this->current
);
151 std::next(this->current
,
152 static_cast<difference_type
>(this->current
!= args
.end()));
157 CurrentAndNextIter(base_t
& args
)
158 : current(args
.begin())
160 std::next(this->current
,
161 static_cast<difference_type
>(this->current
!= args
.end())))
166 class CurrentAndTwoMoreIter
168 friend class cmConditionEvaluator::cmArgumentList
;
171 base_t::iterator current
;
172 base_t::iterator next
;
173 base_t::iterator nextnext
;
175 CurrentAndTwoMoreIter
advance(base_t
& args
)
177 this->current
= std::next(this->current
);
179 std::next(this->current
,
180 static_cast<difference_type
>(this->current
!= args
.end()));
181 this->nextnext
= std::next(
182 this->next
, static_cast<difference_type
>(this->next
!= args
.end()));
187 CurrentAndTwoMoreIter(base_t
& args
)
188 : current(args
.begin())
190 std::next(this->current
,
191 static_cast<difference_type
>(this->current
!= args
.end())))
192 , nextnext(std::next(
193 this->next
, static_cast<difference_type
>(this->next
!= args
.end())))
198 CurrentAndNextIter
make2ArgsIterator() { return *this; }
199 CurrentAndTwoMoreIter
make3ArgsIterator() { return *this; }
201 template <typename Iter
>
202 void ReduceOneArg(const bool value
, Iter args
)
204 *args
.current
= cmExpandedCommandArgument(bool2string(value
), true);
205 this->erase(args
.next
);
208 void ReduceTwoArgs(const bool value
, CurrentAndTwoMoreIter args
)
210 *args
.current
= cmExpandedCommandArgument(bool2string(value
), true);
211 this->erase(args
.nextnext
);
212 this->erase(args
.next
);
216 // END cmConditionEvaluator::cmArgumentList
218 cmConditionEvaluator::cmConditionEvaluator(cmMakefile
& makefile
,
219 cmListFileBacktrace bt
)
221 , Backtrace(std::move(bt
))
222 , Policy12Status(makefile
.GetPolicyStatus(cmPolicies::CMP0012
))
223 , Policy54Status(makefile
.GetPolicyStatus(cmPolicies::CMP0054
))
224 , Policy57Status(makefile
.GetPolicyStatus(cmPolicies::CMP0057
))
225 , Policy64Status(makefile
.GetPolicyStatus(cmPolicies::CMP0064
))
226 , Policy139Status(makefile
.GetPolicyStatus(cmPolicies::CMP0139
))
230 //=========================================================================
231 // order of operations,
232 // 1. ( ) -- parenthetical groups
233 // 2. IS_DIRECTORY EXISTS COMMAND DEFINED etc predicates
234 // 3. MATCHES LESS GREATER EQUAL STRLESS STRGREATER STREQUAL etc binary ops
238 // There is an issue on whether the arguments should be values of references,
239 // for example IF (FOO AND BAR) should that compare the strings FOO and BAR
240 // or should it really do IF (${FOO} AND ${BAR}) Currently IS_DIRECTORY
241 // EXISTS COMMAND and DEFINED all take values. EQUAL, LESS and GREATER can
242 // take numeric values or variable names. STRLESS and STRGREATER take
243 // variable names but if the variable name is not found it will use the name
244 // directly. AND OR take variables or the values 0 or 1.
246 bool cmConditionEvaluator::IsTrue(
247 const std::vector
<cmExpandedCommandArgument
>& args
, std::string
& errorString
,
252 // handle empty invocation
257 // store the reduced args in this vector
258 cmArgumentList
newArgs(args
.begin(), args
.end());
260 // now loop through the arguments and see if we can reduce any of them
261 // we do this multiple times. Once for each level of precedence
263 using handlerFn_t
= bool (cmConditionEvaluator::*)(
264 cmArgumentList
&, std::string
&, MessageType
&);
265 const std::array
<handlerFn_t
, 5> handlers
= { {
266 &cmConditionEvaluator::HandleLevel0
, // parenthesis
267 &cmConditionEvaluator::HandleLevel1
, // predicates
268 &cmConditionEvaluator::HandleLevel2
, // binary ops
269 &cmConditionEvaluator::HandleLevel3
, // NOT
270 &cmConditionEvaluator::HandleLevel4
// AND OR
272 for (auto fn
: handlers
) {
273 // Call the reducer 'till there is anything to reduce...
274 // (i.e., if after an iteration the size becomes smaller)
275 auto levelResult
= true;
276 for (auto beginSize
= newArgs
.size();
277 (levelResult
= (this->*fn
)(newArgs
, errorString
, status
)) &&
278 newArgs
.size() < beginSize
;
279 beginSize
= newArgs
.size()) {
283 // NOTE `errorString` supposed to be set already
288 // now at the end there should only be one argument left
289 if (newArgs
.size() != 1) {
290 errorString
= "Unknown arguments specified";
291 status
= MessageType::FATAL_ERROR
;
295 return this->GetBooleanValueWithAutoDereference(newArgs
.front(), errorString
,
299 //=========================================================================
300 cmValue
cmConditionEvaluator::GetDefinitionIfUnquoted(
301 cmExpandedCommandArgument
const& argument
) const
303 if ((this->Policy54Status
!= cmPolicies::WARN
&&
304 this->Policy54Status
!= cmPolicies::OLD
) &&
305 argument
.WasQuoted()) {
309 cmValue def
= this->Makefile
.GetDefinition(argument
.GetValue());
311 if (def
&& argument
.WasQuoted() &&
312 this->Policy54Status
== cmPolicies::WARN
) {
313 if (!this->Makefile
.HasCMP0054AlreadyBeenReported(this->Backtrace
.Top())) {
314 std::ostringstream e
;
316 e
<< (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054
))
318 "Quoted variables like \"" << argument
.GetValue() << "\" "
319 "will no longer be dereferenced when the policy is set to NEW. "
320 "Since the policy is not set the OLD behavior will be used.";
323 this->Makefile
.GetCMakeInstance()->IssueMessage(
324 MessageType::AUTHOR_WARNING
, e
.str(), this->Backtrace
);
331 //=========================================================================
332 cmValue
cmConditionEvaluator::GetVariableOrString(
333 const cmExpandedCommandArgument
& argument
) const
335 cmValue def
= this->GetDefinitionIfUnquoted(argument
);
338 def
= cmValue(argument
.GetValue());
344 //=========================================================================
345 bool cmConditionEvaluator::IsKeyword(
346 cm::static_string_view keyword
,
347 const cmExpandedCommandArgument
& argument
) const
349 if ((this->Policy54Status
!= cmPolicies::WARN
&&
350 this->Policy54Status
!= cmPolicies::OLD
) &&
351 argument
.WasQuoted()) {
355 const auto isKeyword
= argument
.GetValue() == keyword
;
357 if (isKeyword
&& argument
.WasQuoted() &&
358 this->Policy54Status
== cmPolicies::WARN
) {
359 if (!this->Makefile
.HasCMP0054AlreadyBeenReported(this->Backtrace
.Top())) {
360 std::ostringstream e
;
362 e
<< cmPolicies::GetPolicyWarning(cmPolicies::CMP0054
)
364 "Quoted keywords like \"" << argument
.GetValue() << "\" "
365 "will no longer be interpreted as keywords "
366 "when the policy is set to NEW. "
367 "Since the policy is not set the OLD behavior will be used.";
370 this->Makefile
.GetCMakeInstance()->IssueMessage(
371 MessageType::AUTHOR_WARNING
, e
.str(), this->Backtrace
);
378 //=========================================================================
379 bool cmConditionEvaluator::GetBooleanValue(
380 cmExpandedCommandArgument
& arg
) const
382 // Check basic and named constants.
383 if (cmIsOn(arg
.GetValue())) {
386 if (cmIsOff(arg
.GetValue())) {
390 // Check for numbers.
393 const double d
= std::strtod(arg
.GetValue().c_str(), &end
);
395 // The whole string is a number. Use C conversion to bool.
396 return static_cast<bool>(d
);
401 cmValue def
= this->GetDefinitionIfUnquoted(arg
);
405 //=========================================================================
406 // Boolean value behavior from CMake 2.6.4 and below.
407 bool cmConditionEvaluator::GetBooleanValueOld(
408 cmExpandedCommandArgument
const& arg
, bool const one
) const
411 // Old IsTrue behavior for single argument.
418 cmValue def
= this->GetDefinitionIfUnquoted(arg
);
421 // Old GetVariableOrNumber behavior.
422 cmValue def
= this->GetDefinitionIfUnquoted(arg
);
423 if (!def
&& std::atoi(arg
.GetValue().c_str())) {
424 def
= cmValue(arg
.GetValue());
429 //=========================================================================
430 // returns the resulting boolean value
431 bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
432 cmExpandedCommandArgument
& newArg
, std::string
& errorString
,
433 MessageType
& status
, bool const oneArg
) const
435 // Use the policy if it is set.
436 if (this->Policy12Status
== cmPolicies::NEW
) {
437 return this->GetBooleanValue(newArg
);
439 if (this->Policy12Status
== cmPolicies::OLD
) {
440 return this->GetBooleanValueOld(newArg
, oneArg
);
443 // Check policy only if old and new results differ.
444 const auto newResult
= this->GetBooleanValue(newArg
);
445 const auto oldResult
= this->GetBooleanValueOld(newArg
, oneArg
);
446 if (newResult
!= oldResult
) {
447 switch (this->Policy12Status
) {
448 case cmPolicies::WARN
:
449 errorString
= "An argument named \"" + newArg
.GetValue() +
450 "\" appears in a conditional statement. " +
451 cmPolicies::GetPolicyWarning(cmPolicies::CMP0012
);
452 status
= MessageType::AUTHOR_WARNING
;
454 case cmPolicies::OLD
:
456 case cmPolicies::REQUIRED_IF_USED
:
457 case cmPolicies::REQUIRED_ALWAYS
: {
458 errorString
= "An argument named \"" + newArg
.GetValue() +
459 "\" appears in a conditional statement. " +
460 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0012
);
461 status
= MessageType::FATAL_ERROR
;
464 case cmPolicies::NEW
:
472 inline int cmConditionEvaluator::matchKeysImpl(
473 const cmExpandedCommandArgument
&)
475 // Zero means "not found"
479 template <int N
, typename T
, typename
... Keys
>
480 inline int cmConditionEvaluator::matchKeysImpl(
481 const cmExpandedCommandArgument
& arg
, T current
, Keys
... key
)
483 if (this->IsKeyword(current
, arg
)) {
484 // Stop searching as soon as smth has found
487 return matchKeysImpl
<N
+ 1>(arg
, key
...);
490 template <typename
... Keys
>
491 inline int cmConditionEvaluator::matchKeys(
492 const cmExpandedCommandArgument
& arg
, Keys
... key
)
494 // Get index of the matched key (1-based)
495 return matchKeysImpl
<1>(arg
, key
...);
498 //=========================================================================
499 // level 0 processes parenthetical expressions
500 bool cmConditionEvaluator::HandleLevel0(cmArgumentList
& newArgs
,
501 std::string
& errorString
,
504 for (auto arg
= newArgs
.begin(); arg
!= newArgs
.end(); ++arg
) {
505 if (this->IsKeyword(keyParenL
, *arg
)) {
506 // search for the closing paren for this opening one
508 auto argClose
= std::next(arg
);
509 for (; argClose
!= newArgs
.end() && depth
; ++argClose
) {
510 depth
+= int(this->IsKeyword(keyParenL
, *argClose
)) -
511 int(this->IsKeyword(keyParenR
, *argClose
));
514 errorString
= "mismatched parenthesis in condition";
515 status
= MessageType::FATAL_ERROR
;
519 // store the reduced args in this vector
520 auto argOpen
= std::next(arg
);
521 const std::vector
<cmExpandedCommandArgument
> subExpr(
522 argOpen
, std::prev(argClose
));
524 // now recursively invoke IsTrue to handle the values inside the
525 // parenthetical expression
526 const auto value
= this->IsTrue(subExpr
, errorString
, status
);
527 *arg
= cmExpandedCommandArgument(bool2string(value
), true);
528 argOpen
= std::next(arg
);
529 // remove the now evaluated parenthetical expression
530 newArgs
.erase(argOpen
, argClose
);
536 //=========================================================================
537 // level one handles most predicates except for NOT
538 bool cmConditionEvaluator::HandleLevel1(cmArgumentList
& newArgs
, std::string
&,
541 for (auto args
= newArgs
.make2ArgsIterator(); args
.current
!= newArgs
.end();
542 args
.advance(newArgs
)) {
544 auto policyCheck
= [&, this](const cmPolicies::PolicyID id
,
545 const cmPolicies::PolicyStatus status
,
546 const cm::static_string_view kw
) {
547 if (status
== cmPolicies::WARN
&& this->IsKeyword(kw
, *args
.current
)) {
548 std::ostringstream e
;
549 e
<< cmPolicies::GetPolicyWarning(id
) << "\n"
551 << " will be interpreted as an operator "
552 "when the policy is set to NEW. "
553 "Since the policy is not set the OLD behavior will be used.";
555 this->Makefile
.IssueMessage(MessageType::AUTHOR_WARNING
, e
.str());
559 // NOTE Checking policies for warnings are not require an access to the
560 // next arg. Check them first!
561 policyCheck(cmPolicies::CMP0064
, this->Policy64Status
, keyTEST
);
563 // NOTE Fail fast: All the predicates below require the next arg to be
565 if (args
.next
== newArgs
.end()) {
570 if (this->IsKeyword(keyEXISTS
, *args
.current
)) {
571 newArgs
.ReduceOneArg(cmSystemTools::FileExists(args
.next
->GetValue()),
574 // check if a file is readable
575 else if (this->IsKeyword(keyIS_READABLE
, *args
.current
)) {
576 newArgs
.ReduceOneArg(cmSystemTools::TestFileAccess(
577 args
.next
->GetValue(), cmsys::TEST_FILE_READ
),
580 // check if a file is writable
581 else if (this->IsKeyword(keyIS_WRITABLE
, *args
.current
)) {
582 newArgs
.ReduceOneArg(cmSystemTools::TestFileAccess(
583 args
.next
->GetValue(), cmsys::TEST_FILE_WRITE
),
586 // check if a file is executable
587 else if (this->IsKeyword(keyIS_EXECUTABLE
, *args
.current
)) {
588 newArgs
.ReduceOneArg(cmSystemTools::TestFileAccess(
589 args
.next
->GetValue(), cmsys::TEST_FILE_EXECUTE
),
592 // does a directory with this name exist
593 else if (this->IsKeyword(keyIS_DIRECTORY
, *args
.current
)) {
594 newArgs
.ReduceOneArg(
595 cmSystemTools::FileIsDirectory(args
.next
->GetValue()), args
);
597 // does a symlink with this name exist
598 else if (this->IsKeyword(keyIS_SYMLINK
, *args
.current
)) {
599 newArgs
.ReduceOneArg(cmSystemTools::FileIsSymlink(args
.next
->GetValue()),
602 // is the given path an absolute path ?
603 else if (this->IsKeyword(keyIS_ABSOLUTE
, *args
.current
)) {
604 newArgs
.ReduceOneArg(
605 cmSystemTools::FileIsFullPath(args
.next
->GetValue()), args
);
607 // does a command exist
608 else if (this->IsKeyword(keyCOMMAND
, *args
.current
)) {
609 newArgs
.ReduceOneArg(
611 this->Makefile
.GetState()->GetCommand(args
.next
->GetValue())),
614 // does a policy exist
615 else if (this->IsKeyword(keyPOLICY
, *args
.current
)) {
616 cmPolicies::PolicyID pid
;
617 newArgs
.ReduceOneArg(
618 cmPolicies::GetPolicyID(args
.next
->GetValue().c_str(), pid
), args
);
620 // does a target exist
621 else if (this->IsKeyword(keyTARGET
, *args
.current
)) {
622 newArgs
.ReduceOneArg(static_cast<bool>(this->Makefile
.FindTargetToUse(
623 args
.next
->GetValue())),
626 // is a variable defined
627 else if (this->IsKeyword(keyDEFINED
, *args
.current
)) {
628 const auto& var
= args
.next
->GetValue();
629 const auto varNameLen
= var
.size();
632 if (looksLikeSpecialVariable(var
, "ENV"_s
, varNameLen
)) {
633 const auto env
= args
.next
->GetValue().substr(4, varNameLen
- 5);
634 result
= cmSystemTools::HasEnv(env
);
637 else if (looksLikeSpecialVariable(var
, "CACHE"_s
, varNameLen
)) {
638 const auto cache
= args
.next
->GetValue().substr(6, varNameLen
- 7);
639 result
= static_cast<bool>(
640 this->Makefile
.GetState()->GetCacheEntryValue(cache
));
644 result
= this->Makefile
.IsDefinitionSet(args
.next
->GetValue());
646 newArgs
.ReduceOneArg(result
, args
);
649 else if (this->IsKeyword(keyTEST
, *args
.current
)) {
650 if (this->Policy64Status
== cmPolicies::OLD
||
651 this->Policy64Status
== cmPolicies::WARN
) {
654 newArgs
.ReduceOneArg(
655 static_cast<bool>(this->Makefile
.GetTest(args
.next
->GetValue())),
662 //=========================================================================
663 // level two handles most binary operations except for AND OR
664 bool cmConditionEvaluator::HandleLevel2(cmArgumentList
& newArgs
,
665 std::string
& errorString
,
668 for (auto args
= newArgs
.make3ArgsIterator(); args
.current
!= newArgs
.end();
669 args
.advance(newArgs
)) {
673 // NOTE Handle special case `if(... BLAH_BLAH MATCHES)`
674 // (i.e., w/o regex to match which is possibly result of
675 // variable expansion to an empty string)
676 if (args
.next
!= newArgs
.end() &&
677 this->IsKeyword(keyMATCHES
, *args
.current
)) {
678 newArgs
.ReduceOneArg(false, args
);
681 // NOTE Fail fast: All the binary ops below require 2 arguments.
682 else if (args
.next
== newArgs
.end() || args
.nextnext
== newArgs
.end()) {
686 else if (this->IsKeyword(keyMATCHES
, *args
.next
)) {
687 cmValue def
= this->GetDefinitionIfUnquoted(*args
.current
);
691 def
= cmValue(args
.current
->GetValue());
692 } else if (cmHasLiteralPrefix(args
.current
->GetValue(),
694 // The string to match is owned by our match result variables.
695 // Move it to our own buffer before clearing them.
697 def
= cmValue(def_buf
);
700 this->Makefile
.ClearMatches();
702 const auto& rex
= args
.nextnext
->GetValue();
703 cmsys::RegularExpression regEntry
;
704 if (!regEntry
.compile(rex
)) {
705 std::ostringstream error
;
706 error
<< "Regular expression \"" << rex
<< "\" cannot compile";
707 errorString
= error
.str();
708 status
= MessageType::FATAL_ERROR
;
712 const auto match
= regEntry
.find(*def
);
714 this->Makefile
.StoreMatches(regEntry
);
716 newArgs
.ReduceTwoArgs(match
, args
);
720 this->matchKeys(*args
.next
, keyLESS
, keyLESS_EQUAL
, keyGREATER
,
721 keyGREATER_EQUAL
, keyEQUAL
))) {
723 cmValue ldef
= this->GetVariableOrString(*args
.current
);
724 cmValue rdef
= this->GetVariableOrString(*args
.nextnext
);
728 auto parseDoubles
= [&]() {
729 return std::sscanf(ldef
->c_str(), "%lg", &lhs
) == 1 &&
730 std::sscanf(rdef
->c_str(), "%lg", &rhs
) == 1;
733 const auto result
= parseDoubles() &&
735 std::less
, std::less_equal
,
736 std::greater
, std::greater_equal
,
738 >::eval(matchNo
, lhs
, rhs
);
740 newArgs
.ReduceTwoArgs(result
, args
);
743 else if ((matchNo
= this->matchKeys(*args
.next
, keySTRLESS
,
744 keySTRLESS_EQUAL
, keySTRGREATER
,
745 keySTRGREATER_EQUAL
, keySTREQUAL
))) {
747 const cmValue lhs
= this->GetVariableOrString(*args
.current
);
748 const cmValue rhs
= this->GetVariableOrString(*args
.nextnext
);
749 const auto val
= (*lhs
).compare(*rhs
);
751 const auto result
= cmRt2CtSelector
<
752 std::less
, std::less_equal
,
753 std::greater
, std::greater_equal
,
755 >::eval(matchNo
, val
, 0);
757 newArgs
.ReduceTwoArgs(result
, args
);
761 this->matchKeys(*args
.next
, keyVERSION_LESS
,
762 keyVERSION_LESS_EQUAL
, keyVERSION_GREATER
,
763 keyVERSION_GREATER_EQUAL
, keyVERSION_EQUAL
))) {
764 const auto op
= MATCH2CMPOP
[matchNo
- 1];
765 const cmValue lhs
= this->GetVariableOrString(*args
.current
);
766 const cmValue rhs
= this->GetVariableOrString(*args
.nextnext
);
767 const auto result
= cmSystemTools::VersionCompare(op
, lhs
, rhs
);
768 newArgs
.ReduceTwoArgs(result
, args
);
771 // is file A newer than file B
772 else if (this->IsKeyword(keyIS_NEWER_THAN
, *args
.next
)) {
773 auto fileIsNewer
= 0;
774 cmsys::Status ftcStatus
= cmSystemTools::FileTimeCompare(
775 args
.current
->GetValue(), args
.nextnext
->GetValue(), &fileIsNewer
);
776 newArgs
.ReduceTwoArgs(
777 (!ftcStatus
|| fileIsNewer
== 1 || fileIsNewer
== 0), args
);
780 else if (this->IsKeyword(keyIN_LIST
, *args
.next
)) {
782 if (this->Policy57Status
!= cmPolicies::OLD
&&
783 this->Policy57Status
!= cmPolicies::WARN
) {
785 cmValue lhs
= this->GetVariableOrString(*args
.current
);
786 cmValue rhs
= this->Makefile
.GetDefinition(args
.nextnext
->GetValue());
788 newArgs
.ReduceTwoArgs(
790 cm::contains(cmList
{ *rhs
, cmList::EmptyElements::Yes
}, *lhs
),
794 else if (this->Policy57Status
== cmPolicies::WARN
) {
795 std::ostringstream e
;
796 e
<< cmPolicies::GetPolicyWarning(cmPolicies::CMP0057
)
798 "IN_LIST will be interpreted as an operator "
799 "when the policy is set to NEW. "
800 "Since the policy is not set the OLD behavior will be used.";
802 this->Makefile
.IssueMessage(MessageType::AUTHOR_WARNING
, e
.str());
806 else if (this->IsKeyword(keyPATH_EQUAL
, *args
.next
)) {
808 if (this->Policy139Status
!= cmPolicies::OLD
&&
809 this->Policy139Status
!= cmPolicies::WARN
) {
811 cmValue lhs
= this->GetVariableOrString(*args
.current
);
812 cmValue rhs
= this->GetVariableOrString(*args
.nextnext
);
813 const auto result
= cmCMakePath
{ *lhs
} == cmCMakePath
{ *rhs
};
814 newArgs
.ReduceTwoArgs(result
, args
);
817 else if (this->Policy139Status
== cmPolicies::WARN
) {
818 std::ostringstream e
;
819 e
<< cmPolicies::GetPolicyWarning(cmPolicies::CMP0139
)
821 "PATH_EQUAL will be interpreted as an operator "
822 "when the policy is set to NEW. "
823 "Since the policy is not set the OLD behavior will be used.";
825 this->Makefile
.IssueMessage(MessageType::AUTHOR_WARNING
, e
.str());
832 //=========================================================================
833 // level 3 handles NOT
834 bool cmConditionEvaluator::HandleLevel3(cmArgumentList
& newArgs
,
835 std::string
& errorString
,
838 for (auto args
= newArgs
.make2ArgsIterator(); args
.next
!= newArgs
.end();
839 args
.advance(newArgs
)) {
840 if (this->IsKeyword(keyNOT
, *args
.current
)) {
841 const auto rhs
= this->GetBooleanValueWithAutoDereference(
842 *args
.next
, errorString
, status
);
843 newArgs
.ReduceOneArg(!rhs
, args
);
849 //=========================================================================
850 // level 4 handles AND OR
851 bool cmConditionEvaluator::HandleLevel4(cmArgumentList
& newArgs
,
852 std::string
& errorString
,
855 for (auto args
= newArgs
.make3ArgsIterator(); args
.nextnext
!= newArgs
.end();
856 args
.advance(newArgs
)) {
860 if ((matchNo
= this->matchKeys(*args
.next
, keyAND
, keyOR
))) {
861 const auto lhs
= this->GetBooleanValueWithAutoDereference(
862 *args
.current
, errorString
, status
);
863 const auto rhs
= this->GetBooleanValueWithAutoDereference(
864 *args
.nextnext
, errorString
, status
);
868 std::logical_and
, std::logical_or
869 >::eval(matchNo
, lhs
, rhs
);
871 newArgs
.ReduceTwoArgs(result
, args
);