I wasn't sure what to search for for this one. So excuse me if this is simple. But let me outline the scenario and see what answers are out there.
Let's say I have a library which defines a structure like this:
struct Example {
int a;
#if B_ENABLED
int b;
#endif
};
This header gets installed as a part of the library's installation as a whole. My question here is that if my library defines B_ENABLED it will have a structure with these two variables included. However if my application does not define this as well. Then it will interpret the header as defining a struct with only one member.
Is the best way to handle this just to generate some kind of "options" header which would include all of the #defines that were specified in the library build?
My library builds with CMAKE. So a CMAKE solution for this is extra credit =D.
Solution #1 (configure + install)
Include config.hpp file in your header file(i.e. foo.hpp):
#ifndef FOO_HPP_
#define FOO_HPP_
#include "config.hpp" // FOO_DEBUG
class Foo {
public:
int result() const;
private:
int a_;
#ifdef FOO_DEBUG
int b_;
#endif // FOO_DEBUG
};
#endif // FOO_HPP_
config.hpp is output of configure_file command:
configure_file(config.hpp.in "${PROJECT_BINARY_DIR}/config/config.hpp")
include_directories("${PROJECT_BINARY_DIR}/config")
install(FILES Foo.hpp "${PROJECT_BINARY_DIR}/config/config.hpp" DESTINATION include)
input file config.hpp.in use special cmakedefine directive:
#ifndef CONFIG_HPP_
#define CONFIG_HPP_
#cmakedefine FOO_DEBUG
#endif // CONFIG_HPP_
Note that when you use installed library in other project:
you still need to specify include directories for library
if your library have dependencies you need to link them manually
you can't have 2 config files (Debug/Release)
Solution #2 (export/import target, recommended)
install(EXPORT ...) command can hold all information about using library
(aka usage requirements: including definitions, linked library, configuration etc):
add_library(Foo Foo.cpp Foo.hpp)
# Target which used Foo will be compiled with this definitions
target_compile_definitions(Foo PUBLIC $<$<CONFIG:Release>:FOO_DEBUG=0>)
target_compile_definitions(Foo PUBLIC $<$<CONFIG:Debug>:FOO_DEBUG=1>)
# This directory will be used as include
target_include_directories(Foo INTERFACE "${CMAKE_INSTALL_PREFIX}/include")
# This library will be linked
target_link_libraries(Foo PUBLIC pthread)
# Regular install
install(FILES Foo.hpp DESTINATION include)
# Install with export set
install(TARGETS Foo DESTINATION lib EXPORT FooTargets)
install(EXPORT FooTargets DESTINATION lib/cmake/Foo)
Installing such project will produce files (CMAKE_DEBUG_POSTFIX is d):
include/Foo.hpp
lib/libFoo.a
lib/libFood.a
lib/cmake/Foo/FooTargets-debug.cmake
lib/cmake/Foo/FooTargets-release.cmake
lib/cmake/Foo/FooTargets.cmake
Include FooTargets.cmake file to import installed library to project. For example using find_package command (need config, see configure_package_config_file):
add_executable(prog main.cpp)
find_package(Foo REQUIRED) # import Foo
target_link_libraries(prog Foo)
Note that:
path to include/Foo.hpp automatically added to compiler options
dependend library pthread is automatically added to prog linker option
definition FOO_DEBUG=0 added to Release build type
definition FOO_DEBUG=1 added to Debug build type
Rationale
So excuse me if this is simple
It is not (:
The root of the problem is ODR (C++ Standard 2011, 3.2 [basic.def.ord], p.3):
Every program shall contain exactly one definition of every non-inline function
or variable that is odr-used in that program; no diagnostic required. The
definition can appear explicitly in the program, it can be found in the
standard or a user-defined library
IMHO good general solution still not exists. Using CMake with imported configuration
can partially helps a little bit, but in some cases you still will get linker errors
(for example if you use library compiled with gcc, which linked to libstdcxx by default,
and try to link it to project with clang compiler, which linked to libcxx).
Some of this problems (not all, still) can be solved using toolchain files.
See examples.
Related
CMake tutorial
Exporting/importing targets
Modern CMake with Qt and Boost
Related
So I have a project (meant to be supported on MacOS, Linux, and Windows) where I am building a shared library and a set of executables linked to that library. In my root CMakeLists.txt I have added:
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
So that all symbols are hidden by default regardless of on Windows (which does this by default) or Linux. This will require me to manually export symbols of the public API. To help facilitate this, I've written a defines.hpp header in my project's utils/ directory which contains the following:
#ifndef __DEFINES_H_
#define __DEFINES_H_
#if defined(linux) || defined(__linux__)
#define EXPORT __attribute__((visibility("default")))
#elif defined(_WIN64) || defined(__WIN32__)
#if defined(COMPILE_LIB)
#define EXPORT __declspec(dllexport)
#elif
#define EXPORT __declspec(dllimport)
#endif
#endif
#endif
The idea being then I would include this header into any header that contains declarations that need to be exported (and subsequently imported as well) via:
#include "utils/defines.hpp"
class EXPORT MyCoolClass();
EXPORT void MyCoolFunction();
(I have not tested this yet so if there are any glaring issues with what I've written please let me know!)
My question is, it is not entirely clear to me how to define the COMPILE_LIB macro, given that the library and exectuables are built/linked with the same CMake setup. How can I ensure that the library is built using dllexport and then linked to the executables using dllimport within the same CMake build? Or would I need to take a different approach?
The accepted answer in this previous SO post seems to indicate using a compiler argument and building each separately, which isn't exactly what I am looking for. A separate answer indicates you should use the CMake defined projectname as when compiling the DLL it will define the macro projectname_EXPORTS. I'm also not sure that this will work again my shared library and the executables are all built within the same CMake project. (Relevant CMakeLists.txt files):
CMakeLists.txt for executables:
add_executable(quick-render quick_render.cpp)
target_link_libraries(quick-render ${PROJECT_NAME})
INSTALL(TARGETS quick-render DESTINATION bin)
...
CMakeLists.txt for library:
...
add_library(${PROJECT_NAME} SHARED
...
)
target_link_libraries(${PROJECT_NAME} PUBLIC
...
)
...
Would this approach still work here, or is it my CMake setup bad for using the project in multiple ways like this?
As Tsyvarev said in the comments,
Define the macro by using target_compile_definitions command with PRIVATE keyword. That way, the macro will be defined only when the library itself is compiled.
Adding to what Tsyvarev said, if you just use the GenerateExportHeader module, that part will be done for you automatically. It will also take care of generating C++ attributes for the compiler you configured the buildsystem to use.
When target_link_library(foo bar) is called with bar as a target of a SHARED library, CMake will use a static lib libbar.dll.a or bar.lib as an input on Windows. However, MinGW, for example, is capable of linking to a binary file like on Ubuntu. Is it possible to tell cmake to use a dll directly when target_link_library is called?
The obvious workaround is to use generator expressions:
target_link_libraries(foo PRIVATE $<TARGET_FILE:bar>)
Clearly, it has its shortcomings. When you link against a target, you also add its PUBLIC and INTERFACE included directories and linked libraries.
So, in order to fully link in that manner one would have to write something like:
get_target_property(INCLUDE_DIRS bar INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(LINK_LIBS bar INTERFACE_LINK_LIBRARIES)
target_include_directories(foo PRIVATE ${INCLUDE_DIRS})
target_link_libraries(foo PRIVATE ${LINK_LIBS} $<TARGET_FILE:bar>)
So the question is about the possibility to override CMake's default target_link_libraries behavior.
Windows shared object creation works quite differently to on Linux. On Linux, all symbols (functions, classes, etc) are visible to consumers of a shared library by default. On Windows, the opposite is true - nothing is visible by default. This feeds in to shared library creation, because Visual C++ will generate two files per shared library - a .dll and a .lib file. The consumer still links against the lib - not directly against the dll. If you're not explicitly exporting any symbols in your source code, then linking will fail, because no .lib file is generated.
In order to do this, you should basically (on a per-library basis) create a file called "library_api.h" which contains the following:
#ifndef LIBRARY_API_H
#define LIBRARY_API_H
#if defined(WIN32)
#if defined(MYAPI)
#define LIBRARY_API __declspec(dllexport)
#else
#define LIBRARY_API __declspec(dllimport)
#endif
#else
#define LIBRARY_API
#endif
#endif
Then, include that in your header files in which your functions are defined for that library, and prefix them like:
#included "library_api.h"
LIBRARY_API int myfunction();
and in the source file where the definition is, do the same.
Then in CMake, you need to add where the library is made:
add_library(library SHARED source.cpp)
target_compile_definitons(library PUBLIC LIBRARY_API)
Now, the lib file should be generated, and CMake will be able to find it, and hence everything should work.
Note that you can do something similar to the Windows default behaviour on Linux to also hide symbol visibility. You can pass the flag -fvisibility=hidden to GCC, and modify the header slightly using __attribute__ ((visibility ("default"))), and then the behaviour is the same as on Windows. That can be quite useful if your primary development environment is on Linux but you need to build on Windows, because you get the same sort of behaviour and so don't end up with something that works on it but not cross-platform.
I'm porting a shared library from CMake to QMake (for better integration with the rest of our codebase), and am having trouble in Windows with having qmake generate both the .lib and .dll files.
I have ported this over to Linux and MacOS (easier with Unix shared library formats), but am having trouble with Windows.
Please note that I am not using the Mingw compiler, but the MSVC 2015 compiler. Unfortunately, I can't use the Mingw compiler for various reasons.
Relevant code in my .pro file:
Qt -= core gui #not using the qt library
CONFIG += c++14 warn_on
TEMPLATE = lib
TARGET = MyLibrary
With CMake, I was able to set certain .h files as PUBLIC_HEADER files, and after running make install, it generated the proper .lib and .dll files.
But with qmake, it only generated a very tiny .dll file (<15kb). I tried adding CONFIG += static but that created a .lib file with all the symbols there, not the shared library files I am looking for.
Edit: After looking more into it, a better question could be:
Is there a qmake equivalent (or workaround) of CMake's PUBLIC_HEADER property?
In a nutshell: #Matt is right, you need to take active action in your source code to export classes, functions and other symbols. This is mandatory in Windows, but it is also a recommended practice in Unix. When the MSVC++ compiler sees exported symbols, it automatically creates both the .DLL and the .LIB import library.
To do this in a cross-platform way with qmake, a nice guide is following the pattern of the Qt Creator wizard: Welcome -> New Project -> Library -> C++ library. Project location..., name: 'testlib' (for instance). Next, next, choose your kit, next and finish. The files created by the wizard in the project directory are:
testlib.cpp
testlib_global.h
testlib.h
testlib.pro
testlib.pro.user
The interesting bits are in testlib.pro:
TEMPLATE = lib
DEFINES += TESTLIB_LIBRARY
And the header "testlib_global.h"
#ifndef TESTLIB_GLOBAL_H
#define TESTLIB_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(TESTLIB_LIBRARY)
# define TESTLIB_EXPORT Q_DECL_EXPORT
#else
# define TESTLIB_EXPORT Q_DECL_IMPORT
#endif
#endif // TESTLIB_GLOBAL_H
This header should be included in other headers, that would be using the TESTLIB_EXPORT macro like this (testlib.h):
#ifndef TESTLIB_H
#define TESTLIB_H
#include "testlib_global.h"
class TESTLIB_EXPORT Testlib
{
public:
Testlib();
// ...
};
The key is to define TESTLIB_LIBRARY in the project .pro that builds the library, so the TESTLIB_EXPORT macro is defined as Q_DECL_EXPORT, which in Windows is defined as __declspec(dllexport), and avoid defining TESTLIB_LIBRARY in the projects using the library. More about this here.
Now, the second part of your question. In CMake documentation, it says:
PUBLIC_HEADER
Specify public header files in a FRAMEWORK shared library target.
Shared library targets marked with the FRAMEWORK property generate
frameworks on macOS, iOS and normal shared libraries on other
platforms. This property may be set to a list of header files to be
placed in the Headers directory inside the framework folder. On
non-Apple platforms these headers may be installed using the
PUBLIC_HEADER option to the install(TARGETS) command.
So the PUBLIC_HEADER attribute is only about installing the public headers. To do so in qmake, you need to use INSTALLS in testlib.pro, for instance:
unix {
target.path = /usr/local/lib
headers.path = /usr/local/include
}
!isEmpty(target.path) {
INSTALLS += target
}
headers.files = $$HEADERS
!isEmpty(headers.path) {
INSTALLS += headers
}
I'm not very familiar with both cmake and mingw.
I have some source code that can be build with it (and the build process works fine with mingw32-make). The problem is, I would like to output the DLLs and not the .exe files.
I have some CMakeList file that I believe stores the configuration I have to change (that part should be responsible to produce the .exe files for .cpp files in Examples/ directory, taking some dependencies into account):
# C examples
if(PSMOVE_BUILD_EXAMPLES)
foreach(EXAMPLE example multiple dump_calibration battery_check)
add_executable(${EXAMPLE} examples/c/${EXAMPLE}.c)
target_link_libraries(${EXAMPLE} psmoveapi)
endforeach()
if(PSMOVE_BUILD_TRACKER AND PSMOVE_BUILD_TUIO_SERVER)
include_directories(${PSMOVEAPI_SOURCE_DIR}/external/TUIO_CPP/TUIO)
include_directories(${PSMOVEAPI_SOURCE_DIR}/external/TUIO_CPP/oscpack)
add_executable(tuio_server examples/c/tuio_server.cpp
external/TUIO_CPP/TUIO/TuioClient.cpp
...
external/TUIO_CPP/oscpack/ip/win32/NetworkingUtils.cpp
external/TUIO_CPP/oscpack/ip/win32/UdpSocket.cpp)
set_target_properties(tuio_server PROPERTIES
COMPILE_FLAGS -DOSC_HOST_LITTLE_ENDIAN)
target_link_libraries(tuio_server psmoveapi psmoveapi_tracker)
else()
# Disable the TUIO Server if we don't build the tracker
set(PSMOVE_BUILD_TUIO_SERVER OFF)
endif()
if(PSMOVE_BUILD_TRACKER)
foreach(EXAMPLE distance_calibration)
add_executable(${EXAMPLE} examples/c/${EXAMPLE}.c)
target_link_libraries(${EXAMPLE} psmoveapi psmoveapi_tracker)
endforeach()
endif()
endif()
I guess I should add -DBUILDING_EXAMPLE_DLL and -shared options somewhere. But where exactly? Or maybe I'm missing the point?
To make a dll you need add_library(mydlltarget SHARED mysourcefiles) instead of add_executable(myexetarget mysourcefiles)
Also to make a dll from code that was an executable you will at minimum have to modify your headers to export the classes/functions that you want exported. From the documentation for building a dll with mingw it appears that the process is similar to Visual Studio. Where you have a header define a macro that translates to __declspec(dllexport) when building the dll and __declspec(dllimport) when using the dll.
A sample of the export header looks like this:
#ifdef BUILDING_EXAMPLE_DLL
#define EXAMPLE_DLL __declspec(dllexport)
#else
#define EXAMPLE_DLL __declspec(dllimport)
#endif
Then the modification for your class is like this:
class EXAMPLE_DLL MyClass
{
public:
MyClass() {};
virtual ~MyClass() {};
void func(void);
};
Then you need to define BUILDING_EXAMPLE_DLL when building the dll. You can do that in CMake with an add_definitions(-DBUILDING_EXAMPLE_DLL)
Also CMake provides support for generating the export header with GENERATE_EXPORT_HEADER
Doc for GenerateExportHeader
I have a library that can both be used as an header-only library and as a traditional library. To enable this optional-header-only functionality, the library includes .cpp source files if compiled in header-only mode. Example:
// Vector.hpp
// (Module file), intended to be included manually by the user
#ifndef LIBRARY_MODULE_VECTOR
#define LIBRARY_MODULE_VECTOR
#include "Library/Vector/Inc/Vector2.hpp"
#include "Library/Vector/Inc/Vector3.hpp"
#include "Library/Vector/Inc/VectorUtils.hpp"
#if defined(LIBRARY_HEADERONLY)
#include "Library/Vector/Src/Vector2.cpp"
#include "Library/Vector/Src/Vector3.cpp"
#include "Library/Vector/Src/VectorUtils.cpp"
#endif
#endif
When the user includes Vector.hpp in one of his/her projects, if LIBRARY_HEADERONLY is defined, the implementation source files will be included right after the header files. Careful usage of the inline keyword is applied to avoid multiple definitions.
If LIBRARY_HEADERONLY is undefined, the .cpp files will be compiled and the library will have to be linked.
My build system of choice is CMake.
Using a CMake flag, the user can define or undefine LIBRARY_HEADERONLY.
The CMakeLists.txt file is similar to this:
# (not shown) set flag and cache variables...
# Include library directory
include_directories("./${INCLUDE_DIRECTORY}")
# Glob all library header/source files
file(GLOB_RECURSE SRC_LIST "${INC_DIR}/*" "${SRC_DIR}/*")
# (Not shown) Check if header-only mode is enabled
# (from now on we assume header-only mode is enabled and that
# LIBRARY_HEADERONLY is defined)
# Use all source/header files as a library target
add_library(HEADER_ONLY_TARGET STATIC ${SRC_LIST})
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
install(DIRECTORY ${INC_DIR} DESTINATION .)
Unfortunately, the CMake-generated makefile always compiles the .cpp files, even when header-only mode is enabled and the target is HEADER_ONLY_TARGET.
How can I prevent the CMake-generated makefile from compiling the source files in header-only mode?
Note that IDEs reliant on CMake-generated output, such as Qt Creator, should display both the header and source files as part of the project.
If I don't glob any source file, but only the .hpp header files, CMake will complain that no source files were selected for a library target, and IDEs that rely on CMake files will not display any item.
Try setting the source files' HEADER_FILE_ONLY property to prevent them from building, e.g.:
if (LIBRARY_HEADERONLY)
set_source_files_properties(${SRC_LIST} PROPERTIES HEADER_FILE_ONLY 1)
...
endif()
Also see the documentation.
if (Create_Header_Only)
add_library(TargetName INTERFACE)
# Clients will build with -DLIBRARY_HEADERONLY
target_compile_definitions(TargetName INTERFACE LIBRARY_HEADERONLY)
else()
add_library(TargetName STATIC thesource.cpp)
endif()
# Clients will build with -Iinclude
target_include_directories(TargetName INTERFACE include)
Client code just does:
add_executable(mine main.cpp)
target_link_libraries(mine TargetName)
See also Transitive Usage Requirements and all the rest of those CMake manuals regarding creating packages, etc.
An approach of defining all library types and letting the consumer choose between them is outlined in:
Opt-in header-only libraries with CMake
Something like:
add_library(lib_shared SHARED ...)
add_library(lib_shared STATIC ...)
add_library(lib_iface INTERFACE)
So that the consumer makes the choice of which to link to:
# target_link_libraries(consumer lib_static)
# target_link_libraries(consumer lib_shared)
target_link_libraries(consumer lib_iface)
Why do you not separate the GLOB for HEADERS and SRC files? You could add a dummy.cpp (a empty .cpp file) for creating header-only libraries. I mean:
# Glob all library header files
file(GLOB_RECURSE HEADER_ONLY_LIST "${INC_DIR}/*.hpp")
# Glob all library src files
file(GLOB_RECURSE SRC_ONLY_LIST "${SRC_DIR}/*.cpp")
# Check if LIBRARY_HEADERONLY is defined, so you can filter the target files
if(LIBRARY_HEADERONLY)
set(SRC_LIST ${HEADER_ONLY_LIST} dummy.cpp) # I don't know if it's needed to add a dummy.cpp file with your set_target_properties(HEADER_ONLY_TARGET ...)
else()
set(SRC_LIST ${HEADER_ONLY_LIST} ${SRC_ONLY_LIST})
endif(LIBRARY_HEADERONLY)
# Use all source/header files as a library target
add_library(HEADER_ONLY_TARGET STATIC ${SRC_LIST})
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
install(DIRECTORY ${INC_DIR} DESTINATION .)
Anyway, CMake 3.X.X gives you the chance for creating INTERFACE libraries (header-only ones).