cmake, lost in the concept of global variables (and PARENT_SCOPE or add_subdirectory alternatives)
Asked Answered
O

2

14

I have a cmake project in which I have some modules and I'm using Find-*.cmake for including the shared modules in the application. For not taking in account every module that I add, I have defined a kind of global LIB variables tor the linker:

# inside a Find-*.cmake or in the CMakeLists.txt of the modules:
set(LIB ${LIB} ...)

so in one of the final applications that uses some modules I can just do:

target_link_libraries(${APP_NAME} ${LIB})

Then, I'd like to have the compiled modules in the /project_path/modules/foo/build so that if a module is really big to compile it can be compiled once for all the application that use it. The way I'm achieving this is to load the CMakeLists.txt of the module from the Find-*.cmake in this way:

# Inside FindFoo.cmake, for loading /project_path/modules/foo/CMakeLists.txt
# and compile it in /project_path/modules/foo/build
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../modules/${PACKAGE_NAME} 
                 ${CMAKE_CURRENT_LIST_DIR}/../modules/${PACKAGE_NAME}/build
)
include_directories(${CMAKE_CURRENT_LIST_DIR}/../modules/${PACKAGE_NAME}/include)

But it happened sometimes that some module require another modules so that the add_subdirectory creates new scopes and can correctly load LIB but cannot write it (when I use set it is in the deeper scope and not changes the upper scope). For bypass this I have to add PARENT_SCOPE in the set).. So I have tried to add it in some module that I think could be nested and hidden in some dependencies but compiling all the application I suddenly faced with:

CMake Warning (dev) at /path_to_repo/cmake/FindFooX.cmake:6 (set):
  Cannot set "LIB": current scope has no parent.
Call Stack (most recent call first):
  CMakeLists.txt:14 (find_package)
This warning is for project developers.  Use -Wno-dev to suppress it.

I am afraid that this can change from app to app in respect to which module I need or in respect of the dependecies tree in the modules itself so I'm looking for a cleaner solution.

Ophthalmitis answered 13/10, 2013 at 13:46 Comment(0)
A
27

All variables in CMake are local by default. While you can use the PARENT_SCOPE parameter to increase the scope of a local variable by one layer, that mostly makes sense for return values of functions.

For find scripts on the other hand you usually want the behavior of a global variable: Once the find script is called by anyone, you want the results to be available everywhere. In particular, a second call to the same find script should just reuse the results of the first call. In CMake this is achieved by storing variables to the cache. The various find_* calls already do this automatically, so you should prefer using those where applicable. For any additional custom variables, set offers the capability to store to the cache as well:

set(MY_GLOBAL_VARIABLE "Some value" CACHE STRING "Description")

Note that local variables can hide cached variables of the same name in their scope.

Andino answered 14/10, 2013 at 9:2 Comment(4)
sorry but i have not understand exactly how to use this cached global variables.. i have tried to replace all instances of set(LIB ${LIB} ${PACKAGE_NAME}) with set(LIB ${LIB} ${PACKAGE_NAME} CACHE STRING "package description") but in the end if i message("-- ${LIB}") only the first package i have called is included inside ${LIB}..Ophthalmitis
@Ophthalmitis Check out the documentation for set I linked above. CMake assumes that cached values usually won't get updated once they have been created, so you need an additional FORCE parameter when changing them afterwards.Andino
so i replaced everywhere with set(LIB ${LIB} ${PACKAGE_NAME} CACHE STRING "Description" FORCE), also tried with INTERNAL but doesn't work anyway: no errors but the lib added in the subpackage added with add_subdirectories are no more present in the upper scopeOphthalmitis
@Ophthalmitis Check that PACKAGE_NAME is not empty. The set command you posted is definitely correct, so if the error persists, you should put together an SSCCE and post a new question.Andino
A
36

You can 'simulate' GLOBAL variable behavior, by using properties with GLOBAL scope :

SET_PROPERTY(GLOBAL PROPERTY MyGlobalProperty "MyGlobalPropertyValue")

Then you can extract your global property by using

GET_PROPERTY(MyLocalVariable GLOBAL PROPERTY MyGlobalProperty)

Then, MyLocalVariable contains "MyGlobalPropertyValue".

Because PARENT_SCOPE extends variable definitions to the only parent directory (and not to its parents), there are cases it's not enough, for example if you have a deep source tree...

Audrit answered 25/3, 2015 at 8:16 Comment(0)
A
27

All variables in CMake are local by default. While you can use the PARENT_SCOPE parameter to increase the scope of a local variable by one layer, that mostly makes sense for return values of functions.

For find scripts on the other hand you usually want the behavior of a global variable: Once the find script is called by anyone, you want the results to be available everywhere. In particular, a second call to the same find script should just reuse the results of the first call. In CMake this is achieved by storing variables to the cache. The various find_* calls already do this automatically, so you should prefer using those where applicable. For any additional custom variables, set offers the capability to store to the cache as well:

set(MY_GLOBAL_VARIABLE "Some value" CACHE STRING "Description")

Note that local variables can hide cached variables of the same name in their scope.

Andino answered 14/10, 2013 at 9:2 Comment(4)
sorry but i have not understand exactly how to use this cached global variables.. i have tried to replace all instances of set(LIB ${LIB} ${PACKAGE_NAME}) with set(LIB ${LIB} ${PACKAGE_NAME} CACHE STRING "package description") but in the end if i message("-- ${LIB}") only the first package i have called is included inside ${LIB}..Ophthalmitis
@Ophthalmitis Check out the documentation for set I linked above. CMake assumes that cached values usually won't get updated once they have been created, so you need an additional FORCE parameter when changing them afterwards.Andino
so i replaced everywhere with set(LIB ${LIB} ${PACKAGE_NAME} CACHE STRING "Description" FORCE), also tried with INTERNAL but doesn't work anyway: no errors but the lib added in the subpackage added with add_subdirectories are no more present in the upper scopeOphthalmitis
@Ophthalmitis Check that PACKAGE_NAME is not empty. The set command you posted is definitely correct, so if the error persists, you should put together an SSCCE and post a new question.Andino

© 2022 - 2024 — McMap. All rights reserved.