With CMake, how can I set environment properties on the gtest_discover_tests --gtest_list_tests call?
Asked Answered
M

1

11

I'm currently working on migrating our current build environment from MSBuild to CMake. I have a situation where I need to update the PATH variable in order for the units tests executable to run. This is not a issue for gtest_add_tests, as it uses the source to identify tests. But gtest_discover_tests, which executes the unit tests with the --gtest_list_tests flag, fails to identify any tests because a STATUS_DLL_NOT_FOUND error is encountered during the build.

For example:

add_executable(gTestExe ...)
target_include_directories(gTestExe ...)
target_compile_definitions(gTestExe ...)
target_link_libraries(gTestExe ...)
set (NEWPATH "/path/to/bin;$ENV{PATH}")
STRING(REPLACE ";" "\\;" NEWPATH "${NEWPATH}")

This works:

gtest_add_tests(TARGET gTestExe TEST_LIST allTests)
set_tests_properties(${all_tests} PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")

But this does not:

#set_target_properties(gTestExe  PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")
#set_property(DIRECTORY PROPERTY ENVIRONMENT "PATH=${NEWPATH}")
gtest_discover_tests(gTestExe  PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")

Edit: The tests themselves work when added using gtest_add_tests. The issue is the call to discover the tests, during the post build step that gtest_discover_tests registers, fails because the required libraries are not in the PATH.

Mcmath answered 18/8, 2019 at 4:52 Comment(3)
Are the tests themselves failing to build because of the missing DLL dependencies? If that is the case, have you seen this answer?Publication
@squareskittles I edited to try to clear up the issue I am seeing. The tests are building, it is the test discovery step that fails. Although, I believe some of the solutions described in that link wold work (copying dlls for example).Mcmath
Ok, if you find a solution or workaround that works for you, please post an answer.Publication
F
-1

I came across the same issue this morning and I found a (dirty ?) workaround. The reason why it won't work is a bit complicated, but the workaround is quite simple.

Why it won't work

gtest_discover_tests(gTestExe  PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")

Will not work is because the PATH contents are separated by semicolons and therefore are treated by CMake as a list value.

If you look a the GoogleTestAddTests.cmake file (located in C:\Program Files\CMake\share\cmake-3.17\Modules), it treats the PROPERTIES argument with a foreach.

The PROPERTIES value look like this for CMake at this point in the script : ENVIRONMENT;PATH=mypath;mypath2 and will treat mypath2 as a third argument instead of a value for the PATH environment variable.

CMake will then generate the following line :

set_tests_properties( mytest PROPERTIES ENVIRONMENT PATH=mypath mypath2)

Escaping the ; won't work because the list is automatically expended in add_custom_command() in GoogleTest.cmake (l. 420 in cmake 3.17.1) ignoring any form of escaping.

To prevent the cmake foreach to treat each value in the path as a list you can use a bracket argument like :

gtest_discover_tests(gTestExe PROPERTIES ENVIRONMENT "[==[PATH=${NEWPATH}]==]")

The cmake foreach will then treat your argument as one entity. Unfortunately CMake will also put a bracket in the generated code as it contains [ = and maybe spaces :

# This line 
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
  set(_args "${_args} [==[${_arg}]==]")
else()
  set(_args "${_args} ${_arg}")
endif()  

resulting in the following generated script :

set_tests_properties( mytest PROPERTIES ENVIRONMENT [==[ [==[PATH=mypath;mypath2] ]==])

And when executing the test cmake will attempt to read the value only removing the first bracket argument as they don't nest.

Possible workaround

So to do this we need CMake to not use bracket argument on our own bracket argument.

First make a local copy of GoogleTestAddTests.cmake file in your own repository (located in C:\Program Files\CMake\share\cmake-3.17\Modules).

At the beginning of your local copy of GoogleTestAddTests.cmake (l. 12) replace the function add_command by this one :

function(add_command NAME)
  set(_args "")
  foreach(_arg ${ARGN})
    # Patch : allow us to pass a bracket arguments and escape the containing list.
    if (_arg MATCHES "^\\[==\\[.*\\]==\\]$")
        string(REPLACE ";" "\;" _arg "${_arg}")
        set(_args "${_args} ${_arg}")
    # end of patch
    elseif(_arg MATCHES "[^-./:a-zA-Z0-9_]")
        set(_args "${_args} [==[${_arg}]==]")
    else()
        set(_args "${_args} ${_arg}")
    endif()
  endforeach()
  set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()

This will make cmake don't use bracket list on our bracket list and automatically escape the ; as set_tests_properties also treat the ; as a list.

Finally we need CMake to use our custom GoogleTestAddTests.cmake instead of the one in CMake.

After your call to include(GoogleTest) set the variable _GOOGLETEST_DISCOVER_TESTS_SCRIPT to the path to your local GoogleTestAddTests.cmake :

# Need google test
include(GoogleTest)

# Use our own version of GoogleTestAddTests.cmake
set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
  ${CMAKE_CURRENT_LIST_DIR}/GoogleTestAddTests.cmake
)

Note : In my example the GoogleTestAddTests.cmake is right next to the processing cmake file.

Then a simple call to

 gtest_discover_tests(my_target
   PROPERTIES ENVIRONMENT "[==[PATH=${my_path};$ENV{PATH}]==]"
 )

should work.

Faviolafavonian answered 14/5, 2020 at 13:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.