4

This is a very trivial problem and may be due to my inexperience with CMake. I have followed the tutorial at http://fabzter.com/blog/cmake-tutorial-1 and am having issues with linking.

Basically I have a library MathFuncs and an executable that uses MathFuncs. The CMakeLists for MathFuncs is:

cmake_minimum_required(VERSION 2.8)
project(MathFuncs)

include_directories(${PROJECT_SOURCE_DIR})

SET (HEADER_FILES mysqrt.hpp)
add_library(MathFuncs SHARED mysqrt.cpp ${HEADER_FILES})

and the executable CMakeLists is:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)

set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
set (Tutorial_VERSION_BUGFIX 0)

#configure header file to pass some of the CMake settings 
#to the source code
configure_file(
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
) 

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

# add the include directories necessary to build library
include_directories("${PROJECT_SOURCE_DIR}/MathFuncs}")
add_subdirectory(MathFuncs)

SET (MATHFUNCTIONS_DIR ${PROJECT_SOURCE_DIR}/MathFuncs)
add_executable(tutorial tutorial.cpp ${MATHFUNCTIONS_DIR}/mysqrt.hpp)

target_link_libraries(tutorial MathFuncs)

CMake runs fine. However when I try to compile with Visual Studio, I get a linker error stating that it cannot open MathFuncs.lib. When I remove the 'SHARED' option from the MathFuncs CMakeList it runs since it is building a static library, however for my application I want a shared library DLL.

How do I make CMake set the library reference as shared?

Thank you,

krames
  • 569
  • 1
  • 6
  • 12
  • In this line: `include_directories("${PROJECT_SOURCE_DIR}/MathFuncs}") ` isn't there an extra `}`? – Javi Oct 21 '14 at 20:23
  • 1
    Have you verified that MathFuncs builds? (The failure of it to build will not nessisarily stop tutorial from *trying* to build, but the link files won't exist). – IdeaHat Oct 21 '14 at 20:39
  • MathFuncs builds no problem into a dll only. I now under stand as per Frasers answer that export definitions are required as a first step, however I am still having issues getting the .lib to be generated at all. Thanks – krames Oct 21 '14 at 22:42

1 Answers1

6

It looks like you're not exporting the classes/functions from your DLL properly. See this article for a detailed description.

This isn't entirely straightforward, but fortunately CMake can offer some help here. You can use the GenerateExportHeader module to auto-generate a header with all the preprocessor definitions you should need to properly export your library's public functions.

Here's an example of how I'd change your files. Each is hopefully commented enough to let you understand what's going on. If not, add a comment and I'll expand.

Top-level CMakeLists.txt:

cmake_minimum_required (VERSION 3.0)
project(Tutorial)

add_subdirectory(MathFuncs)

set(Tutorial_VERSION_MAJOR 1)
set(Tutorial_VERSION_MINOR 0)
set(Tutorial_VERSION_BUGFIX 0)

# Configure header file to pass some of the CMake settings to the source code.
set(TutorialConfigHeader "${PROJECT_BINARY_DIR}/TutorialConfig.h")
configure_file(TutorialConfig.h.in "${TutorialConfigHeader}")

# Include the output file of 'configure_file' to ensure it gets configured.
add_executable(tutorial tutorial.cpp "${TutorialConfigHeader}")

# Add TutorialConfig.h's path to include dirs for 'tutorial'.
target_include_directories(tutorial PRIVATE "${PROJECT_BINARY_DIR}")

target_link_libraries(tutorial MathFuncs)

# If 'MathFuncs' is a shared lib, copy it to 'tutorial's bin dir so
# it can be found at runtime.
get_target_property(MathFuncsType MathFuncs TYPE)
if(MathFuncsType STREQUAL "SHARED_LIBRARY")
  add_custom_command(TARGET tutorial POST_BUILD
                     COMMAND ${CMAKE_COMMAND} -E copy_if_different
                         $<TARGET_FILE:MathFuncs> $<TARGET_FILE_DIR:tutorial>
                     COMMENT "Copying MathFuncs shared lib alongside tutorial."
                     VERBATIM)
endif()


MathFuncs/CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(MathFuncs)

add_library(MathFuncs SHARED
            mysqrt.cpp
            mysqrt.hpp
            "${PROJECT_BINARY_DIR}/mathfuncs_export.h")

# Write appropriate export PP definitions in a file called
# 'mathfuncs_export.h' in the current binary dir
include(GenerateExportHeader)
generate_export_header(MathFuncs)

# Add the current source dir as a PUBLIC include dir
# (to allow mysqrt.hpp to be found by dependent targets)
# Add the current binary dir as a PUBLIC include dir
# (to allow mathfuncs_export.h to be found by dependent targets)
target_include_directories(MathFuncs PUBLIC
                           ${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})


Then to use the generated export header, you just need to have mysqrt.hpp contain something like:

#ifndef MYSQRT_HPP_
#define MYSQRT_HPP_

#include "mathfuncs_export.h"

double MATHFUNCS_EXPORT MySqrt(double input);

#endif  // MYSQRT_HPP_


This should now cause VS to create an export library ${CMAKE_BINARY_DIR}/MathFuncs/Debug/MathFuncs.lib (fixing the linker issue) and the actual DLL ${CMAKE_BINARY_DIR}/MathFuncs/Debug/MathFuncs.dll.

Fraser
  • 74,704
  • 20
  • 238
  • 215
  • Thank you for your help! Unfortunately the .lib file is not being generated by VS. Everything is compiling just fine, and the .dll is still generating, however I am still getting the same linker error. – krames Oct 21 '14 at 22:40
  • I'm pretty sure that if VS isn't generating the .lib it's because you're not exporting anything from your library. You need at least one function or class to be declared as `__declspec(dllexport)`, which is where the generated "mathfuncs_export.h" should come in - it should contain `#define MATHFUNCS_EXPORT __declspec(dllexport)` meaning you can just use `MATHFUNCS_EXPORT` in your function/class declaration. Do you have "mathfuncs_export.h" in your build tree, and does it contain this definition? – Fraser Oct 21 '14 at 22:46
  • mathfuncs_export.h is in the build tree - also MATHFUNCS_EXPORT is defined as __declspec(dllexport). I even implemented the exported MySqrt in mathfuncs.cpp and still no lib file being generated – krames Oct 21 '14 at 22:56
  • You didn't mention mathfuncs.cpp or mathfuncs.hpp in your original question - are you sure you're including all the files in your lib? – Fraser Oct 21 '14 at 23:04
  • My mistake - I meant mysqrt.hpp/cpp – krames Oct 21 '14 at 23:29
  • OK - I pushed my example to a [gitlab repo](https://gitlab.com/Fraser999/26495564#tab-readme). You should be able to clone this, build it and run the tutorial exe. Let me know how that goes. – Fraser Oct 21 '14 at 23:36
  • Strange - I am not sure what I was doing wrong but I managed to get it working exactly as you prescribed. Thank you! – krames Oct 22 '14 at 00:46
  • Glad you got there in the end :-) – Fraser Oct 22 '14 at 00:55
  • Instead of copying the shared library to the executable's folder, one could set `CMAKE_RUNTIME_OUTPUT_DIRECTORY` (like here [Building of executable and shared library with cmake, runtimelinker does not find dll](http://stackoverflow.com/a/26513002/1032277) – Liviu Aug 18 '16 at 08:10