3

In my CMake project, I have 2 targets (a static library and a shared library) that share the same source code files, like this:

add_library("${PROJECT_NAME}" STATIC
    "${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h"
    "${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp"
    "${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h"
    "${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp"
)
add_library("${PROJECT_NAME}-shared" SHARED
    "${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h"
    "${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp"
    "${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h"
    "${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp"
)

Obviously, there is a problem here: the sources definition is duplicated. It's hard to maintain and it's also error prone.

In order to avoid that, I'd like to create a CMake list variable so that the sources definition could be reused in both targets.

I've tried this but it doesn't work:

set(CALCULATOR_CORE_SOURCES_LIST
    "${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h"
    "${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp"
    "${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h"
    "${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp"
)
string(REPLACE ";" " " CALCULATOR_CORE_SOURCES "${CALCULATOR_CORE_SOURCES_LIST}")

add_library("${PROJECT_NAME}" STATIC ${CALCULATOR_CORE_SOURCES})
add_library("${PROJECT_NAME}-shared" SHARED ${CALCULATOR_CORE_SOURCES})

It fails with the error: Cannot find source file.

So... how could I reuse source file definitions between targets without this duplication? Is it possible to do this using lists or is there a better approach to solve this issue?

PS: I'm using CMake 3.15

Kevin
  • 14,269
  • 7
  • 44
  • 64
hbobenicio
  • 705
  • 12
  • 14

1 Answers1

4

Your code is close; you can simply use the CALCULATOR_CORE_SOURCES_LIST variable to add the group of sources/headers to both add_library() calls:

set(CALCULATOR_CORE_SOURCES_LIST
    ${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.h
    ${PROJECT_SOURCE_DIR}/src/calculator/core/ShuntingYard.cpp
    ${PROJECT_SOURCE_DIR}/include/calculator/core/Calculator.h
    ${PROJECT_SOURCE_DIR}/src/calculator/core/Calculator.cpp
)

add_library(${PROJECT_NAME} STATIC ${CALCULATOR_CORE_SOURCES_LIST})
add_library(${PROJECT_NAME}-shared SHARED ${CALCULATOR_CORE_SOURCES_LIST})

If the group of sources is the same for both libraries, you should only need to define the list once. From the documentation, the set command will do the following:

Multiple arguments will be joined as a semicolon-separated list to form the actual variable value to be set.

Thus, this list can be passed to the add_library() calls. You also shouldn't need the quotes (") around each file.

Kevin
  • 14,269
  • 7
  • 44
  • 64
  • It worked! Much simpler than I thought! I didn't know that add_library could accept lists directly (I thought it could only handle space separated source values). It even worked with double quotes (") around the files in the set() call and the variable expansion in add_library() call. I'm using quotes on every variable expansion and filepath value definitions as a good default... are there cases where it isn't advisable to use them? – hbobenicio Sep 25 '19 at 13:27
  • 1
    @hbobenicio There are some cases where quotes are useful, but in many cases, you don't need them at all. There is a great explanation [here](https://stackoverflow.com/a/35853080/3987854) about the implications of using quotes around your variables. – Kevin Sep 25 '19 at 13:31