2
votes

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} )
1

1 Answers

10
votes

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.