简体   繁体   中英

Building of executable and shared library with cmake, runtimelinker does not find dll

I am working with gcc(cygwin), gnu make, windows 7 and cmake.

my cmake testprojekt has the following structure

rootdir
|-- App
|   |-- app.cpp
|   +-- CMakeLists.txt
|-- Lib
|   |-- lib.cpp
|   |-- CMakeLists.txt
|-- MakeFileProject
+ CMakeLists.txt

rootdir/App/app.cpp:

#include<string>
void printThemMessageToScreen(std::string input);//prototype
int main(int argc,char **argv){
 printThemMessageToScreen("this will be displayed by our lib");
 return 0;
}

rootdir/Lib/lib.cpp:

#include<iostream>
#include<string>

void printThemMessageToScreen(std::string input){
 std::cout<<input;
}

rootdir/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)
project(TestProject)

add_subdirectory(App)
add_subdirectory(Lib)

rootdir/Lib/CMakeLists.txt:

add_library(Lib SHARED lib.cpp)

rootdir/App/CMakeLists.txt:

# Make sure the compiler can find include files from our Lib library. 
include_directories (${LIB_SOURCE_DIR}/Lib) 

# Make sure the linker can find the Lib library once it is built. 
link_directories (${LIB_BINARY_DIR}/Lib) 

# Add executable called "TestProjectExecutable" that is built from the source files 
add_executable (TestProjectExecutable app.cpp) 

# Link the executable to the lib library. 
target_link_libraries (TestProjectExecutable Lib) 

Now, when i run cmake and make, everything will get generated & built with no errors, but when i try to execute the binary, it will fail because the library which was generated could not be found.

BUT: when i copy the lib dll into the same directory like the app exe, it will get executed!

also: if i configure the library to be static, it will also execute.

how to tell the runtime linker where to look for my dll?

UPDATE:

Solution according to the Method proposed by User Vorren:

I opened up the registry editor, and navigated to the following Key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths

, here i created a new key with the name of my Applikation:

in this case : TestProjectExecutable.exe

after that, the (default) value was set to the full path of TestProjectExecutable.exe including the filename and extension. Then i created another String Value called "Path" and set the value to the folder where the dll was located:

在此输入图像描述

Your problem lies not with linker or compiler, but with the way Windows searches for DLL's.

The OS will use the following algorithm to locate the required DLL's:

Look in:

  1. The directories listed in the Application-specific Path registry key;
  2. The directory where the executable module for the current process is located;
  3. The current directory;
  4. The Windows system directory;
  5. The Windows directory;
  6. The directories listed in the PATH environment variable;

Thus you have two reasonable options if you don't want to clutter the OS directories with your app-specific dll:

  1. Create an app-specific Path registry entry (I would go with this option) ;
  2. Put your DLL in the same folder as your EXE;
  3. Modify the PATH variable (but why would you do that, if you can go with option 1?) ;

A solution I prefer that hasn't really been mentioned, is build your shared-libs into the same directory as your executables. This tends to be a much simpler solution.

One way to do this with cmake is

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

Or you can also set output directories based on build flavours.

See how do I make cmake output into a 'bin' dir?

I tried the option 1 from accepted answer (by pdeschain). I even created a cmake hook to register paths of linked libraries automatically

function (xtarget_link_libraries target libs) # same as target_link_libraries but with additional improvements to allow windows find the library at runtime
LIST(REMOVE_AT ARGV 0)
SET(LIBS ${ARGV}) # this is to pass list into this function
target_link_libraries(${target} ${LIBS}) # call standard routine

if(WIN32)
    set(TFILE ".")
    get_property(slibs TARGET ${target} PROPERTY all_libs) # recall libs linked before
    set(LIBS ${slibs};${LIBS})
    set_property(TARGET ${target} PROPERTY all_libs ${LIBS}) # save all libs
    FOREACH(lib ${LIBS}) # compose a list of paths
        set(TFILE "${TFILE};$<TARGET_LINKER_FILE_DIR:${lib}>")
    ENDFOREACH()
    #add reg key
    add_custom_command(TARGET ${target} POST_BUILD COMMAND  reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /v "Path" /d "${TFILE}" /f )
endif()
endfunction()

Can be used as xtarget_link_libraries(test lib1 lib2) . The application will be able to find dynamic libraries at their absolute paths.

BUT, there is a big problem with this, that the App Paths mechanism https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths

does not allow to have different entries for say 'Debug/test.exe' and 'Release/test.exe'. So to me this is a poor option.

You may add the following line to fill the Default key as path to the program as suggested in the post.

add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\App Paths\\\\${target}.exe" /ve /d "$<TARGET_FILE:${target}>" /f )

Now you can enjoy running test.exe from anywhere in the system... I guess my next try will be option

  1. Create symbolic links to dlls with cmake.

I discovered (what I believe to be) quite a nice way of handling this. It follows the approach of adding the .dll to the same directory as the .exe. You can do it in CMake like so:

if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
    TARGET <app-target> POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    $<TARGET_FILE_DIR:<lib-target>>
    $<TARGET_FILE_DIR:<app-target>)
endif()

where app-target is the name of the application or library you're building (created through add_executable or add_library ) and lib-target is the imported library brought in with find_package .

# full example
cmake_minimum_required(VERSION 3.14)

project(my-app-project VERSION 0.0.1 LANGUAGES CXX)

find_package(useful-library REQUIRED)

add_executable(my-application main.cpp)

target_link_libraries(my-application PUBLIC useful-library::useful-library)

if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
    TARGET my-application POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    $<TARGET_FILE_DIR:useful-library::useful-library>
    $<TARGET_FILE_DIR:my-application>)
endif()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM