10

I have simple program as follow:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)

project(test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

LINK_DIRECTORIES(${PROJECT_SOURCE_DIR})

add_executable(test main.cpp)
target_include_directories(test PRIVATE ${PROJECT_SOURCE_DIR})    
target_link_libraries(test PRIVATE power.dll)

main.cpp:

#include <iostream>
#include "power.h"

using namespace std;

int main()
{
    cout << "Hello World!" << endl;
    power(4.);
    return 0;
}

power.h:

#ifndef POWER_H
#define POWER_H

double power(double number) noexcept;

#endif // POWER_H

Implementation of power.h is in a .dll named power.dll. If I compile this project with MinGW 7.3.0 X64 says:

error: undefined reference to `power(double)'

If I compile it with MSVC 2017 X64 says:

error: LNK1104: cannot open file 'power.lib'

both errors show that power.dll can't detect by the linker. I did many searches but none of solutions worked for me! Can anyone help about this? Thanks in advance!

Former contributor
  • 2,466
  • 2
  • 10
  • 15
Nima Ghorab
  • 309
  • 1
  • 3
  • 13
  • Where is `power.dll` located on your system? Did you try just doing `target_link_libraries(test PRIVATE power)`? – Kevin Oct 28 '19 at 20:56
  • It is located in test.exe directory and also in source directory. yes I tried target_link_libraries(test PRIVATE power) too but CMake by default looking for .lib version not .dll one... – Nima Ghorab Oct 28 '19 at 21:01
  • 3
    On Windows **linking** with a library is performed using `.lib` file. `.dll` is used at *runtime* only. Also, make sure that `power` function is **exported** from the library. – Tsyvarev Oct 28 '19 at 21:02
  • @Tsyvarev I knew this but I don't have .lib version and I have only .dll file! So there is no way to link against a .dll file really? – Nima Ghorab Oct 28 '19 at 21:06
  • You may create `.lib` from `.dll`. E.g. https://stackoverflow.com/questions/9360280/how-to-make-a-lib-file-when-have-a-dll-file-and-a-header-file, https://stackoverflow.com/questions/9946322/how-to-generate-an-import-library-lib-file-from-a-dll or just google for "get lib from dll". – Tsyvarev Oct 28 '19 at 21:56
  • Dear @Tsyvarev let me ask my question like this: what if we have power.lib file without source code. the disadvantage of using .lib file is that it binary will embed in .exe file! So we should use .dll one but how? also if we have both power.lib and power.dll as I read in many topics that using .dll is not make sense during compilation process, We should use .lib file in this case. So .dll file become useless... – Nima Ghorab Oct 29 '19 at 00:18
  • You confuse a **static** library and an **export** library. **Both** these libraries have `.lib` suffix and both are used during linking stage. A **static** library is "standalone": it contains both exported symbols and code, which is embedded into the executable during the linking stage. An **export** library contains only exported symbols without the code, and it should be accompanied with `.dll` (**shared** library) used at **runtime**. I didn't mean that your `power.dll` is useless. I meant that you need to have corresponded **export** `.lib` for link with it. – Tsyvarev Oct 29 '19 at 07:47
  • See also this answer for distinguish libraries: https://stackoverflow.com/a/34391123/3440745. – Tsyvarev Oct 29 '19 at 07:48
  • Dear @Tsyvarev Thank you so much! If we pass a .lib file in order to have a successful linking stage, how can we use .dll file? because passing a .lib file cause linker to put all binaries in main .exe and in this case .dll file is not needed anymore! – Nima Ghorab Oct 29 '19 at 13:08
  • "because passing a .lib file cause linker to put all binaries in main .exe and in this case .dll file is not needed anymore!" - No, you again confuse **static** and **export** libraries. Please, re-read my previous comment. – Tsyvarev Oct 29 '19 at 13:11
  • Dear @Tsyvarev thanks a lot! Can you do what you are telling me with my example in the first post? I don't no how I should create a .lib as export library! – Nima Ghorab Oct 29 '19 at 13:25
  • See my previous comment: https://stackoverflow.com/questions/58597988/how-to-use-dynamic-link-library-with-cmake?noredirect=1#comment103510339_58597988 – Tsyvarev Oct 29 '19 at 13:35

2 Answers2

6

Your modelling of the dynamic library is incorrect, both on CMake and on the source level.

As a starting point, try building the dll as part of the same CMake project as the consuming executable:

cmake_minimum_required(VERSION 3.5)

project(test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(GenerateExportHeader)
add_library(power SHARED power_sources.cpp power.h)
generate_export_header(power)
target_include_directories(power PUBLIC ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR})

add_executable(test main.cpp)
target_link_libraries(test PRIVATE power)

Note the use of the generate_export_header function, which instructs CMake to generate macros for exporting functions on shared library interfaces in a portable way. Since generated files go to the binary directory tree, we have to adjust the include directories for the library accordingly.

To make sure the function gets properly exported, change your header as follows:

#ifndef POWER_H
#define POWER_H

#include <power_export.h>

POWER_EXPORT double power(double number) noexcept;

#endif // POWER_H

Note that generare_export_header allows you to customize the generated export header extensively.

Be sure you get the project to build and run from this baseline.

If you want to build the dll externally (which is not strictly necessary, but since that's what your question is about...), we have to modify the CMake file to something like:

cmake_minimum_required(VERSION 3.5)

project(test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(power)

add_executable(test main.cpp)
target_link_libraries(test PRIVATE power)

With all the magic here happening in the find_package call. That call is now responsible for providing all the information that was previously handled by the lines for building the library:

  • Providing of an imported target power for consumption by the target_link_libraries call
  • Association of the library name of the import library (the power.lib file) via that imported target
  • Exposure of the public include directories for both power.h and power_export.h via that imported target

You can either construct such an imported target manually in the find script, or have CMake do it for you. In the first case, create a FindPower.cmake script file, make sure it's location is part of the CMAKE_MODULE_PATH and write the code for finding the library and header files and constructing the imported target in there. Note that getting this right in a portable way can be very tricky and goes far beyond the scope of a StackOverflow question. In the second case, have the CMake script that builds the power library perform an install step during which a config file package will get generated, which can then be consumed by your test project. Note that this approach is not viable if the power library is not itself being built with CMake, so in that case you will have to stick with the first option.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Dear @ComicSansMS thanks a lot for the great answer. How OpenCV, Boost and ... work so that we can link our program to dll of these libraries directly? How do they handle this problem? – Nima Ghorab Oct 29 '19 at 23:46
  • @NimaGhorab They do the same as what I described in the answer. Find scripts for [Boost ship as part of CMake](https://cmake.org/cmake/help/latest/module/FindBoost.html) (approach #1), but you could just as easily write such a script yourself. [OpenCV provides a config file script](https://github.com/opencv/opencv/blob/master/cmake/templates/OpenCVConfig.cmake.in) as part of its installation (approach #2). – ComicSansMS Oct 30 '19 at 06:05
  • Thanks a lot man! As my final question why we should link to .dll file when using opencv (not .lib) in following example? Because I read in many posts that linking against dll files in such way is wrong! [Linking to dynamic library using QMake](https://wiki.qt.io/How_to_setup_Qt_and_openCV_on_Windows) – Nima Ghorab Oct 30 '19 at 13:55
  • 1
    @NimaGhorab Short answer: MinGW treats dynamic libraries in a weird (ie. non-Windows-y) way, mainly due to the fact that its model of shared libraries is influenced largely by Linux `.so` files. Shared libraries on Windows differ a *lot* from shared libraries on Linux. Most of the confusion regarding how to integrate shared libraries with a portable build system like CMake is usually due to the fact that people only understand how they work on one of those systems, but not the other. – ComicSansMS Oct 30 '19 at 19:01
2

Dynamic linking in Windows requires that externally visible symbols are declared with the keyword __declspec. Your header "power.h" should be modified:

#ifndef POWER_H
#define POWER_H

#if defined(__WIN32__) && !defined(__CYGWIN__)
#  if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_DLL)
#    define POWERAPI __declspec(dllexport)
#  elif (defined(_MSC_VER) || defined(__MINGW32__))
#    define POWERAPI __declspec(dllimport)
#  endif
#endif

POWERAPI double power(double number) noexcept;

#endif // POWER_H

In the project building the DLL power.dll with CMake, you should define the symbol BUILD_DLL:

add_definitions(-DBUILD_DLL)

then it should generate a power.lib file when the MSVC compiler and a power.a when using MINGW. Don't define BUILD_DLL in the project using the DLL, and it should work.

Former contributor
  • 2,466
  • 2
  • 10
  • 15
  • Dear @Pedro, it did not work! It gives this error now: error: BUILD_DLL: No such file or directory – Nima Ghorab Oct 28 '19 at 21:20
  • Sorry, small mistake. The add_definitions() macro must be added to the project that builds the DLL, not the one using the DLL. – Former contributor Oct 28 '19 at 21:25
  • If you can't rebuild the DLL, then you should explore [other options](https://stackoverflow.com/questions/31708832/how-to-reference-a-dll-to-visual-studio-without-lib-file). – Former contributor Oct 28 '19 at 21:30
  • Dear @Pedro it is still not working! If I use MinGW says: error: undefined reference to `power(double)' and MSVC says: power.h:12: error: C2144: syntax error: 'double' should be preceded by ';' and also: power.h:12: error: C4430: missing type specifier - int assumed. Note: C++ does not support default-int – Nima Ghorab Oct 28 '19 at 21:32
  • Dear @Pedro if I have only .lib file without having power.cpp and power.dll can I convert power.lib to power.dll? – Nima Ghorab Oct 28 '19 at 21:45