Controlling Library Output Directories in CMake with LIBRARY_OUTPUT_DIRECTORY


Purpose

  • Offers customization over the default output directory for better project organization.
  • Controls the location where CMake builds library target files (shared libraries, module libraries).

Setting the Property

  • Use the set_target_properties command within your CMakeLists.txt file:
set_target_properties(my_library PROPERTIES LIBRARY_OUTPUT_DIRECTORY my_custom_lib_dir)

Behavior

  • For multi-configuration generators like Visual Studio or Xcode, an additional subdirectory might be appended based on the build configuration (e.g., Debug, Release). You can use generator expressions to override this behavior.
  • The specified directory (my_custom_lib_dir in the example) becomes the base location for library files.

Key Points

  • The initial value might be set by the CMAKE_LIBRARY_OUTPUT_DIRECTORY variable if defined before target creation.
  • Doesn't affect static libraries, which use the ARCHIVE_OUTPUT_DIRECTORY property.
  • Primarily applies to shared libraries and module libraries.

Example

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../lib)  # Initial library output directory

add_library(my_library SHARED source1.cpp source2.cpp)

if(NOT CMAKE_BUILD_TYPE)  # If build type not explicitly set
  set(CMAKE_BUILD_TYPE Debug)  # Set default build type
endif()

set_target_properties(my_library PROPERTIES LIBRARY_OUTPUT_DIRECTORY custom_libs/${CMAKE_BUILD_TYPE})

In this example:

  • LIBRARY_OUTPUT_DIRECTORY for my_library is overridden to custom_libs/${CMAKE_BUILD_TYPE}, resulting in build-type-specific library directories (e.g., custom_libs/Debug or custom_libs/Release).
  • If the build type isn't specified, Debug is used as the default.
  • my_library is created as a shared library.
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY is initially set to ../lib.


Setting a Fixed Output Directory

This example sets a fixed directory named my_libs for all shared libraries:

set(MY_LIB_DIR "${PROJECT_BINARY_DIR}/my_libs")  # Define relative path

add_library(my_library SHARED source1.cpp source2.cpp)
set_target_properties(my_library PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${MY_LIB_DIR})

add_library(another_library SHARED source3.cpp source4.cpp)
set_target_properties(another_library PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${MY_LIB_DIR})

In this case, both my_library and another_library will be placed within the my_libs directory after building.

Using Generator Expressions

This example uses generator expressions to create a build-type specific directory structure for libraries, even with multi-configuration generators:

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/$<CONFIG>/lib")

add_library(my_library SHARED source1.cpp source2.cpp)

Here, the generator expression $<CONFIG> expands to the current build configuration (e.g., Debug, Release). This results in libraries being placed in directories like PROJECT_BINARY_DIR/Debug/lib or PROJECT_BINARY_DIR/Release/lib.

Overriding Initial Value

This example demonstrates overriding the initial value set by CMAKE_LIBRARY_OUTPUT_DIRECTORY:

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../default_lib)  # Initial directory

add_library(my_library SHARED source1.cpp source2.cpp)
set_target_properties(my_library PROPERTIES LIBRARY_OUTPUT_DIRECTORY custom/libs)

Here, even though CMAKE_LIBRARY_OUTPUT_DIRECTORY is initially set to ../default_lib, my_library will be built in the custom/libs directory due to the specific target property setting.



  1. Leveraging CMAKE_BINARY_DIR

    • If you just want a basic way to place libraries within the build directory, you can utilize the built-in CMAKE_BINARY_DIR variable. For example:
    add_library(my_library SHARED source1.cpp source2.cpp)
    set_target_properties(my_library PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
    

    This approach is simple but doesn't offer as much flexibility compared to LIBRARY_OUTPUT_DIRECTORY.

    • For more granular control, you can define custom build steps using CMake commands like add_custom_command or execute_process. However, this requires writing additional code and might be less maintainable compared to using a dedicated property.
  2. External Build Systems

    • If you're using an external build system like Make or Bazel alongside CMake, you can configure it to handle library placement through its own mechanisms. This can be a suitable option for complex build environments.

Choosing the Right Approach

The best approach depends on your specific requirements:

  • For more complex scenarios or when you need to integrate with external build systems, consider using custom build steps or exploring the build system's capabilities.
  • If you simply need a well-defined location within the build directory, using CMAKE_BINARY_DIR with LIBRARY_OUTPUT_DIRECTORY is a good choice.