CMake has a great feature: transitive link dependencies. Let’s imagine an executable A that depends on library B which itself depends on library C. CMake is smart enough to link A with B and C, even if the dependency list of A only includes B.
Knowing this, I organize my sources in a particular way. A class is usually declared in a .h file and implemented in a cpp file. Well, for almost each .h/.cpp pair I define a library and list in the CMakeLists.txt the dependencies directly used by the cpp file.
Such an approach as huge advantages. First, the code base can scale easily, with many utility classes and executables that link only what is required. For example, each h/cpp file pair should ideally have a unit test. This test probably needs to be linked with only a small fraction of the code base. Relying an CMake transitive dependencies is a great way of selecting only what is required and all that is required.
There is another advantage. Imagine a library L that depends on boost. Of course, all users of L must link boost. Later in the life of the project, L is refactored to rely on c++11 features and it does not need boost anymore. Well, simply changing the dependency list of L will remove boost from everything that does not need it anymore. How convenient!
Of course, this scheme blows up the number of libraries, but this is not a problem. Even visual studio can manage workspaces with hundreds of projects quite well.
add_library(logging logging.h logging.cpp)
cxx_test(loggingTest loggingTest.cpp logging gtest_main)