Daniel Marques
Daniel Marques

Reputation: 526

CMake target compile option leaking to other targets

I'm using CMake and I have an external dependency on a project that also uses CMake. It looks like a target compile option is leaking from that dependency's target to mine. The dependency is setting a high warning level and thus I get some warnings on my project's code as well.

I understand that the right thing to do might be to embrace that and actually fix the warnings, but I'd like to:

  1. Understand why this is happening in the first place
  2. Know what I can do to address the issue without modifying the dependency's CMakeLists.txt
  3. (Optional) Know what the dependency author could have done to prevent it (if applicable)

Here's what my CMakeLists.txt looks like:

project(MyProject)
add_subdirectory(External/MyDependency)
# ...
add_executable(${PROJECT_NAME} ${MY_SOURCE_FILES})
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
target_link_libraries(${PROJECT_NAME} my_dependency_lib)

And this is an excerpt of my dependency's CMakeLists.txt:

project(MyDependency)

# This is not the actual library I link
add_library(my_dependency_common INTERFACE)

# ...

# If I remove this line, I no longer get the warnings on my own code
target_compile_options(my_dependency_common INTERFACE /W4)

# This is where the actual library is built
# and my_dependency_lib is then linked to the my_dependency_common library
add_subdirectory(my_dependency_path)

Edit: Since the keyword INTERFACE seems to play an important role here, I'm including an excerpt of the subdirectory that is included in my dependency.

add_library(my_dependency_common_2 INTERFACE)
target_link_libraries(my_dependency_common_2 INTERFACE my_dependency_common)

# this is the actual lib that I am linking
add_library(my_dependency_lib SHARED) 
target_link_libraries(my_dependency_lib my_dependency_common_2)

Upvotes: 1

Views: 1781

Answers (1)

fabian
fabian

Reputation: 82511

Info attached to a target with INTERFACE visibility (or PUBLIC visibility which implies INTERFACE) is automatically applied to a linking target. You could avoid this by creating a new target INTERFACE library target that provides the necessary properties to link the lib, but not the undesirable properties. For this reason info like this should have a visibility that is "as PRIVATE as possible".

Note that the project may not be designed to be included via add_subdirectory. You may want to check, if there's cmake installation logic in the project that provides a cmake configuration script. If this is the case, compiling&installing the dependency to a local directory as a bootstrap step of your build may be a good option.

Here's how you could include the lib without the undesirable option though:

add_library(bad_lib foo.cpp foo.hpp)
target_include_directories(bad_lib PUBLIC good_include)
target_compile_options(bad_lib PUBLIC --bad-option)


# create interface lib for linking the lib without --bad-option being inherited
add_library(bad_lib_fixed INTERFACE)

# link the lib via the absolute library file path, not via the cmake target name
target_link_libraries(bad_lib_fixed INTERFACE $<TARGET_FILE:bad_lib> $<TARGET_PROPERTY:INTERFACE_LINK_LIBRARIES>)

target_include_directories(bad_lib_fixed INTERFACE $<TARGET_PROPERTY:bad_lib,INTERFACE_INCLUDE_DIRECTORIES>)


add_executable(LinkingTarget ...)
target_link_libraries(LinkingTarget PRIVATE bad_lib_fixed)

Note that this only works, there are no cmake targets linked to bad_lib_fixed with PUBLIC or INTERFACE visibility having the same issues. Otherwise instead of $<TARGET_PROPERTY:INTERFACE_LINK_LIBRARIES> the absolute paths to those libraries needs to be added...

Furthermore note that the INTERFACE library target is only there for convenience. If only a single target links the lib, you could directly add the properties to this target (possibly changing the visibilities to PUBLIC or PRIVATE).

Upvotes: 3

Related Questions