Roland Sarrazin
Roland Sarrazin

Reputation: 1345

Using a CMake cache variable "before" it is defined

CMake cache variables can be set from virtually everywhere (see here @Florian's What's the CMake syntax to set and use variables?). I was under the assumption that the set value is visible everywhere, even to CMake lists parsed before, but this isn't the case.

Use case

Short example showing the behavior

cmake_minimum_required(VERSION 3.7)
project(test)
add_executable(EXEC test.cpp)
target_compile_definitions(EXEC PRIVATE MYDEF=${CMAKE_MYDEF})
set(CMAKE_MYDEF "MyValue" CACHE STRING "")

Questions

Edit: Short example solution

Following @Florian's answer, here the adapted example showing the solution:

cmake_minimum_required(VERSION 3.7)
project(test)
add_executable(EXEC test.cpp)
target_link_libraries(EXEC MyOtherLib)

add_library(MyOtherLib INTERFACE)
set(CMAKE_MYDEF "MyValue" CACHE STRING "")
target_compile_definitions(MyOtherLib INTERFACE MYDEF=${CMAKE_MYDEF})

Upvotes: 2

Views: 1337

Answers (2)

Florian
Florian

Reputation: 43068

Yes, I'm fully with @Tsyvarev's answer that CMake's parser works sequentially. So variables - even cached ones - or generator expressions - that can't read variables - are no good here.

I just wanted to add the possibilities you have using target and directory properties depending on the dependencies between A and B:

  1. When A depends on B with e.g.

    target_link_libraries(A PUBLIC B)
    

    then a simple

    target_compile_definitions(B PUBLIC MYDEF=SOME_DEF)
    

    would propagate the necessary define to A.

  2. When B depends an A and A is already known than it would be

    target_link_libraries(B PUBLIC A)
    target_compile_definitions(A PUBLIC MYDEF=SOME_OTHER_DEF)
    
  3. If you're working with sub-directories I would recommend putting the definition in the root CMakeLists.txt globally:

    add_definitions(-DMYDEF=GLOBAL_DEF)
    
  4. Finally the full variant with sub-directories letting B decide what to do:

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.7)
    
    project(test)
    
    add_subdirectory(A)
    add_subdirectory(B)
    

    A\CMakeLists.txt

    file(WRITE a.cpp [=[
    #include <iostream>
    
    #ifndef MYDEF
    #define MYDEF "Hello from A!"
    #endif
    
    void a()
    {
        std::cout << MYDEF << std::endl;
    }
    ]=])
    
    add_library(A a.cpp)
    

    B\CMakeLists.txt

    file(WRITE b.cpp [=[
    
    void a();
    
    void main()
    {
        a();
    }
    ]=])
    
    add_executable(B b.cpp)
    target_link_libraries(B A)
    
    if (TARGET "A")
        target_compile_definitions(A PRIVATE MYDEF="Hello from B!")
    else()
        set_property(
            DIRECTORY ".." 
            APPEND PROPERTY 
                COMPILE_DEFINITIONS "MYDEF=\"Hello from Global!\""
        )
    endif()
    

Reference

Upvotes: 1

Tsyvarev
Tsyvarev

Reputation: 66337

CMake processes scripts sequentially, starting from top-level CMakeLists.txt and executing its lines one by one.

So, if read variable before assigning it, you will get nothing. The only specific of CACHE variable in that scenario is possibility for that variable to be assigned on previous cmake invocation.


Needs for using a variable before its assigning taking a place usually signals about bad design. In many situations (even with legacy code), design can be fixed gracefully.

Forcing CMake to reconfigure the project can be accomplished e.g. by touching current script:

to force a re-configure, one could "cmake -E touch" the CMAKE_CURRENT_LIST_FILE, somehow during target building or some such.

Upvotes: 1

Related Questions