Answer the exact question first
1. Can a PUBLIC compile definition be scoped so downstream consumers do not receive it while the producer retains it? No.
CMake’s PUBLIC scope is defined to propagate the property to every target that links to the producing target. Once the definition is added with target_compile_definitions(lib PUBLIC INTERNAL_MACRO), the macro becomes part of INTERFACE_COMPILE_DEFINITIONS and is automatically applied to the compiler command line of every consumer that links to lib. CMake has no built‑in flag or option that can cancel that propagation on a per‑consumer basis.
2. Is there a consumer‑side link expression or usage requirement that strips an inherited definition without changing the producer target? No. The only way to prevent the macro from reaching a consumer is to alter the producer: either change the definition to PRIVATE or move the macro to a separate, internally‑linked target. Consumer‑side commands such as target_link_libraries or target_link_options cannot “un‑inherit” a compile definition.
Typical workaround
The most common pattern is to separate internal build requirements from the public interface:
# lib.cmake
add_library(lib SHARED ...)
# Public API – no internal macro
# Internal implementation details
add_library(lib_internal OBJECT ...)
# Mark the macro as PRIVATE on the internal target
target_compile_definitions(lib_internal PRIVATE INTERNAL_MACRO)
# Link the internal object library into lib
target_sources(lib PRIVATE $)
# Expose only the public target to consumers
# Consumers link only to "lib" – they never see INTERNAL_MACRO
If you must keep the macro on
lib for internal use but avoid leaking it, you can use a generator expression that only expands when the target itself is built:
target_compile_definitions(lib PRIVATE "$,INTERNAL_MACRO,>")
This still requires a change to the producer, but it gives you control over when the macro is defined. It is not a consumer‑side solution.
Verification steps (for your project)
- Baseline test: Add
target_compile_definitions(lib PUBLIC INTERNAL_MACRO), build a consumer app that links to lib, and inspect the compiler command line. The macro should appear.
- Change to PRIVATE: Replace PUBLIC with PRIVATE and rebuild. Verify that
app no longer receives the macro.
- Internal target approach: Create
lib_internal as shown above, link it to lib, and ensure app is linked only to lib. The macro should be absent from app's compile command.
Assumptions and caveats
- These techniques require CMake 3.12+ for the generator expression syntax.
- Changing the macro to PRIVATE may force recompilation of all existing consumers that previously relied on it, even if they didn’t use it directly.
- Tests that need the macro should link to the internal target or add the definition explicitly.
Ask for missing diagnostic detail
If you have a specific use‑case where the macro must stay on the public target for some consumers but not others, let me know which consumers need it. That will help refine the solution.