1

I am new in CMake and I was wondering whether there is a possibility to exclude certain sources from target_sources() based on a variable.

Let's say I have this below

target_sources(myTarget
    PUBLIC
    PRIVATE
        myDir1/src/a.c
        myDir2/src/b.c
        myDir3/src/c.c      
    INTERFACE
)

target_include_directories(myTarget
    PUBLIC
    PRIVATE       
        myDir1/inc
        myDir2/inc
        myDir3/inc  
    INTERFACE
)

I would like to exclude/include sources/directories from myDir3 based on a flag called i.e. myFlag. How can I achieve this?

target_sources(myTarget
    PUBLIC
    PRIVATE
        myDir1/src/a.c
        myDir2/src/b.c
        if(DEFINED myFlag)
           myDir3/src/c.c
        endif()
    INTERFACE
)

target_include_directories(myTarget
    PUBLIC
    PRIVATE        
        myDir1/inc
        myDir2/inc
        if(DEFINED myFlag)
           myDir3/inc  
        endif()
    INTERFACE
)
ndarkness
  • 897
  • 2
  • 13
  • 27

2 Answers2

5

You cannot put if statements inside a command's argument list. The target_* commands don't overwrite, but append, so the easiest solution is the following:

target_sources(
  myTarget
  PRIVATE
    myDir1/src/a.c
    myDir2/src/b.c 
)
if (myFlag) 
  target_sources(myTarget PRIVATE myDir3/src/c.c)
endif ()

target_include_directories(
  myTarget
  PRIVATE
    myDir1/inc
    myDir2/inc
)
if (myFlag) 
  target_include_directories(myTarget PRIVATE myDir3/inc)
endif ()

Another more declarative option would be to use a generator expression, like so:

target_sources(
  myTarget
  PRIVATE
    myDir1/src/a.c
    myDir2/src/b.c
    $<$<BOOL:${myFlag}>:myDir3/src/c.c>
)
Alex Reinking
  • 6,342
  • 2
  • 28
  • 47
  • one question, the second option you have placed, declarative option, does this enable or disable the usage of `myDir3/src/c.c` if `myFlag` is present? – ndarkness Mar 26 '21 at 16:47
  • 1
    If myFlag evaluates to something true (e.g. `1`, `ON`, `TRUE`, etc.), it will be included. – Alex Reinking Mar 26 '21 at 18:44
  • Thanks! How could I use this option if I would like to include the file in case the flag ‘myflag‘ is ‘0‘, ‘False‘ or ‘OFF‘? – ndarkness Mar 27 '21 at 16:39
  • 1
    `$>` see: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html – Alex Reinking Mar 27 '21 at 16:44
  • Hi @AlexReinking, I am using `$:myDir3/src/c.c>> ` where `set( myFlag "" )` but I am getting as an error `$ parameter must resolve to exactly one '0' or '1' value`. What I am doing wrong? – ndarkness Mar 29 '21 at 06:21
  • 1
    See the difference? `$>:myDir3/src/c.c>`. If you need more help, please ask a new question. – Alex Reinking Mar 29 '21 at 06:22
  • I do, thank you very much!! – ndarkness Mar 29 '21 at 06:26
  • I have seen an issue with the declarative option. If the flag `myFlag` does not exist, I will get an error from `CMake saying CMake Error in CMakeLists.txt:Target myTarget contains relative path in its INTERFACE_INCLUDE_DIRECTORIES: myDir3/inc`. I have that include directory as `$:myDir3/inc>>` is this expected? – ndarkness Mar 31 '21 at 13:12
  • Please ask a new question – Alex Reinking Apr 01 '21 at 18:22
1

Depending on why you're trying to do that it might also make sense to keep them as source files (so they show up e.g. in Visual Studio's target view), but to mark them as headers so they're not being compiled:

if (NOT myFlag)
  set_source_files_properties(
      srcfile1.cpp
      srcfile2.cpp
    PROPERTIES
      HEADER_FILE_ONLY ON
  )
endif()

This is even recommended by CMake's documentation for that purpose:

This is useful if you have some source files which you somehow pre-process, and then add these pre-processed sources via add_library() or add_executable(). Normally, in IDE, there would be no reference of the original sources, only of these pre-processed sources. So by setting this property for all the original source files to ON, and then either calling add_library() or add_executable() while passing both the pre-processed sources and the original sources, or by using target_sources() to add original source files will do exactly what would one expect, i.e. the original source files would be visible in IDE, and will not be built.

Corristo
  • 4,111
  • 1
  • 16
  • 32
  • In the modern world, Visual Studio can open CMake projects directly and this is in fact the _preferred_ workflow. So there's very little reason to do this. – Alex Reinking Mar 25 '21 at 21:40
  • @AlexReinking: I'm aware that Visual Studio can open the CMake projects directly, but if you do it will only show files that have been added to a target in the targets view. If you don't add headers they will not show up. Similarly, if you only conditionally add a source file to the target sources it will not show up unless the flag is set. And the same is true for other IDEs, like Qt Creator. And I don't see any way how IDEs could reliably do something differently. After all, headers can be included from anywhere, how should an IDE determine which target to add them to? – Corristo Mar 25 '21 at 23:49