Difference between modules and shared libraries? - module

The title mostly covers it, what is the difference between a module and a shared library? I just found this distinction in CMake's add_library command, where they say:
SHARED libraries are linked dynamically and loaded at runtime. MODULE libraries are plugins that are not linked into other targets but may be loaded dynamically at runtime using dlopen-like functionality.
But I can load a shared object using dlopen(), can't I?

The difference is that you can link to a SHARED library with the linker, but you cannot link to a MODULE with the linker. On some platforms.
So... to be fully cross-platform and work everywhere CMake works, you should never do this:
# This is a big NO-NO:
add_library(mylib MODULE ${srcs})
target_link_libraries(myexe mylib)
To be fair, on Windows, they're both just dlls, and so this code might actually work. But when you take it to a platform where it's impossible to link to the MODULE, you'll encounter an error.
Bottom line: if you need to link to the library, use SHARED. If you are guaranteed that the library will only be loaded dynamically, then it's safe to use a MODULE. (And perhaps even preferable to help detect if somebody does try to link to it...)

I think the distinction being made is that shared libraries are specified by the developer at compile-time and must be present for the application to run, even though their methods are loaded at runtime. A module, i.e. plugin, adds additional support at runtime but isn't required. Yes, you can dlopen() a shared library but in that case it would not have been specified as a required part of the program and functions instead as a module.

Another difference is in how ..._OUTPUT_DIRECTORY and ..._OUTPUT_NAME are handled:
Module libraries are always treated as library targets. For non-DLL platforms shared libraries are treated as library targets. For DLL platforms the DLL part of a shared library is treated as a runtime target and the corresponding import library is treated as an archive target. All Windows-based systems including Cygwin are DLL platforms.
For example, this means that if you compile a SHARED library on Windows, LIBRARY_OUTPUT_DIRECTORY will be ignored, because it's looking at ARCHIVE_OUTPUT_DIRECTORY and RUNTIME_OUTPUT_DIRECTORY instead.

Related

How is CMake supposed to work for shared libraries?

I am trying to figure out how CMake is supposed to work for shared libraries. I create a shared library like so:
(in /mdp_opt/CMakeLists.txt:)
add_library (mdp_opt SHARED librarycomponent.cpp)
Now, a test executable uses this library:
(/test/CMakeLists.txt:)
add_executable (test test.cpp)
target_link_libraries(test PRIVATE mdp_opt)
If the library is marked STATIC (instead of SHARED as above), I can cmake -> built (under Visual Studio) and successfully run the executable. When it is marked SHARED (as in above), I need to do two things. First thing, is adding:
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
That is fine. However, now it still only works if I copy the file mdp_opt.dll from build/x64-debug/mdp_opt to build/x64-debug/test. But I don’t understand why this is needed?
In the book “professional CMake”, I read:
LINK_LIBRARIES
This target property holds a list of all libraries the target should
link to directly. It is initially empty when the target is created and
it supports generator expressions. An associated interface property
INTERFACE_LINK_LIBRARIES is supported. Each library listed can be one
of the following (emphasis mine):
• A path to a library, usually specified as an absolute path.
• Just the library name without a path, usually also without any
platform-specific file name prefix (e.g. lib) or suffix (e.g. .a, .so,
.dll).
• The name of a CMake library target. CMake will convert this to a
path to the built library when generating the linker command,
including supplying any prefix or suffix to the file name as
appropriate for the platform. Because CMake handles all the various
platform differences and paths on the project’s behalf, using a CMake
target name is generally the preferred method.
I was under the impression that
target_link_libraries(test PRIVATE mdp_opt)
expresses that I intend to link the output associated with the target mdp_opt with the test executable? Also, in my reading of the above book excerpt, my understanding is that the location of the .dll will convert to a path? If the purpose of this conversion is not to somehow tell the executable where to find the shared library, then what is that conversion for?
Basically, can anybody tell me how CMake is supposed to work for shared libraries, and why is works like that? Is a manual post-copy (possibly via CMake instructions) really needed, and the best for this scenario (of intermediate builds while developing)?
On windows you need to export symbols of a shared library or add a linker option that results in symbols being exported; otherwise the .lib file for linking the dll simply isn't generated. This is why you need
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
, use __declspec( dllexport ) or similar.
The other issue is the way windows locates dlls: It basically first searches the directory containing the executable and then continues the search via the PATH environment variable. This can result in issues locating the dll at runtime.
You can avoid this issue by setting the directory to place the runtime artefacts before generating the first one of the targets involved:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
...
add_library(mdp_opt ...)
...
add_executable(test ...)
Alternatively you could simply change the environment variable visible to the vs debugger:
set_target_properties(test PROPERTIES VS_DEBUGGER_ENVIRONMENT "PATH=$<TARGET_FILE_DIR:mdp_opt>;$ENV{PATH}")
If you're working with CTest, there's also a similar property for tests: ENVIRONMENT
If the purpose of [target_link_libraryes] is not to somehow tell the executable where to find the shared library, then what is that conversion for?
You still need to link a dll, or more precisely the corresponding import library; this is what target_link_libraries does, in addition to adding build system dependencies, transferring some properties (e.g. include directories with PUBLIC visibility), ect.

How can I "add_dependencies(..)" to an "UNKNOWN IMPORTED" library?

I want to use this way of getting googletest in my project. But there seems to be something wrong:
ninja: error: 'libsuper/test/googletest-prefix/src/googletest-build/googlemock/gtest/libgtest.a', needed by 'libdsuper/test/libsuper_test', missing and no known rule to make it
As I understand the error, there is no gtest library available when ninja tries to build the test-executable. As a consequence, I added a dependency just after the test-target:
add_executable(${PROJECT_NAME}_test ${SOURCES})
add_dependencies(${PROJECT_NAME}_test ${GTEST_LIBRARY})
But this has no effect. It seams like it's silently ignored. I guess this is because IMPORTED is meant for system libraries that need not to be compiled and are thus available from the start. In my case, this is however not true. The googletest libraries are only available after they have been compiled by the ExternalProject_Add.
Is this also linked to ninja? The site linked above does not mention problems - and I could imagine that's because googletest and libSomeOtherThingThatUsesGTest are build in sequence, as make does.
How can I instruct cmake to wait until the gtest library is truly available?
There is a little sense to be depended on IMPORTED library target - there is no actions bonded with such target, so you are actually depends on nothing.
Instead, you should depend on a target, which creates the library. In case of External Project, this is a target created for such project (the first argument to the ExternalProject_Add function's call).
In case of normal (non-IMPORTED) library target situation is quite different: actions for create such library are bonded with the target itself, so being dependent from the library target means that library should be built before the dependee. But such dependence is rarely used: instead, one links with the library target, which by itself implies the dependency from the actions, bonded with that target.
for googletest you should use:
add_executable(${PROJECT_NAME}_test ${SOURCES})
target_link_directories(${PROJECT_NAME}_test gtest)
Dependency will be automatically managed.
Also the imported target gtest already depends on googletest ExternalProject_Add according to this
I guess the errors come from the fact that the path for the imported target file location aka ${binary_dir}/googlemock/gtest/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a is wrong IMHO.

cmake target_link_libraries - unwanted target

I have a problem with cmake target_link_libraries.
I have 3 libs. The first is static compiled, the second one (shared lib) link to it and the third one is an executable which use the second lib.
My problem is that my first lib is automatically added to the third lib and leads into a "object already defined" problem.
Is it possible to hide away the first lib from the third one?
I use cmake 3.4.x
Compiler: msvc 2010 x64
Thanks in advance
Tonka
Your third "lib" isn't a library, but an application. You need to add this using add_executable, not add_library.
If your shared library links in a static library, and then you want to link an application to both the static library and that shared library, you get two copies of the static library. Never link static libraries you plan to use elsewhere into a shared library, for this reason. Either make the first shared as well (the name implies that's what you want, as it is exactly what you are describing), or a workaround for this design problem could be to not explicitly link the application to the static library.
I've solved it. I can link to a library private, so f.e.
target_link_libraries(MyLib2 PRIVATE MyLib1)
will hide MyLib1 from everybody linking to MyLib2

How to find RelWithDebInfo or MinSizeRel libraries with CMake?

I'm trying to link my project to a external library that I also developed in which also also use CMake to build. When I try to find RelWithDebInfo or MinSizeRel like this:
FIND_LIBRARY(PCM_LIBRARY_DEBUG pcm
PATHS #CMAKE_LIBRARY_OUTPUT_DIRECTORY#
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/Debug
NO_DEFAULT_PATH
)
FIND_LIBRARY(PCM_LIBRARY_RELEASE pcm
PATHS #CMAKE_LIBRARY_OUTPUT_DIRECTORY#
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/Release
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/MinSizeRel
#CMAKE_LIBRARY_OUTPUT_DIRECTORY#/RelWithDebInfo
NO_DEFAULT_PATH
)
SET(PCM_LIBRARIES debug ${PCM_LIBRARY_DEBUG} optimized ${PCM_LIBRARY_RELEASE})
It does not search in ather directories that are not Release or Debug. I also tried creating PCM_LIBRARY_RELWITHDEBINFO and PCM_LIBRARY_MINSIZEREL but the same thing happens because there is only debug and optimized prefixes in SET. Anyone knows how can I link the correct libraries?
This is unfortunately one of the shortcomings of using find_library. There is no easy way around this without introducing tons of boilerplate code.
The problem here is that when passing files as dependencies to target_link_libraries, you can only distinguish between debug and optimized. If you need more fine-grained control, you will have to manipulate the respective target properties like LINK_INTERFACE_LIBRARIES directly. This is not only quite cumbersome, it also requires detailed knowledge about the inner workings of CMake's property system.
Fortunately, there is another way: The aforementioned limitation only applies when specifying dependencies via filenames. When specifying them as targets, this problem does not occur. The most obvious example is if a library and the executable that depends on it are built from the same source:
add_library(foo_lib some_files.cpp)
add_executable(bar_exe more_files.cpp)
target_link_libraries(bar_exe PUBLIC foo_lib)
This 'just works'. The correct library will be chosen for each build configuration. Things get a little more complicated if the library and the executable live in different independent projects. In that case the library has to provide a configure file with an exported target in addition to the binary files.
Instead of calling find_library to locate the binaries, the dependent executable now just loads that config file and can then use the imported target as if it was a target from the same project.
Many modern libraries already use this approach instead of the classical find_library technique (Qt5 is a prominent example). So if you are at liberty to change the CMakeLists of your dependency and you do not need to support very old CMake versions (<2.6), this is probably the way to go.

What's the meaning of dylib files?

My C++ compiler creates "dylib" files which contain dynamic libraries. Whats the difference between .dylib and .so files?
And what is the difference between files in Mach-O format and files in an ELF format? I have to build files for later use under iOS (static libraries only/Mach-O) and Android (ELF).
Thanx!
I found that:
One Mach-O feature that hits many people by surprise is the strict
distinction between shared libraries and dynamically loadable modules.
On ELF systems both are the same; any piece of shared code can be used
as a library and for dynamic loading. Use otool -hv some_file to see
the filetype of some_file.
Mach-O shared libraries have the file type MH_DYLIB and carry the
extension .dylib. They can be linked against with the usual static
linker flags, e.g. -lfoo for libfoo.dylib. However, they can not be
loaded as a module. (Side note: Shared libraries can be loaded
dynamically through an API. However, that API is different from the
API for bundles and the semantics make it useless for an dlopen()
emulation. Most notably, shared libraries can not be unloaded.) [This
is no longer true—you can use dlopen() with both dylibs and bundles.
However, dylibs still can't be unloaded.]
Loadable modules are called "bundles" in Mach-O speak. They have the
file type MH_BUNDLE. Since no component involved cares about it, they
can carry any extension. The extension .bundle is recommended by
Apple, but most ported software uses .so for the sake of
compatibility. Bundles can be dynamically loaded and unloaded via dyld
APIs, and there is a wrapper that emulates dlopen() on top of that
API. [dlopen is now the preferred API.] It is not possible to link
against bundles as if they were shared libraries. However, it is
possible that a bundle is linked against real shared libraries; those
will be loaded automatically when the bundle is loaded.
To compile a normal shared library on OS X, you should use -dynamiclib
and the extension .dylib. -fPIC is the default.