1

I have the following issue: I have two different sets of files (main and an additional one), that I would like to keep separate. So, I have one set of files (main), that I configure this way:

set(libplayersource
        ....
        )

add_library( # Sets the name of the library.
        libplayer

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        ${libplayersource})

Then I have second set of files (additional), that I configure this way:

set(codec_source
        ...)

add_library(libcodec SHARED ${codec_source})

Eventually, I need to link these two sets of files:

target_link_libraries( # Specifies the target library.
        libplayer
        libcodec)

And after this configuration, I also need to include log lib in order to make it work. Firstly, I need to find this log lib, and then include it in my native lib.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

Also, I should to edit target_link_libraries to include log lib:

target_link_libraries( # Specifies the target library.
        libplayer
        libcodec
        ${log-lib})

All is fine if you are going to use this log lib in libplayer, but if you are going to use it in libcodec set, you'll get this error:

undefined reference to `__android_log_print'

clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)

It is means that the linker doesn't see the implementation of this method.

I found this answer on SO:

https://stackoverflow.com/a/47803975/5709159

and in order to solve the issue, I have added to my CMakeLists file this line:

target_link_libraries( # Specifies the target library.
        libcodec
        android
        ${log-lib}
        )

Main CMake file implementation:

...
#Main module
set(libplayersource
        ....
        )

add_library( # Sets the name of the library.
        libplayer

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        ${libplayersource})

#Additional module
set(codec_source
        ...)

add_library(libcodec SHARED ${codec_source})

#Log lib
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

#Linking

target_link_libraries( # Specifies the target library.
        libcodec
        ${log-lib}
        )

target_link_libraries( # Specifies the target library.
        libplayer
        libcodec
        ${log-lib})
...

So, I need to mention log lib in both of libraries.

Question is - Why doesn't the linker see log lib in libcodec? Why do I have to add the additional block?

target_link_libraries( # Specifies the target library.
        libcodec
        ${log-lib}
        )

to make log lib visible for linker in libcodec?

P.S In Visual Studio, if you have main project A and two libs B and C, you include these B and C libs in A, and that is it; everyone knows about everyone. I can call methods in B from C and so on. In order to call a B method from C, I don't need to include C in B. It is enough that both of these libs are included in A as a main project...

If I missed something in the question, feel free to ask.

Community
  • 1
  • 1
Sirop4ik
  • 4,543
  • 2
  • 54
  • 121
  • Because `libcodec` is compiled and linked separately from the rest of the program. Why should CMake guess that for you? – Botje Nov 19 '19 at 14:09
  • @Botje edited my question, added P.S block – Sirop4ik Nov 19 '19 at 14:16
  • Are you absolutely sure Visual Studio does this for *static* libraries (`.lib`s) _AND_ *shared* libraries (DLLs)? I would expect the shared situation to fail similarly in VS. – Botje Nov 19 '19 at 14:18
  • @Botje do you mean one of libs should be static and other one shared? I am not sure, but if it will be two of them static (.lib) , so they can to speak each to other – Sirop4ik Nov 19 '19 at 14:42
  • They do not 'speak to each other'. The compiler probably generates static libraries (`.lib` files) with unmet symbol references. It is only when you link everything together in an executable or another shared library that the unmet symbols are resolved. If you make `libplayer` and `libcodec` STATIC in your CMakeLists.txt you will see the same result. – Botje Nov 19 '19 at 14:44

1 Answers1

5

If your libcodec uses the implementations defined in log-lib, you must link log-lib to libcodec explicitly. This call:

target_link_libraries( # Specifies the target library.
        libplayer
        libcodec
        ${log-lib})

links libcodec and log-lib to libplayer, it does not link log-lib to libcodec. It implies this dependency graph:

        libplayer
        /       \
  libcodec     log-lib

The first argument to the target_link_libraries() call is the "linked-to" library, and all the following targets are linked to the first. Thus, you need to link log-lib to libcodec, like this:

target_link_libraries( # Specifies the target library.
        libcodec
        ${log-lib}
        )

Now, libcodec will know about the implementation defined in log-lib, implying the dependency graph here:

        libplayer
        /       \
  libcodec     log-lib
     /
log-lib

You can make this cleaner though. We can remove the direct link between libplayer and log-lib, and allow the log-lib implementation propagate through libcodec to libplayer.

target_link_libraries(libcodec PUBLIC
        ${log-lib}
        )

target_link_libraries(libplayer PRIVATE
        libcodec
        )

This will simplify the dependency graph to the following:

        libplayer
        /
  libcodec
     /
log-lib

See this section in the CMake documentation for how and when to use the PUBLIC and PRIVATE keywords when linking.

Kevin
  • 16,549
  • 8
  • 60
  • 74
  • So, if I understand you correctly this approach is actually right way for such implementation, right? – Sirop4ik Nov 19 '19 at 14:21
  • Yes, however, it would seem you can simplify this a bit. If `libplayer` depends on `libcodec`, and `libcodec` depends on `log-lib`, you link `log-lib` *only* to `libcodec`, and it will be **inhereted** by `libplayer` through `libcodec`. – Kevin Nov 19 '19 at 14:24
  • @AlekseyTimoshchenko I expanded my answer to show this simplification. Hope this is helpful! – Kevin Nov 19 '19 at 14:42