6

I have this code project can be built either independently or as a subproject of a larger repository (checking it our as a sub-repository). In the latter case, I have a top-level CMakeLists.txt for the main project which has

add_subdirectory(${MY_SUBPROJ_SUBDIR})

now, I want the subproject to behave somewhat differently in case it's used via the add_directory(). Obviously, I would be using a large if instruction. But what condition do I check? How can CMake "tell", when running for some CMakeLists.txt, whether it's a subdir file or the main file?

einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

12
  1. After the project() call in project's CMakeLists.txt and in the project's subdirectories you may use:

     if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
         # I am top-level project.
     else()
         # I am called from other project with add_subdirectory().
     endif()
    

    NOTE: As stated in the comments, this approach may to not work on Windows, where CMAKE_SOURCE_DIR contains lower-case version of PROJECT_SOURCE_DIR and thus cannot be compared directly for equality. For that case approach with checking PARENT_DIRECTORY property, as described in that answer to the duplicate question, seems to be more robust.

  2. Alternative for use before the project() call:

     if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
         # I am top-level project.
     else()
         # I am called from other project with add_subdirectory().
     endif()
    

    This alternative can also be used anywhere in project's CMakeLists.txt (but not in subdirectories).


Assume you have a project A with two CMakeLists.txt: one in the project's directory, and one in subdirectory src/. Scheme for use approaches described above:

CMakeLists.txt:

cmake_minimum_required(...)
...
<only approach *2* can be used there>
...
project(A)
...
<approach *1* or *2* can be used there>
...
add_subdirectory(src)
...

src/CMakeLists.txt:

...
<only approach *1* can be used there>
...

With given scheme project A may detect, whether it is built standalone (top-level project) or as a part of another project B.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Sounds reasonable. – einpoklum Mar 10 '17 at 11:31
  • Just a hint: When using `PROJEC_SOURCE_DIR` it means CMake has already parsed your sub-directories `project()` command. Alternatively you can find `if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)` (see e.g. [here](https://github.com/preshing/junction/blob/master/CMakeLists.txt) or [here](https://github.com/ufz-vislab/VtkFbxConverter/blob/master/CMakeLists.txt)), – Florian Mar 10 '17 at 11:37
  • @Florian: I wanted to write the same, but was interrupted. Now the post is updated. Thanks. – Tsyvarev Mar 10 '17 at 11:53
  • The repositories shouldn't really make assumptions about whether or not we will even have a project call, or in which order. – einpoklum Mar 10 '17 at 14:26
  • None project has assumptions about its caller. By `project()` call I mean the **call from the project itself**, not from the callers. I have edited the post, probably you find it more clear. – Tsyvarev Mar 10 '17 at 14:55
  • I needed to use `CMAKE_CURRENT_SOURCE_DIR`, not `CMAKE_SOURCE_DIR` – sourcedelica Feb 08 '18 at 20:32
  • @sourcedelica That will always return true in the same file as the `project()` call. – Justin Sep 03 '18 at 04:49
  • 3
    This doesn't work on Windows. `CMAKE_SOURCE_DIR` expands to `c:/users/...`, but `PROJECT_SOURCE_DIR` expands to `C:/Users/...`. Instead of `STREQUAL`, one has to do a case insensitive comparison (convert both strings to lowercase, then do `STREQUAL`) – Justin Sep 03 '18 at 04:50
  • `string(TOLOWER ${CMAKE_SOURCE_DIR} source_dir) string(TOLOWER ${CMAKE_CURRENT_SOURCE_DIR} current_source_dir) if(${source_dir} STREQUAL ${current_source_dir}) message(STATUS "not in subdirectory") else() message(STATUS "in subdirectory") endif()` –  Nov 11 '19 at 11:12
  • Newer CMake provides `PROJECT_IS_TOP_LEVEL` variable – Mariusz Jaskółka Feb 17 '22 at 14:01
  • @MariuszJaskółka: This variable is already described in [that answer](https://stackoverflow.com/a/68820462/3440745) to the duplicate question. – Tsyvarev Feb 17 '22 at 14:08