Creating Shared libraries and an executable

I’m a cmake newbie and I’m converting a rather large existing project to cmake so the basic tutorials don’t seem to help that much, so after Google and the help files, here I am…

The overall concept of the build is a small single file executable (essentially just a main()) and a series of shared libraries.

My main struggle now seems to be how to create the target so that it triggers the building of the libraries, but doesn’t explicitly link with them. What I get now is the creation of the .so libraries, however the executable attempts to also link together all of the object files from the libraries. Am I barking up the wrong tree here or should I separate the creation of the libraries into a new/separate cmake file?

More details available obviously, but I first wanted to see if what I am attempting is possible, and that is to (within the same build target, create .so libraries and dynamically link against them to create an executable).


Use add_executable and add_library.

You can use add_dependencies if you don’t want to link/establish dependency using target_link_libraries.

My recommendation is this book:

Ah, and it was as simple as that, thank you. And thanks, I’ll check out that book.

Ok, I guess I replied a bit prematurely.

Using the add_dependencies() does allow the separation between the executable and the libraries, so that is good, however the executable still contains the libraries.

I have defined my executable as:

add_executable( myexec EXCLUDE_FROM_ALL ${PROJECT_SOURCE_DIR}/main.cpp)

and added a library:

add_library(mylibA SHARED)
target_sources(mylibA PUBLIC Path/ToLibA/Source )
set_target_properties(mylibA PROPERTIES LINKER_LANGUAGE CXX)

as suggested, I then added a dependency on the library to the exec:

add_dependencies( myexec mylibA )

This creates the library however, on it’s own, fails to build the executable myexec and rightly so because we haven’t yet identified that it requires the library. My thought would then to be to add the line:

target_link_libraries( myexec LINK_PUBLIC mylibA )

however as I mentioned, this creates a myexec that contains not just the code for main.cpp as hoped, but also the code from the shared library mylibA.

The LINK_PUBLIC keyword is deprecated and only kept for compatibility reasons. The preferred keyword is PUBLIC.

However, executables usually don’t need a link interface, so it would make more sense to use the PRIVATE keyword here.

This would mean that mylibA is a static library, not a shared one. Maybe you had add_library(mylibA) instead of add_library(mylibA SHARED) initially and you already built a static library. You should consider deleting/cleaning the build directory and build again.

Using PUBLIC here means "make these sources part of mylibA and also make them part of whoever links to mylibA". That is how the sources are getting into your executable. You should probably be using PRIVATE here. Actually, I don’t see a reason for you to use target_sources() at all: just list the source files directly in the add_library() command, and they will be PRIVATE by default.

It’s extremely rare to need to use anything other than PRIVATE with target_sources(). I answered a similar question recently, see that for more info.

I’ve changed all LINK_* to be the updated versions, I should have mentioned off the top as well, that I’m using:

cmake_minimum_required (VERSION 3.17)

Since I’m still trying to figure this all out, I by default clear the build directory and start from scratch on every build. The link paths are:

target_link_libraries( myexec PRIVATE ${CMAKE_BINARY_DIR}/ )

so it’s not pulling in some stale version, although a good suggestion.

Thanks Petr,

I understand your explanation, however oddly, I tried both options (setting the sources as both PUBLIC and PRIVATE) and I see no difference in the output. In both cases, the library and the executable create the same outputs.

As for using the target_sources() line, this is where the simple examples don’t really match larger projects. In my case, it’s not just one library, but 8 of them, plus a few 3-rd party .a libs. The libraries themselves vary from having their source in one directory to having them spread out over multiple directories (and not necessarily all of the source files in those directories), each with potential different compile options, , etc, etc.

Perhaps this all stems from the way that I am creating the libs in the first place. I’ve looked for examples on this and again they seem to all offer the simple solution of just listing the source files directly in the add_library() call.

They way it’s being done now is that in a given directory, a shared library is created as:

add_library(mylibDirA SHARED <local source files> )

then it’s added to the mylibA target:

target_link_libraries( mylibA PRIVATE mylibDirA $<TARGET_OBJECTS:mylibDirA> )

Why you ask? Well that’s just the way that it was created from the existing build mechanism through the use of some scripts. If there’s a better (correct) way to do this then that may be the source of the problem.

The goalposts keep moving on this one. Can you please reduce your real project into an SSCCE that will actually reproduce your problems while being as minimal as possible, and post that? It’s much easier to look for bugs in code when one can see that code.

Sorry, I was being intentionally vague to try to get the obvious out of the way since what I’m trying to do is a lot more complicated, but the basic premise still stands. I’ll try to reproduce it with a shorter example of the concepts.

As suspected, when I create a simple example, it works. I’ll start ‘adding’ in some of the concepts that the full project uses and see where it breaks.

So my next possible source of an issue is the link order. I was able to get back to where I was using the test example format however it was giving me the same results. So at least I know that the top level format is correct. So I went to look at the linking output of cmake to see what the difference was between it and what I was previously using.

One of the libraries created contains some source code and is also built with some 3rd party deliverables (.a’s), so they are added:

target_link_libraries( libA_lib PRIVATE ${PROJECT_SOURCE_DIR}/3rdPatry/lib3rdParty.a )

What I see in the link.txt file however is that the .a’s are added BEFORE the object files. In my executable I also had to add the 3rd party library to it’s target_link_libraries() and I couldn’t understand why, but I’m realizing now it’s because the libA_lib didn’t actually add in the code because there was no reference to it (as the object files came after the archives). To test the theory I manually modified the link.txt file to put the .a’s at the end of the link and voila it ‘works’.

So now the question was how to you specify the link order? Or the larger question is why would cmake not place object file first in the linker?

Short term solution I have is to add the option:

add_link_options( -Wl,--start-group )

Not ideal, but it at least gets the library generation correct, or so it seems.

In summary, it looks like the format I was using was correct all along, the issue was that the link order on one of the libraries was incorrect requiring the executable to include all of the 3rd party code to resolve the remaining symbols.