[CMake] Explanation....

Johannes Zarl johannes.zarl at jku.at
Wed Apr 16 05:39:18 EDT 2014


Hi,

On Monday, 14. April 2014, 19:23:19, Theodore Papadopoulo wrote:
> > if ("${arg}" STREQUAL "TOTO") -> if ("TOTO" STREQUAL "TOTO") -> if
> > (B STREQUAL B)
> 
> What I do not follow is why there is an implicit evaluation of "TOTO"
> into "B" (in both this case or the next I explicitely asked for a
> string containing the content of the variable ${arg}, if I had
> intended the content of the variable which name is in ${arg} I would
> have written ${${arg}}.

If it is any consolence to you, this is one of the most-hated (anti-)features 
of cmake. The best you can do is to embrace this oddity (it's not going to go 
away soon), and use a different idiom:

Instead of ``"${var}" STREQUAL "VALUE"'', write:

IF ( var MATCHES "^VALUE$" )
-> The MATCHES expression only does string/variable expansion on the right 
side. Therefore you get a well-defined behaviour regardless of any possible 
aliasing effects on variable names and values.

> Of course, you are right about what happened, it is just very unsafe.
> That means that if I'm using as a variable name the name of a constant
> used in some other module, there will be very unexpected failures....

You are totally right. I guess if there was a way to change this in a 
backwards-compatible way, cmake developers would not hesitate to do so.

> For example taking /usr/share/cmake/Modules/FeatureSummary.cmake as an
> example, if I set a variable PROPERTIES with a value might (untested)
> provoke quite unexpected results (if(NOT "${_props}" STREQUAL
> "PROPERTIES")). Or if I do set(0 1) before calling
> CheckLanguage.cmake (if(CMAKE_${lang}_COMPILER AND "${result}"
> STREQUAL "0")).

You can have even more fun by redefining some version strings:

set(MY_VERSION "2.8.15")
set("2.8.15" hehehe)
if (MY_VERSION VERSION_EQUAL "2.8.15" )
	message("All as expected...")
else()
    message("I'm so evil!")
endif()
# You can probably guess what this prints out ;-)

IMO one thing that *could* be done would be to deprecate the unsafe 
expressions and replace them with some safe version.

> I realize now that this is what probably means the
>  if(<variable|string> STREQUAL <variable|string>)
> in the cmake manual. But in my reading that was meaning some
> overloading so that
> 
> TOTO is a variable so evaluated into ${TOTO}
> but "TOTO" remains a string and is not evaluated any further.
> 
> At least that what I thought (basically at some type system
> differentiating variables and strings)... I'm clearly wrong as:
> 
> set(TOTO B)
> if ("B" STREQUAL "TOTO")
>     message("AhAh")
> endif()

What helped me to understand it was to see this as a two-step procedure:

1) Parsing: ${variable} gets replaced by its value
2) Execution: IF is called with the strings as argument.

At step 2, the IF statement cannot possibly determine how its arguments looked 
before expansion.


> > if ( "_ASDF_${arg}" STREQUAL "_ASDF_TOTO")
> 
> Well I knew the technique, but thought it was mainly for protection
> againts empty strings (which "${arg}" should be equivalent). Not sure
> it makes the thing safer though.
> 
> Well I guess I need to go and see cmake source code, as I cannot
> understand cery well its evaluation rules.

Sorry for my mistake in the first mail. I wrote my response rather quick, not 
pausing to think of better alternatives. Had I invested more time, I would've 
given you the solution using "MATCHES" that I explained above. Sorry for the 
inconvenience...

Cheers,
  Johannes


More information about the CMake mailing list