2

I'm trying to build a static library to be released as an API for a network device. I can successfully compile and link the library to produce .lib output files, and I relocate them into a directory structure as follows:

EyeLib
 L-Include
 |  L-PublicInterface.h
 L-Lib
 |  L-debug
 |  |   L-MyLib.lib
 |  |   L-MyLib.pdb
 |  L-release
 |      L-MyLib.lib
 L-MyLibConfig.cmake

Where the MyLibConfig.cmake file is extremely simple, and contains:

# the header file is relative to this cmake file, so get the path.
GET_FILENAME_COMPONENT( MyLib_TOPLEVEL_DIR ${CMAKE_CURRENT_LIST_FILE} PATH )
SET( MyLib_INCLUDE_DIR ${MyLib_TOPLEVEL_DIR}/include )

IF( WIN32 )
  FIND_LIBRARY( MyLib_DEBUG_LIBRARY MyLib ${MyLib_TOPLEVEL_DIR}/lib/debug )
  FIND_LIBRARY( MyLib_RELEASE_LIBRARY MyLib ${MyLib_TOPLEVEL_DIR}/lib/release )
  SET( MyLib_LIBRARIES optimized ${MyLib_RELEASE_LIBRARY} debug ${MyLib_DEBUG_LIBRARY} )
ENDIF( WIN32 )
IF( UNIX )
  FIND_LIBRARY( MyLib_LIBRARY MyLib ${MyLib_TOPLEVEL_DIR}/lib )
  SET( MyLib_LIBRARIES "${MyLib_LIBRARY}" )
  MARK_AS_ADVANCED( MyLib_LIBRARY )
ENDIF( UNIX )

# handle the QUIETLY and REQUIRED arguments
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MyLib DEFAULT_MSG MyLib_LIBRARIES MyLib_INCLUDE_DIR)

MARK_AS_ADVANCED( MyLib_INCLUDE_DIR )

This build structure has worked for some test libraries I've built in the past, but I'm getting a link error when I try and use it to build a simple test app saying "error LNK1104: cannot open file 'libboost_thread-vc110-mt-s-1_54.lib'"

I can get the test app to build and run successfully if I add it to the same project as the library build. I assume this is because the library build is finding the boost libs to link against, so it propagates through to the executables in the project.

I built boost 1.54 with b2 link=static runtime-link=static threading=multi variant=debug,release --layout=tagged and linked both the library build and the test app build to the static MSVC runtime (/MT).

Can anyone offer some help/advice/further tests with this one? I need to make sure that all the boost stuff is compiled-in to the API library, so our clients don't have to install boost themselves.

Additional Info

In-case it's helpful, here's the cmakelists.txt file from the library build:

set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/lib")

set(Boost_USE_STATIC_LIBS   ON)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost REQUIRED COMPONENTS system date_time regex thread chrono)
if(NOT WIN32)
  list(APPEND Boost_LIBRARIES pthread)
endif()

include_directories(${Boost_INCLUDE_DIRS})

FILE(GLOB srcs *.cpp)
FILE(GLOB headers *.h)
set(libname MyLib)

set(deps ${Boost_LIBRARIES})

#To allow compilation. std=c++0x is for accepting the access to enums, which usually is just accepted with Visual Studio
IF( NOT WIN32 )
  set (CMAKE_CXX_FLAGS "-fpermissive -std=c++0x")
ENDIF( NOT WIN32 )

SOURCE_GROUP( ${libname} FILES ${srcs} )
SOURCE_GROUP( "${libname}\\Hdr" FILES ${headers} )
add_library(${libname} ${srcs} ${headers})
target_link_libraries( ${libname} ${deps} )
Artless
  • 4,522
  • 1
  • 25
  • 40
OcularProgrammer
  • 482
  • 2
  • 5
  • 19

1 Answers1

10

This is by design.

When building a static library, any dependencies to that library will not get linked into the library directly. Instead when building an executable all library dependencies (direct and indirect) will be linked directly to that executable.

This is also the way most compiler handle static libraries. While VS does offer a special option to link dependencies into static libs, this is not possible on e.g. gcc without resorting to dirty file hacks. Since CMake only supports features that can be used on all supported generators, CMake will not allow to do this even on VS builds.

You have a couple of options now:

  • Use a dll instead of a static library (add_library(${libname} SHARED ...)). While a static library is basically a bunch of object files wrapped together, a dll is more or less the same as an executable. In particular, all static library dependencies get linked directly into the dll. The disadvantage here is that you have to deal with the usual dll mess: You have to specify which functions to export and the usual issues with passing stuff across dll boundaries apply.
  • Have your find script also search for all the dependencies. You should be able to restructure your library's dependency handling in a way that the amount of code duplication is minimal. The disadvantage is that configuring a third-party application becomes more difficult (especially on Windows) since you now not only need to find the library itself, but also all of its dependencies.
  • Use exported targets. This approach makes most sense if the library is built on the same machine as the final executable. Upon building the library, CMake auto-generates the config files for using that library. Your application then just needs to find an include that script and you're good to go. Disadvantage is that the export mechanism is not the most straight-forward feature of CMake, so you will have to spend some time to familiarize yourself with it.
  • Pull in the library sources directly into each executable. Basically each executable does an add_subdirectory on the library source dir. You still have to configure the dependencies for each executable and on top of that you also have to build the library seperately for each executable. You probably don't want to do this.
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • One option you haven't listed here, will it work if I just distribute the dependencies with the library? As long as they're in the same location as MyLib.lib cmake should be able to find it. It'd mean that clients could do their own links into either a DLL or a fully portable executable. Is this solution likely to cause any unforseen issues? My main concern is that I don't want clients to have to be using the same version of boost for their apps as I used to build the library. – OcularProgrammer Aug 22 '13 at 00:27
  • @OcularProgrammer This is basically option #2: You write a find script that resolves the dependencies, potentially by taking them from your distribution. Mixing library versions is an issue, but unfortunately one that applies to all forms of binary library distribution. If you expose a Boost type on the interface, your library and the executable must both use the same boost version. Otherwise passing boost types across library boundaries will probably mess things up. Contained use of different versions is usually okay, but you should still strive to avoid it if possible. – ComicSansMS Aug 22 '13 at 08:52
  • Cheers for the advice. The MyLibConfig.cmake needed a bit of hacking to correctly grab both the library and dependencies from the directory structure, but we now have a nice, portable install package when I grab all the dependencies and move them into the appropriate lib directories. Thank you very much. – OcularProgrammer Aug 23 '13 at 08:11