Reputation: 11696
I have a pretty complicated make
-based project that I'm trying to migrate to modern CMake usage instead. In the Makefile
, I have several similar shared library targets that share mostly-identical compile flags. So, it would look something like this:
add_library(lib1 SHARED src1.cpp src2.cpp)
target_compile_options(lib1 PUBLIC -public-flag1 -public-flag2 -unique-flag3 PRIVATE -private-flag)
add_library(lib2 SHARED src3.cpp src4.cpp)
target_compile_options(lib2 PUBLIC -public-flag -public-flag2 PRIVATE -private-flag)
Is there some way I can abstract out the common dependencies so I don't have to repeat them under each target? It would seem that an Interface Library would be the way to do this, something like:
add_library(common_options INTERFACE)
target_compile_options(common_options
PUBLIC -public-flag1 -public-flag2
PRIVATE -private-flag
)
add_library(lib1 SHARED src1.cpp src2.cpp)
target_compile_options(lib1 PUBLIC -unique-flag3)
target_link_libraries(lib1 common_options)
add_library(lib2 SHARED src3.cpp src4.cpp)
target_link_libraries(lib1 common_options)
which would be a net win for maintainability in my opinion. However, CMake throws an error at this, complaining that an interface target can only carry INTERFACE
options (I can't add the PRIVATE
options to it). Is there a different way to do this that would allow me to encapsulate the PUBLIC
and PRIVATE
flags that are shared among targets?
Upvotes: 1
Views: 493
Reputation: 1993
You're on the right track. Consider this:
add_library(public INTERFACE)
add_library(private INTERFACE)
target_compile_options(public INTERFACE -Werror)
target_compile_options(private INTERFACE -Wall)
target_link_libraries(lib1
PUBLIC # <-- public's flags will be applied to lib1 and any targets linking to lib1
public
PRIVATE # <-- private's flags will only be applied to lib1
private
)
In short, you forgot to add scopes in your calls to target_link_libraries
.
Update:
So I think the direct answer to my question is that I can't do exactly what I want, which would be to encompass both private and public compile option dependencies into a single entity. Instead, I just have to make a public one and a private one. Not quite as clean as what I'd hoped, but manageable.
Not really, you will always have to specify PUBLIC
and PRIVATE
scope, but if you are willing to use target_compile_options
instead of interface libraries to achieve this, then this would also work:
set(COMMON_FLAGS
PUBLIC
-Werror
-ffast-math
PRIVATE
-Wall
)
target_compile_options(lib1 ${COMMON_FLAGS})
target_compile_options(lib2
${COMMON_FLAGS}
PUBLIC # <-- you can still set scoped flags
-fno-fast-math
)
In my opinion, however, using interface libraries and just deal with the fact that you have to use PUBLIC
and PRIVATE
is both cleaner and more maintainable in the longer run.
Btw, if you want to compact COMMON_FLAGS
to a single line: set(COMMON_FLAGS "PUBLIC;-Werror;-ffast-math;PRIVATE;-Wall")
Upvotes: 1