Search code examples
cmakecmakelists-optionscmake-language

Update the list defined in a top-level CMakeLists.txt in a .cmake function called from sub directory CMakeLists.txt file


I am trying to use a .cmake file to update a global list to collect all the library names and use that list to link all the libraries from the top-level CMakeLists.txt file but unfortunately at the end I see the list is empty. Please see the CMakeLists.txt and .cmake calls in below:

ROOOT
|
---> CMakeLists.txt
     This file creates an empty list
     set(global_list "")
|
---> sub_dir1
        |
        ---> CMakeLists.txt
             This file includes a build.cmake file
        ---> build.cmake
             This file defines a cmake function to update the global_list
             function(update_list)
                list(APPEND global_list ${lib_name})
                set(global_list ${global_list} PARENT_SCOPE)
             endfunction()
---> sub_dir2
        |
        ---> CMakeLists.txt
             This file calls update_list function to send the lib name as an input
             update_list(lib_sub_dir2)
---> sub_dir3
        |
        ---> CMakeLists.txt
             This file calls update_list function to send the lib name as an input
             update_list(lib_sub_dir3)
---> sub_dir4
        |
        ---> CMakeLists.txt
             This file calls update_list function to send the lib name as an input
             update_list(lib_sub_dir4)

At the end when the root level CMakeLists.txt file prints the global_list it shows empty.

Requirement: global_list should contain lib_sub_dir2, lib_sub_dir3, lib_sub_dir4


Solution

  • The problem here is the scope of variables. Every subdirectory added via add_subdirectory and every call of a cmake function() introduces a new variable scope. Variables of ancestor scopes are readable, but if you write a variable, you're working with a variable in the current scope. The only way of communicating the information to the parent scope is to use set(VAR_NAME value PARENT_SCOPE).

    Using this command isn't well maintainable when using add_subdirectory though, since you need to remember to use set(... PARENT_SCOPE) in every subdirectory.

    You could simply introduce a global cmake property of your own for this though.

    #introduce the property
    set_property(GLOBAL PROPERTY GLOBAL_LIST)
    
    # add "new_element"
    set_property(GLOBAL APPEND PROPERTY GLOBAL_LIST new_element)
    
    # store the current value of the GLOBAL_LIST in the GLOBAL_LIST_VALUE variable
    get_property(GLOBAL_LIST_VALUE GLOBAL PROPERTY GLOBAL_LIST)
    

    Introduce the global property in the toplevel CMakeLists.txt and you'll be able to add to the property and read the current value of the property from anywhere using the commands above.