Creating a executable out of a library in CMake, and linking the library in another library - cmake

I am trying to link foo-lib library into another target bar-lib however doing the following results in an error.
(add_executable):
Cannot find source file: foo-lib
How can I create an executable out of a library and the same library can be linked into another target?
add_library(foo-lib STATIC src/foo.cpp)
add_executable(foo-ut foo-lib)
target_include_directories(foo-ut PRIVATE include)
target_link_libraries(foo-ut PUBLIC lib1 lib2)
# second library that links foo-lib
add_library(bar-lib STATIC src/bar.cpp)
add_executable(bar-ut bar-lib)
target_include_directories(bar-ut PRIVATE include)
target_link_libraries(bar-ut PUBLIC foo-lib)
This worked the way I wanted but I am not sure if I should be adding foo.cpp for the bar-ut target
add_executable(bar-ut src/foo.cpp src/bar.cpp)

I'm not going to question the design choices and what you need it for and if it is a good idea in any way. I will just provide you with a "scalable" way of doing this.
As the others pointed out add_executable() requires source files.
Now assuming that the source files that you use to create a static library contain a main() function. Then you can create (out of the same source files) an executable, by passing to the add_executable the same source files as you would to add_library.
As the program grows these would get lengthy, so what you should do is something that is no longer recommended by "CMake best practices" and that is to introduce a SOURCES variable. I.e.:
set(PROJECT_SOURCES source1.cpp source2.cpp source3.cpp)
set(PROJECT_HEADERS header1.h header2.h header3.h)
add_library(foo-lib STATIC ${PROJECT_SOURCES} ${PROJECT_HEADERS})
add_executable(foo-ut ${PROJECT_SOURCES} ${PROJECT_HEADERS}
As your program grows you would just add the respective files into the designated variables. Now as to possible improvements:
fabian mentioned a very good thing which is OBJECT libraries, since you are rebuilding the same files for both the executable and library you could just create an object library and link it. This would make it twice as fast (you only need to compile once).
Since these SOURCES are already once passed to some target, you could just get them from the target's properties via get_target_properties(MY_SOURCES foo-lib SOURCES) this would give you a variable MY_SOURCES that contains sources which are used by the target library.

Related

How to link a shared library without having to export its internal targets

The following repro cmake project fail to configure. The goal is to create a shared library that internally consists of a couple of static libraries. I want the internal symbols and include paths etc to be exported from the shared library. To do that I make the internal libs PUBLIC link libraries. But then cmake tells me I need to export my target. I don't want to "pollute" my package config with a bunch of internal targets. Is there no way to "merge" internal targets (static libs) into the public exported target (shared lib) or hide it in the config targets file?
The SystemC::systemc imported target does not show up in my target file and it also don't generate any error. I assume that is because this target is already exported in its own package? If so, would that mean I need to make my internal libs packages and so imported targets to make them go away from my exported targets list?
cmake_minimum_required(VERSION 3.16)
project(mylib)
find_package(SystemCLanguage 2.3.3 CONFIG REQUIRED)
add_library(mysublib STATIC mysublib.cpp)
add_library(mylib SHARED mylib.cpp)
target_link_libraries(mylib
PUBLIC SystemC::systemc
PUBLIC mysublib
)
export(
TARGETS mylib
NAMESPACE MyLib::
FILE MyLibTargets.cmake
)
Error generated at configure
CMake Error in CMakeLists.txt:
export called with target "mylib" which requires target "mysublib" that is
not in any export set.
The goal is to create a shared library that internally consists of a couple of static libraries. I want the internal symbols and include paths etc to be exported from the shared library. To do that I make the internal libs PUBLIC link libraries.
So, first, I'll note that PUBLIC has nothing at all to do with symbol visibility. Your example mysublib doesn't have any INTERFACE properties, so you could make it PRIVATE to mylib and avoid the need to export mysublib.
However, if you do need mysublib in the INTERFACE of mylib, then you do need to export it, period. There is no way to, as you say "'merge' internal targets [...] into the public exported target [...] or hide it in the config targets file". But this is also not a real problem.
If you're concerned about people relying on mysublib, then you can set the EXPORT_NAME property of mysublib to something that indicates it's not meant to be used, like _private_mysublib:
set_target_properties(mysublib PROPERTIES EXPORT_NAME _private_mysublib)
If you want to be really aggressive about it, you could even make the name random:
string(RANDOM LENGTH 12 mysublib_export)
set_target_properties(mysublib PROPERTIES EXPORT_NAME "x${mysublib_export}")
Prepending an x makes sure it doesn't start with a number. There is also a remote chance of collision if you do this a lot. If you want to be absolutely safe, you should write a function that records the names it's already returned in a global property and tries again if it chooses a collision.
The SystemC::systemc imported target does not show up in my target file and it also don't generate any error. I assume that is because this target is already exported in its own package? If so, would that mean I need to make my internal libs packages and so imported targets to make them go away from my exported targets list?
You are correct: because SystemC::systemc is imported, it does not need to be (and in fact cannot be) re-exported. You're expected to call find_dependency(SystemCLanguage 2.3.3) in your MyLibConfig.cmake file, both within the build tree (export) and after install (install(EXPORT)).
If you want this behavior to apply to mysublib, then yes, you will need to split your projects apart. I don't see a compelling reason to do that, though. Either make mysublib PRIVATE or just live with it being renamed in your package config. Targets aren't precious and they're namespaced, so there's no real reason to worry.

What happens with target_link_libraries(libstatic libfoo libbar)?

I am bit confused about linking libraries when our target is a static library.
For instance, for an executable it will help linker resolve undefined symbols. But, incase of a static libraries, why would it link at this stage?
Won't linking be done when I will link some executable against libstatic?
Thanks.
In CMake,
target_link_libraries(targetName PUBLIC lib1 lib2)
affects to the linker's argument in two scenarios:
PRIVATE: when the linker is called for the for the executable/library, corresponded to the target targetName.
INTERFACE: when the linker is called for the other executable/library otherTargetName, which is linked with targetName via
target_link_libraries(otherTargetName PUBLIC targetName)
This is known as transitive property of the linking libraries.
You are right that the linker is not called for the static libraries, so in that case the first scenario is eliminated.
But the second scenario remains: When you create the executable (or other shared library) and call
target_link_libraries(otherTargetName PUBLIC libStatic)
then CMake automatically links that executable(or shared library) with everything, to which libStatic is "linked" with target_link_libraries.
Such automation helps in structuring the project:
By calling
target_link_libraries(libStatic PUBLIC lib1 lib2)
you state, that libStatic uses functions defined in lib1 and lib2
By calling
target_link_libraries(otherTargetName PUBLIC libStatic)
you state, that executable/library otherTargetName uses functions from libStatic.
At this stage you don't care about internals of libStatic, whether it is self-contained or depends from some other libraries: CMake will care about this for you.
Note on using PUBLIC keyword in target_link_libraries: while in some cases this is equivalent to omitting the keyword, a modern CMake way is to specify keywords explicitly. See also policy CMP0023.
Other possible keywords are PRIVATE and INTERFACE, each of them selects only a single scenario described above.
Note that transitive linking property is a pure CMake feature and works only when linking to a target. The library file (.a or .lib) itself doesn't contain information about dependent libraries, so linking with a file doesn't trigger transitive linking.

Is all of a static library included in a final product after linking?

Suppose I create an iOS application. I include a static library. I create an object of a class that is defined and implemented in static library. This object doesn't use other classes defined in the library. Will all of the static library be present in the application I build? The idea is that much of the static library contains unused code and wouldn't need to be present.
I believe there a flags that help determine the behavior -- if someone can spell out how this works, I sure would appreciate it.
A static library is an archive of object files. If you link against a static library libfoo.a then
the linker by default will link into your final executable all and only those object files in libfoo.a
that are required to provide definitions for the public symbols that are referenced by the program.
More specifically, if the linker finds the library requested (via the option -lfoo) at a given
point in the commandline sequence of object files and libraries to be linked, then it will
extract from the archive and link into the executable each object file in the archive that provides
a definition for any symbol that remains undefined up to that point in the linkage.
In so doing, definitions of unused public symbols may be redundantly linked into
your program, but only if they are found in an object file (whether free-standing or a member of
a library) that is not completely redundant.
If you do not want to tolerate even those potential redundancies, then a combination of
compiler and linker options can eliminate them: see this answer

"Linking" my static lib with a third-party static lib

I'm writing a CMake script that makes a static library MyLib. I'd like to concatenate it with a 3rd party static library AnotherLib. I try to accomplish this as follows:
"Deconstruct" AnotherLib to object files by invoking ar as a part of ADD_CUSTOM_COMMAND.
Prepare an intermediate MyLibObj using ADD_LIBRARY(MyLibObj OBJECT ${MYLIB_SOURCES})
Make the final MyLib using (1) and (2) -- here is the problem. How to do this? Is it possible to make CMake treat the object files made in (1) as OBJECT library?
This CMake script merges several static libraries - but in a straightforward way, without the new OBJECT feature. It's a part of MERGE_STATIC_LIBS macro referenced in this answer.

Getting CMake to give an error/warning about unreferenced symbols

I'm wondering how I would go about making CMake produce an error, or at least a warning, when the linker cannot find symbols that are referenced in a source file?
For example, let's say I have foo.c:
#include "bar.h" //bar.h provides bar()
void foo(void)
{
bar()
return;
}
In the case that I am building a static library, if i am not smart about how i have used my add_library() directive, the default behavior seems to be to not even give a warning that bar is an unreferenced symbols in foo's object archive file (.a)
The CMAKE_SHARED_LINKER_FLAGSĀ compiler flags for building shared libraries should get the compiler to do what you want.
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
On Unix systems, this will make the linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order).
Source: http://www.cmake.org/Wiki/CMake_Useful_Variables
There's the -z now for the GCC linker these days, but yeah, this isn't CMake's problem.
The most fool-proof way I've found only works on shared libraries, but what you do is basically write a test for each shared library and it then just does dlopen(path, RTLD_NOW) (and similar for Windows) and then use its return value as the test return value. To get a list of all shared objects, I have a wrapper function around add_library which adds all shared libraries to a global property which then is used to generate the tests dynamically. I remember there being some way to tell if a target was shared or static, but I'm not finding it the docs right now.