I am on Windows 10, Visual Studio 2015. Suppose I am building library A with CMakeLists looking like
cmake_minimum_required(VERSION 3.7)
project(A)
set(DLLIMPORT "__declspec(dllimport)")
set(DLLEXPORT "__declspec(dllexport)")
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/src/TestA.cpp)
set(PROJECT_INCS
${PROJECT_SOURCE_DIR}/include/TestA.h)
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS} ${PROJECT_INCS})
target_compile_definitions(${PROJECT_NAME} INTERFACE
WINDOWS_DLL_API=${DLLIMPORT})
target_compile_definitions(${PROJECT_NAME} PRIVATE
WINDOWS_DLL_API=${DLLEXPORT})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)
I am defining the macro WINDOWS_DLL_API as dllexport when it's building library A, and defining WINDOWS_DLL_API as dllimport for external applications that is linking library A. The problem is when I have another library B that is also linking A, I don't know how to overwrite WINDOWS_DLL_API back to dllexport. Below is my attempt of my CMakeLists for library B,
cmake_minimum_required(VERSION 3.7)
project(B)
set(DLLEXPORT "__declspec(dllexport)")
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/src/TestB.cpp)
set(PROJECT_INCS
${PROJECT_SOURCE_DIR}/include/TestB.h)
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS} ${PROJECT_INCS})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)
target_link_libraries(${PROJECT_NAME} A)
# does not work
target_compile_definitions(${PROJECT_NAME} PRIVATE
WINDOWS_DLL_API=${DLLEXPORT})
What is the right way to do it?
Concept of INTERFACE option for command target_compile_definitions (and for other target_* CMake commands) is to enforce something for all users of the library, both executables and libraries.
Intention to clear enforcement for at least single library's user means that the conception is used in a wrong way. And other approaches should be used instead.
In given case, you need to use different macro names for libraries A and B. And it is better to remove INTERFACE option completely, so even non-CMake users of your library will be happy.
TestA.h:
#ifdef BUILD_A
#define WINDOWS_DLL_API_A __declspec(dllexport)
#else
#define WINDOWS_DLL_API_A __declspec(dllimport)
#endif
...
WINDOWS_DLL_API_A void foo(void);
...
TestB.h:
#ifdef BUILD_B
#define WINDOWS_DLL_API_B __declspec(dllexport)
#else
#define WINDOWS_DLL_API_B __declspec(dllimport)
#endif
// Assume usage of A is here.
#include <TestA.h>
...
WINDOWS_DLL_API_B void bar(void);
A/CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(A)
...
add_library(${PROJECT_NAME} SHARED ...)
target_compile_definitions(${PROJECT_NAME} PRIVATE "BUILD_${PROJECT_NAME}=1")
B/CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(B)
...
add_library(${PROJECT_NAME} SHARED ...)
target_compile_definitions(${PROJECT_NAME} PRIVATE "BUILD_${PROJECT_NAME}=1")
target_link_libraries(${PROJECT_NAME} A)
See also this answer, which provides more detailed header, which works on Windows platforms too.
Note, that when the library B includes header from A, it treats foo() as imported, and this is correct: the function is defined in A, not in B. With your approach (even if you would manage to redefine WINDOWS_DLL_API for B), library B would incorrectly treat foo() as exported.
This is an advantage of the conception: intention to overcome a conception signals that you do something wrong.
Just wanted to add my piece of code I'm using (compatible with CMake versions prior to 2.8.12).
In my root CMakeLists.txt file I have:
if (MSVC)
add_definitions(-DWINDOWS_DLL_API=__declspec\(dllexport\))
else()
add_definitions(-DWINDOWS_DLL_API=)
endif()
In the (sub-)project's CMakeLists.txt using the DLL I've put:
if (MSVC)
remove_definitions(-DWINDOWS_DLL_API=__declspec\(dllexport\))
add_definitions(-DWINDOWS_DLL_API=__declspec\(dllimport\))
endif()
The MSVC checks are necessary in my case because I also cross-compile.
Reference
CMake - override compile flags for single files
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.
I have Project A at the top.
Project A requires library B and library C.
Library B also requires library C by itself.
So, in short. How can I link libraries B and C up to A by just linking B?
I have it working now by individually linking all the libraries, but I feel like there is redundancy I could get rid of.
This is part of the CMAKE for Project A at the top:
find_package(libB REQUIRED)
include_directories(${libB_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libB_LIBRARY})
find_package(libC REQUIRED)
include_directories(${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
But also within libB I have this in its CMAKE:
find_package(libC REQUIRED)
include_directories(${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
I feel like there is a better way to do this, please let me know. Thank you for any help.
You can use target_include_directories (documentation) to specify include directories for your target instead of specifying them for all targets of the current directory using include_directories. Among other things, this gives the ability to provide visibility specifiers to your includes dirs, which control whether your include dirs will affect only current target (PRIVATE), only those targets which link to your target (INTERFACE) or both (PUBLIC). The same specifiers are used similarly for target_link_libraries.
So, you can use PUBLIC to avoid duplicating your lib C includes and libraries, and since it is the default specifier, you can omit it. Then, parts of your CMakeLists may look like this:
In project A CMakeLists.txt:
find_package(libB REQUIRED)
target_include_directories(${PROJECT_NAME} ${libB_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libB_LIBRARY})
In lib B CMakeLists.txt:
find_package(libC REQUIRED)
target_include_directories(${PROJECT_NAME} ${libC_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${libC_LIBRARY})
If you, e.g., want only lib B to link to lib C, but leave public 'inheritance' of include directories, you could change the last line of lib B CMakeLists.txt excerpt:
target_link_libraries(${PROJECT_NAME} PRIVATE ${libC_LIBRARY})
I am trying to run swig using CMake and I am getting
java.i:9: Error: Unable to find 'api.h'
java.i:10:Error: Unable to find 'TestStruct.h'
I have tried setting USE_LIBRARY_INCLUDE_DIRECTORIES, SWIG_USE_TARGET_INCLUDE_DIRECTORIES, SWIG_USE_LIBRARY_INCLUDE_DIRECTORIES, without any luck. I am probably failing to understand something that is going on. My understanding was that I am linking to the api target so those include directories should be visible.
Note: That it works if I copy those headers into the build directory where swig is getting called.
Any information will be helpful. Thanks.
java.i
%module example
%include "std_string.i"
%{
/* Put header files here or function declarations like below */
#include "api.h"
extern TestStruct TestFunc();
%}
%include "api.h"
%include "TestStruct.h"
extern TestStruct TestFunc();
CMakeLists.txt for the swig target
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
find_package(Java REQUIRED)
find_package(JNI REQUIRED)
set_property(SOURCE java.i PROPERTY CPLUSPLUS ON)
swig_add_library(javaExample
TYPE SHARED
LANGUAGE java
SOURCES java.i)
target_include_directories(javaExample PRIVATE ${JNI_INCLUDE_DIRS})
target_link_libraries(javaExample PRIVATE api)
CMakeLists.txt for the api target
add_library( api SHARED
inc/api.h
src/api.cpp
)
target_include_directories( api
PUBLIC
inc
)
target_link_libraries( api
PUBLIC
FooTypes
PRIVATE
FooBar
)
add_subdirectory(src/FooBar)
Found the problem. I was unfortunately using cmake 3.10 instead of latest (The default apt repo for cmake is old?). Upgraded to 3.17 and there was no issue.
All that is required is:
set_property(TARGET javaExample PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE)
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 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