0

I have a small test program that I want to link to GLFW. I am currently able to download, configure and build the .dll using ExternalProject_Add command. When I build my test program I get an executable that doesn't run because it can't find the .dll. If I manually copy the .dll to the directory where the executable is, it runs just fine.

How do I get my executable to properly link to the library?

Is there a way to automatically copy the .dll to where it needs to be?

What is the best way to ensure that, when it comes time to package my program, the library is available to use and easily accessible?

CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (GLFW-test)

set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/app )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )

# Include OpenGL
find_package(OpenGL REQUIRED)
if (OPENGL_FOUND)
    include_directories(${OPENGL_INCLUDE_DIR})
    link_libraries(${OPENGL_LIBRARIES})
endif()

# Add directories for library linkage
set(GLFW_LIB_DIR ${CMAKE_BINARY_DIR}/downloads/deps/Build/GLFW_EX/src)
link_directories(${GLFW_LIB_DIR})

# Download and unpack dependencies at configure time
configure_file(deps-CMakeLists.txt downloads/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/downloads)

add_subdirectory(${CMAKE_BINARY_DIR}/downloads/deps/Source/GLFW_EX
                 ${CMAKE_BINARY_DIR}/downloads/deps/Build/GLFW_EX
                 EXCLUDE_FROM_ALL )

include_directories(${CMAKE_BINARY_DIR}/downloads/deps/Source/GLFW_EX/include)

add_executable(GLFW-test src/GLFW-test.cpp)
target_link_libraries (GLFW-test glfw3 ${OPENGL_LIBRARIES})

add_custom_command(TARGET GLFW-test POST_BUILD        # Adds a post-build event to MyTest
    COMMAND ${CMAKE_COMMAND} -E copy_if_different     # which executes "cmake - E copy_if_different..."
        "${GLFW_LIB_DIR}/glfw3.dll"                   # <--this is in-file
        $<TARGET_FILE_DIR:GLFW-test>)                 # <--this is out-file path

dep-CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (GLFW-dl)

include(ExternalProject)
set_directory_properties(PROPERTIES EP_BASE "./deps/")

# Include GLFW
ExternalProject_Add (
       GLFW_EX
       GIT_REPOSITORY "https://github.com/glfw/glfw.git"
       GIT_TAG "master"
       CMAKE_ARGS -DGLFW_BUILD_EXAMPLES=OFF
                  -DGLFW_BUILD_TESTS=OFF
                  -DGLFW_BUILD_DOCS=OFF
                  -DGLFW_INSTALL=OFF
                  -DBUILD_SHARED_LIBS=ON
       UPDATE_COMMAND ""
       TEST_COMMAND      "" )

UPDATE: The way I am using ExternalProject_Add is described on this site: https://crascit.com/2015/07/25/cmake-gtest/ It allows the external projects to be configured and built only once during the configure phase of my project. I have changed the directories around a bit from their test program to make things a little easier for when I eventually add more external projects. The test project on the site does not seem to account for dynamic libraries which is what I am trying to do.

UPDATE 2: I've added 2 set commands to help clean up the build directory towards the top of the CMakeLists file. I also added a command at the bottom which copies .dll that is built from the ExternalProject_Add command to where I need it (next to the final executable). That seems to work for Windows, but it seems a bit hacky and doesn't resolve the errors in my IDE, which is currently Eclipse. Is there still a better way to do this?

Helpful Related topics:

Craig Scott
  • 9,238
  • 5
  • 56
  • 85
VERT9x
  • 372
  • 2
  • 13
  • Your `execute_process()` calls build *another project* in the binary dir of the *current* one. I wonder how CMake processes that without errors. Actually, you can call `ExternalProject_Add()` in the same project which uses external library. No needs to create *separate* project for that call. – Tsyvarev Oct 01 '15 at 09:01
  • Yes, however the `execute_process()` allows me to build the other project only once and during the configure phase of my project instead of everytime I build my project. The technique is described here: http://crascit.com/2015/07/25/cmake-gtest/ – VERT9x Oct 01 '15 at 16:37
  • Note, that in the article you refer external project is downloaded and built to `${CMAKE_BINARY_DIR}/gtest-download`, not to `${CMAKE_BINARY_DIR}` as it is done in your project. Fix that issue and check whether your problem is still persisted. – Tsyvarev Oct 01 '15 at 21:44
  • I have already been there and done that. It does not make a difference, especially since the dll that is built gets put in another subdirectory anyway. – VERT9x Oct 01 '15 at 22:05
  • Please, update your question to reflect changes which you have made. Just for being sure that previous issue is completely gone off, and new issues are not appears. – Tsyvarev Oct 01 '15 at 22:25
  • I've updated my question with things I've tried. – VERT9x Oct 04 '15 at 22:01
  • You still build external project in the binary directory of main project: `configure_file` and `execute_process` both works in `${CMAKE_BINARY_DIR}`. Two builds in the same directory may confuse CMake. Change build directory of external project to, e.g. `${CMAKE_BINARY_DIR}/GLFW-download`. – Tsyvarev Oct 04 '15 at 22:41
  • I just updated my project and question to move the external project to another directory and it still doesnt work. – VERT9x Oct 05 '15 at 03:01
  • 1
    As for problems with Eclipse, try to rename your main project or executable: according to [this manual](http://sgpsproject.sourceforge.net/JavierVGomez/index.php/How_to_configure_a_C/C%2B%2B_project_with_Eclipse_and_CMake), Eclipse may have problems with executable and project named equally. – Tsyvarev Oct 05 '15 at 09:28
  • BTW, as your executable is linked with library from external project, you should make this executable dependent from external project: `add_dependencies(GLFW-test GLFW_EX)`. – Tsyvarev Oct 05 '15 at 10:03
  • `add_dependencies` "fails" from Policy CMP0046 due to GLFW_EX being in executed in a different process. I've tried using `glfw` instead of `GLFW_EX` since that is the target that gets output from the other project, but then I get `undefined references` for GLFW functions during the build of my project. – VERT9x Oct 05 '15 at 18:15
  • Oops, sorry, I forget again that you actually build external project at configuration step, so you don't need to `add_dependencies()` from it. What about renaming main project name or executable (both are `GLFW-test`)? Does it resolve Eclipse issues? – Tsyvarev Oct 05 '15 at 19:39
  • It looks as though the renaming of the project seems to have worked. I also installed the plugin that was linked to on the link you provided. This forced me to restart Eclipse which may have also had something to do with the errors. – VERT9x Oct 06 '15 at 01:00

1 Answers1

0

How do I get my executable to properly link to the library?

As your second link states, there is no other way than to have .dll in the same directory as executable.

Is there a way to automatically copy the .dll to where it needs to be?

In you main project you already use variable CMAKE_RUNTIME_OUTPUT_DIRECTORY for setup directory where executables and .dlls should be placed after build. You can pass this variable to ExternalProject_add for force it to use same conventions:

ExternalProject_Add (...
    CMAKE_ARGS -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
    ...
)

What is the best way to ensure that, when it comes time to package my program, the library is available to use and easily accessible?

Packaging just uses install-tree of your project. So it is sufficient to install executables and libraries into same location:

set(INSTALL_RUNTIME_DIR bin)
install(TARGETS GLFW-test
    RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR}
)
install(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/glfw3.dll
    DESTINATION ${INSTALL_RUNTIME_DIR}
)

Note, that target GLFW_EX obtained from external project has no special type(like executable or library), so you need to install its deliverables using plain filenames.

Community
  • 1
  • 1
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153