10

I've been using CMake and Eclipse on linux for a while and have been working with multiple target projects containing numerous executables and shared objects.

I use out of source builds and on linux the binaries are put into their own directories. When I do this on linux eclipse is somehow able to find the shared objects and run the executables fine but on windows this doesn't happen.

On Windows I am having to add a PATH environment variable that points to the dlls or I could build into a single bin and lib directory (although I had an issue I've forgotten in the past with CMake that made me want to avoid this).

Why is this working differently on Windows to linux? Is it a setting I'm missing or does this just not work?

The builds themselves are working perfectly. I'm using MinGW, Eclipse Kepler and Windows 7 64 bit.

Thanks in advance.

Matt_JD
  • 566
  • 6
  • 16

3 Answers3

11

Windows simply doesn't have some of the necessary concepts to allow CMake to set up your build environment. When linking Windows will look in the same directory as the binary, and then search the directories in your PATH. There isn't anything like RPATH, which is used on most Unix platforms, to inject in other more appropriate paths. The DLLs should generally be installed alongside your binaries, in the same directory.

In my opinion, best practice on Windows is to put the DLLs next to your binaries. CMake attempts to make this easier,

install(TARGETS MyTarget
  EXPORT "MyProjectTargets"
  RUNTIME DESTINATION "${INSTALL_RUNTIME_DIR}"
  LIBRARY DESTINATION "${INSTALL_LIBRARY_DIR}"
  ARCHIVE DESTINATION "${INSTALL_ARCHIVE_DIR}")

would install DLLs to the RUNTIME destination, but put the libs in the LIBRARY destination. This means that typically on Unix-like OSes lib has the shared objects, but CMake knows that DLLs are effectively runtime and would go in bin. Hopefully this makes things clearer. It is impossible for CMake/Eclipse to really improve this much, beyond perhaps injecting additional directories into your PATH when clicking run from Eclipse (not sure if that is possible).

If you are concerned with the build tree, then the following would work well there (as suggested in the comments below):

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

If you want to allow these to be overridden (can be useful) they should be protected with if(NOT var_name) blocks too.

Marcus D. Hanwell
  • 6,716
  • 1
  • 21
  • 20
  • 1
    Yeah, it's what I found out then. I had thought about Eclipse doing injection to PATH but I don't think it could because it would need to interpret the Makefile and potentially not all of the executables built need all of the libraries built so it could get messy. I don't use install at the moment because I have had no need. Maybe I'll need to look into it. I think I had previously used CMAKE_RUNTIME_OUTPUT_DIRECTORY and it's friends but it caused CMake to choke at some point when it seemed to have lost track of where it put a binary files so I stopped. – Matt_JD Dec 06 '13 at 21:39
  • 1
    I've solved it by setting these. It does similar but doesn't need installing: set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) – Matt_JD Dec 06 '13 at 21:59
  • Yes, they are entirely correct if you are using the build tree. I will update the answer to include the variant for the build tree. This is the approach we use in several projects, and it works well. – Marcus D. Hanwell Dec 08 '13 at 19:45
2

Just a possible answer to my own question. I think that on linux the rpath is being used to identify the locations of the dependent libraries but on windows with mingw I cannot use the elf parser and so cannot use rpath.

Matt_JD
  • 566
  • 6
  • 16
0

Thanks everyone for question and responses. I want just to summarize what you need to for windows support:

Please add the following code to root CMakeLists.txt:

# Common output directory is required for OS without rpath support.
set (CMAKE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/result")
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})

Than you need to add working directory for every add_test:

add_test (
  NAME ${TEST_NAME}
  COMMAND ${TEST_TARGET}
  WORKING_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}
)

Please be aware that TEST_TARGET should be unique inside current project.

I've added windows support for lzws library using this method. You can always checkout source code and clarify remaining question.

puchu
  • 3,294
  • 6
  • 38
  • 62