# Best Practice: Separate CMake targets for declaration and definition?

**URL:** https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112
**Category:** Usage
**Created:** [July 20, 2022, 7:26pm UTC](https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112 "2022-07-20T19:26:51Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [July 20, 2022, 7:26pm UTC](https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112/1 "2022-07-20T19:26:51Z")

</div>

I have a project where we have multiple threading libraries: Threading\_Linux, Threading\_Baremetal, etc. They all implement the same threading interfaces (function declarations and C++ interfaces), declared in it’s own `INTERFACE` Threading library. In turn, our business logic libraries (or other higher level libraries) will only link to Threading, instead of a specific platform Threading implementation. Then, when our application is assembled it will look something like below.

```auto
# Threading
add_library(Threading INTERFACE)

target_sources(Threading INTERFACE IThreading.h)

target_include_directories(Threading INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

# Threading_Linux
add_library(Threading_Linux)

target_sources(Threading_Linux PRIVATE Threading_Linux.h Threading_Linux.cpp)

target_include_directories(Threading_Linux PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(Threading_Linux PUBLIC Threading)

# BusinessLogic_Foo
add_library(BusinessLogic_Foo)

target_sources(BusinessLogic_Foo PRIVATE Foo.h Foo.cpp)

target_include_directories(BusinessLogic_Foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(BusinessLogic_Foo PRIVATE Threading) # Only the interfaces are leveraged

# App
add_executable(App)

target_source(App PRIVATE main.cpp)

target_link_libraries(App
   PRIVATE
      BusinessLogic_Foo
      Threading_Linux
)

```

I believe link order does become important here such that Threading\_Linux needs to be last (as described [here](https://stackoverflow.com/a/3363420/5374468).

Is this common practice? Does it violate CMake practices to provide a target (BusinessLogic\_Foo) that itself does not have the complete definitions and doesn’t explicitly specify where to find the definition? Would there be a different way to do this? It is beneficial in cross platform systems or if we wanted to inject a fake threading library for testing BusinessLogic\_Foo.

---

<div class="post-metadata">

### Author: ![benthevining](https://discourse.cmake.org/user_avatar/discourse.cmake.org/benthevining/32/1924_2.png) [@benthevining](https://discourse.cmake.org/u/benthevining)
#### Post date: [July 20, 2022, 7:38pm UTC](https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112/2 "2022-07-20T19:38:52Z")

</div>

I would make just one target, which in its C++ code selects the appropriate implementation using `#if SOMETHING`, and these macros can be added to the target using `target_compile_definitions()`.

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [July 20, 2022, 8:12pm UTC](https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112/3 "2022-07-20T20:12:12Z")

</div>

In this scenario, who would set/request the definition? Would the Threading library set `target_compile_definitions()` based on whatever platform we’re configuring for?

This wouldn’t support if we wanted the ability to link `BusinessLogic_Foo` to a `Threading_Fake` or `Threading_Linux` implementation on the same platform. For example, a test application could link together `BusinessLogic_Foo` and `Threading_Fake`, while the real application could link together `BusinessLogic_Foo` and `Threading_Linux`

---

<div class="post-metadata">

### Author: ![benthevining](https://discourse.cmake.org/user_avatar/discourse.cmake.org/benthevining/32/1924_2.png) [@benthevining](https://discourse.cmake.org/u/benthevining)
#### Post date: [July 20, 2022, 9:01pm UTC](https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112/4 "2022-07-20T21:01:02Z")

</div>

> [@developer](#):
>
> Would the Threading library set `target_compile_definitions()` based on whatever platform we’re configuring for?

Yes. I would expose the various backends as CMake options, but have some default logic to select a sensible backend in the event the user hasn’t selected one manually.

> [@developer](#):
>
> For example, a test application could link together `BusinessLogic_Foo` and `Threading_Fake`, while the real application could link together `BusinessLogic_Foo` and `Threading_Linux`

So you want to be able to build multiple backends simultaneously? You’re right, this strategy would not support that.

---

<div class="post-metadata">

### Author: ![benthevining](https://discourse.cmake.org/user_avatar/discourse.cmake.org/benthevining/32/1924_2.png) [@benthevining](https://discourse.cmake.org/u/benthevining)
#### Post date: [July 20, 2022, 9:04pm UTC](https://discourse.cmake.org/t/best-practice-separate-cmake-targets-for-declaration-and-definition/6112/5 "2022-07-20T21:04:42Z")

</div>

Here is a library of mine that works very similarly to the solution I suggested: [Limes/libs/limes\_vecops at main · benthevining/Limes · GitHub](https://github.com/benthevining/Limes/tree/main/libs/limes_vecops)

This is a library that wraps various platform-specific vector operations libraries, like Intel IPP, Apple vDSP, etc. Within CMake, there is only one target for this library, and its selection of backend is controlled using macro definitions. In the C++ code, I do some error checking of these macros and also set their defaults if they are not defined: [Limes/vecops\_macros.h at main · benthevining/Limes · GitHub](https://github.com/benthevining/Limes/blob/main/libs/limes_vecops/vecops/vecops_macros.h)

And in the CMake code, the user can select any of the available backends using cache variables, and there is also some default selection logic if the user doesn’t pick one: [Limes/SelectImpl.cmake at main · benthevining/Limes · GitHub](https://github.com/benthevining/Limes/blob/main/libs/limes_vecops/SelectImpl.cmake)
