28

I have (mostly) successfully set up ExternalProject_Add for googletest. However, I noticed that things like my choice of C++ compiler, build type, etc. are not automatically forwarded to the ExternalProject.

I can easily add any given flag by adding it to CMAKE_ARGS in the call to ExternalProject_Add like so:

CMAKE_ARGS -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS}

However, this requires that I enumerate all of the possible arguments that should be forwarded to googletests's CMake invocation, and that list is pretty enormous. I would also need to create that same list for every other ExternalProject_Add I wanted. That seems fragile and error prone.

Is there a way to tell CMake to "forward" the configuration that the user provided along? In other words, if I invoked CMake as:

cmake <path-to-project> -DCMAKE_C_COMPILER=/usr/bin/clang -DSOME_RANDOM_FLAG=stuff

Then I would like my call to ExternalProject_Add to provide the same compiler selection and value for SOME_RANDOM_FLAG, without needing to explicitly list those names. I'm not sure that simply passing CMake's ARGV along would work, since saying

CC=/usr/bin/clang cmake <path-to-project>

would ideally work as well.

Any thoughts on how to accomplish this?

thiagowfx
  • 4,832
  • 6
  • 37
  • 51
acm
  • 12,183
  • 5
  • 39
  • 68

3 Answers3

11

After long hours of trying to figure this out, it was finally answered (by Don Hinton) in the CMake mailing list. Fraser's solution is very close, but can still pass some project specific arguments that can cause some unpredictable behavior.

The following works properly. Hopefully this will save people some time trying to figure this out:

cmake_minimum_required(VERSION 3.1)

# MUST be done before call to 'project'
get_cmake_property(vars CACHE_VARIABLES)
foreach(var ${vars})
  get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING)
    if("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
        # message("${var} = [${${var}}]  --  ${currentHelpString}") # uncomment to see the variables being processed
        list(APPEND CL_ARGS "-D${var}=${${var}}")
    endif()
endforeach()

project(SuperBuild)

include(ExternalProject)

ExternalProject_Add(ext_proj
  ...

  CMAKE_ARGS ${CL_ARGS}
)

Link to mailing list thread: https://cmake.org/pipermail/cmake/2018-January/067002.html

Samaursa
  • 16,527
  • 21
  • 89
  • 160
  • From the mailing list: _,just note that it won't work if CMakeCache.txt already exists._ If you re-run cmake, some of your cached variables will get mixed in, as if you used this code after a call to 'project'. To solve this, just save your CL_ARGS to cache via `set(CL_ARGS ${CL_ARGS} CACHE STRING "comment")` and wrap the whole construct with `if (NOT CL_ARGS)`. – akwky Mar 06 '23 at 14:15
4

I don't know of a robust way to achieve this, and I'm pretty sure there's no standard "CMake way", but my answer to a similar question about capturing CMake command line arguments might help you?

Community
  • 1
  • 1
Fraser
  • 74,704
  • 20
  • 238
  • 215
  • Hmm. That is unfortunate. One thing that I looked at was the '-C ' option to cmake. I thought that if I used that argument to point the ExternalProject_Add cmake invocation at my top level CMakeCache.txt it would work. But the -C option doesn't want a CMakeCache.txt file, it wants some thing else ("not a cache-format file"). Do you have any idea how to make the top level cmake invocation emit something that could be consumed by the lower level cmake with -C? – acm Aug 18 '12 at 19:33
  • Not in a robust way I'm afraid. The `-C` option is really just a way of passing in a bundle of command line options - it's looking for a bunch of `set(... CACHE ...)` type commands. You *could* use an initial cache file, and then parse it yourself inside CMakeLists.txt to derive the various settings it created. But this would require the path to the file be known, and also would force you to always pass command line parameters via the `-C` file if you want them to get to gtest. I hope this CMake limitation will get more attention soon as more people start using `ExternalProject_Add`. – Fraser Aug 18 '12 at 19:45
  • 1
    Thanks for your insights. I'm accepting your answer as it seems most promising, even if not particularly satisfying. There is clearly some difficulty in this area with CMake. The impedance mismatch between ExternalProject_Add and FindPackage is another headache, and 'superbuilders' is a pretty awful solution. I just managed to sidestep my problem by switching away from ExternalProject_Add and just using add_subdirectory to pull in googletest, which worked. It does mean it needs to be cloned into my source tree, which I had hoped to avoid, but what can you do. – acm Aug 18 '12 at 20:09
  • I am facing a similar problem. IMHO @acm's latest comment seems to be the most promising workaround so far: to use a bunch of *git* submodules (or similar) **plus** `add_subdirectory`, instead of using `ExternalProject_Add`. – thiagowfx Jan 16 '17 at 21:39
0
  1. Create a toolchain file to include CMake variables, for example:
    # /tmp/toolchain.cmake
    set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Targeting macOS version" FORCE)
    set(CMAKE_C_COMPILER_LAUNCHER "sccache" CACHE STRING "C compiler launcher" FORCE)
    
  2. Set the environment variable CMAKE_TOOLCHAIN_FILE to the toolchain file
    export CMAKE_TOOLCHAIN_FILE=/tmp/toolchain.cmake
    
  3. Run cmake to configure the root project

Now the environment variable CMAKE_TOOLCHAIN_FILE should be inherited by CMake external projects automatically.

See https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html

Yang Bo
  • 3,586
  • 3
  • 22
  • 35