2

I have the following structure

project_root/
    CMakeLists.txt     (A)
    ext/
        CMakeLists.txt (B)
    apps/
        CMakeLists.txt (C)

The setup seems to be the fundamental issue, only when adding this new "config-style" library.

TL;DR: when find_package(foo) in (B) defines foo::foo as the library, how can I make foo::foo available in the parent scope so that target_link_libraries(tgt foo) will work for both (A) and (C)?

  1. List (A) defines my project's options, such as what drivers to compile support for.
  2. add_subdirectory(ext) takes place, and the needed external libraries are found. They are a mixture of add_subdirectory and find_package. List (B) populates lists for extra include directories, libraries, and compile time definitions, making them available to (A) (and subsequently (C)) with

    set(MYPROJ_EXTRA_INC_DIRS "${MYPROJ_EXTRA_INC_DIRS}" PARENT_SCOPE)
    set(MYPROJ_EXTRA_LIBS     "${MYPROJ_EXTRA_LIBS}"     PARENT_SCOPE)
    set(MYPROJ_EXTRA_DEFINES  "${MYPROJ_EXTRA_DEFINES}"  PARENT_SCOPE)
    
  3. List (A) now adds my library, including these extra directories, adding these extra definitions, and ultimately

    target_link_libraries(${MYPROJ_LIB_NAME} ${MYPROJ_EXTRA_LIBS})
    
  4. When the applications are requested to be built, add_subdirectory(apps) takes place, and list (C) defines a simple macro that creates an executable using the specified dependencies. The relevant part

    target_link_libraries(${appName} ${MYPROJ_LIB_NAME} ${MYPROJ_EXTRA_LIBS})
    

This has been working very well for a long time. However, I added support for a new library that uses config-style find_package definitions, and I can't figure out how to use it correctly.

Call this new library dependency foo. It ultimately defines a single foo_LIBRARY which is foo::foo. My understanding was that I would need to do target_link_libraries(tgt foo), which works in list (A) for my library. However, it does not work for the applications, and in the macro I have to do find_package(foo) again for every executable.

Is there a way to use the existing approach (list(APPEND MYPROJ_EXTRA_LIBS <something>)) that does not require running find_package every time?

I've exhausted every reasonable option, and either get that -lfoo is not defined (if I just append foo to the list like I thought I should be), or find_package() is missing for an IMPORTED or ALIAS target. AKA since find_package(foo) happens in (B), by the time we reach (C) this target is not available. I tried making an ALIAS, but the error was then something that amounts to ALIAS cannot be created to an IMPORTED library.

svenevs
  • 833
  • 9
  • 24

1 Answers1

6

Results of find_package call(both CONFIG and MODULE) are intended be used in the same directory or below. You are just lucky in that simple propagating of variables into PARENT_SCOPE makes results of find_package usable by the parent.

add_subdirectory(ext) takes place, and the needed external libraries are found.

Instead of ext/CMakeLists.txt included with add_subdirectory create CMake file (e.g. external.cmake) for being included via include. Because include command doesn't introduce new variable's scope, its find_package calls works for the main CMakeLists.txt.

Many existed projects process their dependencies in include files.


Another approach would be propagating results of find_package calls from subdirectory to the parent by creating INTERFACE library target which itself uses these results:

add_library(MyLibExtra INTERFACE)
target_link_libraries(MyLibExtra INTERFACE ${MYPROJ_EXTRA_LIBS})
target_include_directories(MyLibExtra INTERFACE ${MYPROJ_EXTRA_INC_DIRS})
target_compile_definitions(MyLibExtra INTERFACE ${MYPROJ_EXTRA_DEFINES})
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • I see, that's solid advice -- writing it up right now. However, I designed the library to be able to build as a child project. The `MYPROJ_EXTRA_LIBS` and related being necessary for the parent project. So even if I get it working for my project, once `add_subdirectory(myproj)` goes down in the parent project, this will ultimately still break right? – svenevs Feb 12 '18 at 08:13
  • 1
    Hmm, I have added a way with *INTERFACE* library. Probably, it will helps in the case when your library is actually a subproject. BTW, `set` with *PARENT_SCOPE* propagates a variable only to a *direct* parent. A "grandparent" won't see that variable. – Tsyvarev Feb 12 '18 at 08:56
  • Ok so I haven't quite gotten everything working, but the interface approach is **definitely** the way to go here. I've never used this before, but it's really quite wonderful -- thanks! For those reading this, with my library `myproj` getting built and linking against `myproj-interface` library, everything ripples through. In the parent project, when I do `target_link_libraries(someTgt myproj)`, since `myproj` links against `myproj-interface`, the parent project doesn't even need to know about the extra libraries going on! – svenevs Feb 13 '18 at 17:31