Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use variables and avoid CMP0054 policy violations?

Tags:

cmake

We are catching errors during testing for CMP0054 policy violations. An example can be found online at Build 367.

cmake : CMake Warning (dev) at CMakeLists.txt:364 (if):
At line:8 char:5
+     cmake -G "Visual Studio 15 2017" -DCMAKE_BUILD_TYPE=$env:configur ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (CMake Warning (...s.txt:364 (if)::String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

  Policy CMP0054 is not set: Only interpret if() arguments as variables or
  keywords when unquoted.  Run "cmake --help-policy CMP0054" for policy
  details.  Use the cmake_policy command to set the policy and suppress this
  warning.
  Quoted variables like "MSVC" will no longer be dereferenced when the policy
  is set to NEW.  Since the policy is not set the OLD behavior will be used.
This warning is for project developers.  Use -Wno-dev to suppress it.

It is due to making an if statement like so:

if ("${CRYPTOPP_AMD64}" STREQUAL "1" OR "${CRYPTOPP_I386}" STREQUAL "1")
    ...
endif()

The statements above are consistent with What's the CMake syntax to set and use variables?, and it was the only thing that seemed to "just work" everywhere, regardless of whether the variable was set, was empty, had a 0 value, had a 1 value or had some other value.

We removed the quotes from the variable to satisfy the god damned policy and all hell broke loose. Now we are lucky to get through a CMake configuration without an error. We tried:

if (${CRYPTOPP_AMD64} STREQUAL "1" OR ${CRYPTOPP_I386} STREQUAL "1")
    ...
endif()

And:

if (${CRYPTOPP_AMD64} STREQUAL 1 OR ${CRYPTOPP_I386} STREQUAL 1)
    ...
endif()

And:

if (${CRYPTOPP_AMD64} EQUAL 1 OR ${CRYPTOPP_I386} EQUAL 1)
    ...
endif()

They mostly lead to a "CMake Error at CMakeLists.txt... if given arguments...". For example, here's testing on Solaris 11:

CMake Error at CMakeLists.txt:507 (if):
  if given arguments:

    "EQUAL" "1" "AND" "NOT" "DISABLE_SSSE3"

  Unknown arguments specified

We don't control user machines, so changing a policy and running commands like cmake_policy is out of the question for us. We don't expect users to do anything special. Users are only expected to make a directory, cd into the directory, and then run cmake. That's it.

What is the syntax we should use for variables that works everywhere given the latest changes in CMake?

When we say everywhere, we mean from Cmake 2.8 to Current, and a variable that might be in any state, including set, empty, 0 value, 1 value or an unexpected value.


EDIT: We are now catching bug reports for the unquoted variables on Windows machines. Yet another damn CMake bug at Windows CMake failure when compiler path has a space.

CMake has been in the library for two years. It makes up 18% of the bugs in our bug reporter. It causes a disproportionate number of problems for users.

like image 573
jww Avatar asked Aug 26 '17 22:08

jww


1 Answers

There are two solutions:

  1. Just set policy CMP0054 to NEW

    cmake_policy(SET CMP0054 NEW)
    

    That would stop CMake version >= 3.1 to try to interpret variables in quotes as variable names again (see also the discussion here, nobody really wanted the OLD behavior).

    The hint that "the OLD behavior of a policy is deprecated by definition and may be removed in a future version of CMake" just means that the CMake developers will sometime in the future remove the legacy code necessary to still support the OLD behavior.

  2. Change - as @Tsyvarev commented - all if statements to just the variable name without the dollar sign and braces

    if (CRYPTOPP_AMD64 STREQUAL "1" OR CRYPTOPP_I386 STREQUAL "1")
        ...
    endif()        
    

    That would also work for older versions of CMake.

It's not an option to use the ${ } variable de-referencing syntax without quotes, because it could end in an empty string (leading to those if() syntax errors).

Note: As a workaround CMake modules itself are using an "x" prefix for their variable checks. But I think that looks a little weird:

if ("x${CRYPTOPP_AMD64}" STREQUAL "x1" OR "x${CRYPTOPP_I386}" STREQUAL "x1")
    ...
endif()

Note: You only get the CMP0054 warning if the context of your variable was actually evaluated again (so it's a hint your CMake code will not work in those places).

like image 61
Florian Avatar answered Oct 18 '22 11:10

Florian