CMAKE logical expressions combined "STREQUAL" and "OR" - cmake

Please correct the usage of logical expressions
$<$<STREQUAL:${VARIANT},abc>or>$<$<STREQUAL:${VARIANT},xyz>:service::accept_service>
Here am trying to check if the variant is either "abc" or "xyz" based on that service::accept_service is enabled.
There is a syntax mistake in combining STREQUAL and OR expressions which am not able to figure.

$<$<OR:$<STREQUAL:${VARIANT},abc>,$<STREQUAL:${VARIANT},xyz>>:service::accept_service>
This worked, thank you #tsyvarev for your help.

Related

In what order does CMake evaluate OR and AND in compound 'if' condition?

The CMake documentation states:
The following syntax applies to the condition argument of the if,
elseif and while() clauses.
Compound conditions are evaluated in the following order of
precedence: Innermost parentheses are evaluated first. Next come unary
tests such as EXISTS, COMMAND, and DEFINED. Then binary tests such as
EQUAL, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL, STREQUAL, STRLESS,
STRLESS_EQUAL, STRGREATER, STRGREATER_EQUAL, VERSION_EQUAL,
VERSION_LESS, VERSION_LESS_EQUAL, VERSION_GREATER,
VERSION_GREATER_EQUAL, and MATCHES. Then the boolean operators in the
order NOT, AND, and finally OR.
But the following prints 'FALSE':
cmake_minimum_required(VERSION 3.22)
project(Test)
if(YES OR NO AND NO)
message("TRUE")
else()
message("FALSE")
endif()
I'd expect the expression to evaluate as YES OR (NO AND NO). What's going on?
This is unfortunately not a bug in the implementation, but in the documentation. CMake is (mis)designed to evaluate AND and OR at the same precedence, and from left to right.
See the MR that will update the documentation here: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/6970

CMake - How does the if() command treat a symbol? As string or as variable?

I am not sure the CMake if() command will treat a symbol in the condition clause as a variable or a string literal. So I did some experiments.
Script1.cmake
cmake_minimum_required(VERSION 3.15)
set(XXX "YYY") #<========== HERE!!
if(XXX STREQUAL "XXX")
message("condition 1 is true") # If reach here, XXX is treated as string
elseif(XXX STREQUAL "YYY")
message("condition 2 is true") # If reach here, XXX is treated as variable
endif()
The output is:
condition 2 is true
So I come to below conclusion 1.
For a symbol in the condition clause:
If the symbol is defined as a variable before, CMake will treat it as variable and use its value for evaluation.
If the symbol is not defined as a variable before, CMake will treat it literally as a string.
Then I did another experiment.
set(ON "OFF")
if(ON)
message("condition 3 is true") # If reach here, ON is treated as a constant.
else()
message("condition 4 is true") # If reach here. ON is treated as a variable.
endif()
The output is:
condition 3 is true
So, though ON is explicitly defined as a variable, the if command still treat it as a constant of TRUE value. This directly contradicts to my previous conclusion 1.
So how can I know for sure the CMake if() command will treat a symbol as string or variable??
ADD 1 - 11:04 AM 7/11/2019
It seems the if(constant) form precedes other forms of if() statement. (src)
if(<constant>)
True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number.
False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the
empty string, or ends in the suffix -NOTFOUND. Named boolean constants
are case-insensitive. If the argument is not one of these specific
constants, it is treated as a variable or string and the following
signature is used.
So for now, I have to refer to the above rule first before applying my conclusion 1.
(This may be an answer, but I am not sure enough yet.)
Welcome to the wilderness of CMake symbol interpretation.
If the symbol exists as a variable, then the expression is evaluated with the value of the variable. Otherwise, the name of the variable (or literal, as you said) is evaluated instead.
The behavior becomes a little more consistent if you add the ${ and } sequences. Then the value of the variable is used in the evaluation every single time. If the variable doesn't exist or has not been assigned a value, then CMake uses several placeholder values that evaluate to "false". These are the values you mentioned in the latter part to your post.
I believe this is done this way for backwards compatibility, which CMake is really good about. For most of the quirky things CMake does, it's usually in the name of backwards compatibility.
As for the inconsistent behavior you mentioned in the "ON" variable, this is probably due to the precedence in which CMake processes the command arguments. I would have to figure that the constants are parsed before the symbol lookup occurs.
So when it comes to knowing/predicting how an if statement will evaluate, my best answer is experience. The CMake source tree and logic is one magnificent, nasty beast.
There's been discussions on adding an alternative language (one with perhaps a functional paradigm), but it's a quite large undertaking.

CMake syntax: how to negate if(<constant>) and if(<variable|string>)

CMake's if command [1] supports several signatures, starting with
if(<constant>)
if(<variable|string>)
if(NOT <expression>)
How to negate the first two?
If the CMake documentation is correct (which in my experience is far from certain), then my question boils down to:
How to convert a constant, a variable, or a string X into an expression, with the additional requirement that X is to be evaluated as a boolean?
[1] https://cmake.org/cmake/help/latest/command/if.html
Actually, <expression> is just a placeholder for any parameter, which can be passed to if. Even the list of possible if constructions is titled as "Possible expressions are".
if(NOT <constant>) # Revert 'if(<constant>)'
if(NOT <variable|string>) # Revert 'if(NOT <variable|string>)'

Globbing expression in cmake: can I use brackets?

In theory cmake should support globbing expression, a little bit like a glob command.
However, I cannot find a way to match, for example, "a sequence of 3 numbers, or nothing". I would guess it should be something like:
file (GLOB outputVar *theImportantStringIWantedToMatch{[0-9][0-9][0-9],}.dll)
But it does not seem to work. For example:
*opencv_*flann{[0-9][0-9][0-9],}.a
does not match a file called
libopencv_flann.a
(Should also match libopencv_flann462.a for example)
It does not even match even if I put an expression like this:
*opencv_*flann{.,.}a
Escaping the brackets or the comma does not help. Is there any way to get brackets working, or any alternative to the expression I am trying to achieve?
So, for the moment the only solution I can come out with is repeating the expression in the 2 versions, namely:
file (GLOB outputVar *theImportantStringIWantedToMatch[0-9][0-9][0-9].dll
*theImportantStringIWantedToMatch.dll
)

What's the difference between parenthesis $() and curly bracket ${} syntax in Makefile?

Is there any differences in invoking variables with syntax ${var} and $(var)? For instance, in the way the variable will be expanded or anything?
There's no difference – they mean exactly the same (in GNU Make and in POSIX make).
I think that $(round brackets) look tidier, but that's just personal preference.
(Other answers point to the relevant sections of the GNU Make documentation, and note that you shouldn't mix the syntaxes within a single expression)
The Basics of Variable References section from the GNU make documentation state no differences:
To substitute a variable's value, write a dollar sign followed by the
name of the variable in parentheses or braces: either $(foo) or
${foo} is a valid reference to the variable foo.
As already correctly pointed out, there is no difference but be be wary not to mix the two kind of delimiters as it can lead to cryptic errors like in the GNU make example by unomadh.
From the GNU make manual on the Function Call Syntax (emphasis mine):
[…] If the arguments themselves contain other function calls or variable references, it is wisest to use the same kind of delimiters for all the references; write $(subst a,b,$(x)), not $(subst a,b,${x}). This is because it is clearer, and because only one type of delimiter is matched to find the end of the reference.
The ${} style lets you test the make rules in the shell, if you have the corresponding environment variables set, since that is compatible with bash.
Actually, it seems to be fairly different:
, = ,
list = a,b,c
$(info $(subst $(,),-,$(list))_EOL)
$(info $(subst ${,},-,$(list))_EOL)
outputs
a-b-c_EOL
md/init-profile.md:4: *** unterminated variable reference. Stop.
But so far I only found this difference when the variable name into ${...} contains itself a comma. I first thought ${...} was expanding the comma not as part as the value, but it turns out i'm not able to hack it this way. I still don't understand this... If anyone had an explanation, I'd be happy to know !
It makes a difference if the expression contains unbalanced brackets:
${info ${subst ),(,:-)}}
$(info $(subst ),(,:-)))
->
:-(
*** insufficient number of arguments (1) to function 'subst'. Stop.
For variable references, this makes a difference for functions, or for variable names that contain brackets (bad idea)