The upcoming COMPILE_ONLY
generator expression for CMake 3.27 should simplify this.
You would have something like:
add_library(A SHARED)
target_compile_definitions(A PUBLIC FOO)
add_library(B SHARED)
target_compile_definitions(B PUBLIC BAR)
target_link_libraries(B PUBLIC $<COMPILE_ONLY:A> PRIVATE A)
add_library(C SHARED)
target_compile_definitions(C PUBLIC HOGE)
target_link_libraries(C PUBLIC $<COMPILE_ONLY:B> PRIVATE B)
add_executable(X)
target_link_libraries(X PRIVATE A C)