tomahh
tomahh

Reputation: 13661

Is it possible to bypass the new scope creation of the add_subdirectory() command?

I currently have the following architecture

`-- system
    |-- CMakeLists.txt
    |-- dll
    |   |-- CMakeLists.txt
    |   |-- UnixDLoader.cpp
    |   |-- ...
    |   `-- WinDLoader.hh
    |-- sockets
    |   |-- CMakeLists.txt
    |   |-- crossplateform_utils.h
    |   |-- ...
    |   `-- Udp.hh
    `-- threads
        |-- CMakeLists.txt
        |-- IMutex.hh
        |-- ...
        `-- WinThread.hh

My root CMakeLists.txt is as follow

project(system)

add_subdirectory(dll)
add_subdirectory(sockets)
add_subdirectory(threads)

add_library(${PROJECT_NAME}
    ${${PROJECT_NAME}_HEADERS}
    ${${PROJECT_NAME}_SOURCES}
)

and in threads/ for example, I have the following

list(APPEND ${PROJECT_NAME}_HEADERS
    IThread.hh
    UnixThread.hh
    WinThread.hh
    IMutex.hh
    UnixMutex.hh
    WinMutex.hh
)
set(${PROJECT_NAME}_HEADERS ${${PROJECT_NAME}_HEADERS} PARENT_SCOPE)

list(APPEND ${PROJECT_NAME}_SOURCES
    UnixThread.cpp
    WinThread.cpp      
    UnixMutex.cpp
    WinMutex.cpp    
    PARENT_SCOPE
)
set(${PROJECT_NAME}_SOURCES ${${PROJECT_NAME}_SOURCES} PARENT_SCOPE)

if(UNIX)
    target_link_libraries(${PROJECT_NAME} pthread)
endif(UNIX)

but, as add_subdirectory() creates a new scope, my ${PROJECT_NAME}_* variables are empty. I read about set(... PARENT_SCOPE), but the paths become invalids and anyhow cmake produces an error on the call to target_link_libraries() in system/threads/CMakeLists.txt saying Cannot specify link libraries for target "system" which is not built by this project.. Scope issue again.

So I though I would be nice if I found a solution to bypass the scope creation, but I'll take any solution that keeps the logic of my architecture.

Upvotes: 4

Views: 1282

Answers (1)

Fraser
Fraser

Reputation: 78320

You have a couple of reasonable options here I think.

The add_subdirectory command is often used to include a directory (it needn't actually be a subdirectory in the filesystem sense) which really contains a standalone module, e.g. a library or executable; one which can be built without the help of a parent CMakeLists file. In this case, the submodule's CMakeLists.txt would contain its own project command, rendering the use of a global ${PROJECT_NAME} variable difficult.

In your case, it appears that you simply want the subdirectories' CMakeLists files to append lists of files to variables defined in the parent scope. You can achieve this by making use of the CMake variable CMAKE_CURRENT_LIST_DIR in your subordinate CMakeLists:

set(${PROJECT_NAME}_HEADERS
    ${${PROJECT_NAME}_HEADERS}
    ${CMAKE_CURRENT_LIST_DIR}/IThread.hh
    ${CMAKE_CURRENT_LIST_DIR}/UnixThread.hh
    ${CMAKE_CURRENT_LIST_DIR}/WinThread.hh
    ${CMAKE_CURRENT_LIST_DIR}/IMutex.hh
    ${CMAKE_CURRENT_LIST_DIR}/UnixMutex.hh
    ${CMAKE_CURRENT_LIST_DIR}/WinMutex.hh
    PARENT_SCOPE
)

set(${PROJECT_NAME}_SOURCES
    ${${PROJECT_NAME}_SOURCES}
    ${CMAKE_CURRENT_LIST_DIR}/UnixThread.cpp
    ${CMAKE_CURRENT_LIST_DIR}/WinThread.cpp      
    ${CMAKE_CURRENT_LIST_DIR}/UnixMutex.cpp
    ${CMAKE_CURRENT_LIST_DIR}/WinMutex.cpp    
    PARENT_SCOPE
)

Ensure you don't have a project command in your subdirectories' CMakeLists files.

This can be made slightly simpler in this case by swapping the add_subdirectory command with the include command, hence avoiding scoping issues.

To do this, remove the PARENT_SCOPE args from the set commands, and in the top-level CMakeLists.txt:

add_subdirectory(dll)
add_subdirectory(sockets)
add_subdirectory(threads)
include(dll/CMakeLists.txt)
include(sockets/CMakeLists.txt)
include(threads/CMakeLists.txt)


Your other issue relating to target_link_libraries(${PROJECT_NAME} pthread) is simply down to the fact that the add_library command defining the library ${PROJECT_NAME} is called after the target_link_libraries command. Your simplest option is probably to move the target_link_libraries command to the parent CMakeLists.txt

Upvotes: 6

Related Questions