Skip to content

Commit

Permalink
New CommonPackageConfig.cmake replaces PackageConfig.cmake
Browse files Browse the repository at this point in the history
- targets are properly exported with CMake's export(EXPORT) feature
- CommonLibrary.cmake adds targets to export set
- generated ${PROJECT_NAME}Config.cmake handles build- and install-tree
  inclusion properly; only find dependencies if the targets are not present,
  which improves CMake run performance
  • Loading branch information
tribal-tec committed Aug 17, 2015
1 parent d14246f commit 00d7d4a
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 558 deletions.
10 changes: 9 additions & 1 deletion CHANGES.md
Expand Up @@ -7,7 +7,15 @@
* Added SHALLOW and VERBOSE options
* new targets *DIR-rebase* and *rebase* to update git externals and sub
projects
* Exported targets provided by PackageConfig.cmake are now generated by
* CommonPackageConfig.cmake replaces PackageConfig.cmake. It uses the
export(EXPORT) feature from CMake 3 to generate ${PROJECT_NAME}Targets.cmake
file which provides IMPORTED targets. Client projects should use those targets
in target_link_libraries() (or NAME_LINK_LIBRARIES if common_library) rather
than ${PROJECT_NAME}_LIBRARIES.
* New variable NAME_OMIT_EXPORT for common_library() excludes a target from the
list of exported targets. This replaces the project-wide
${PROJECT_NAME}_EXCLUDE_LIBRARIES variable.
* Exported targets provided by CommonPackageConfig.cmake are now generated by
common_application() and common_library(); generic target hook for
add_executable() and add_library() does not exist anymore.
* common_check_targets() now adds cppcheck, cpplint and clangcheck targets
Expand Down
8 changes: 2 additions & 6 deletions CommonCPack.cmake
Expand Up @@ -4,7 +4,7 @@

# No support for subproject packaging
if(NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
include(PackageConfig)
include(CommonPackageConfig)
return()
endif()

Expand Down Expand Up @@ -54,10 +54,6 @@ set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH})
set(CPACK_PACKAGE_VERSION ${VERSION})
set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE.txt)
if(NOT CPACK_PACKAGE_CONFIG_REQUIRES)
string(REPLACE ";" " " CPACK_PACKAGE_CONFIG_REQUIRES
"${${UPPER_PROJECT_NAME}_DEPENDENT_LIBRARIES}")
endif()
if(NOT CPACK_DEBIAN_BUILD_DEPENDS)
# setup'd by Buildyard config, same as for travis CI
set(CPACK_DEBIAN_BUILD_DEPENDS ${${UPPER_PROJECT_NAME}_BUILD_DEBS})
Expand Down Expand Up @@ -193,4 +189,4 @@ set(CPACK_PACKAGE_FILE_NAME_BACKUP "${CPACK_PACKAGE_FILE_NAME}")
include(CPack)
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME_BACKUP}")

include(PackageConfig)
include(CommonPackageConfig)
17 changes: 17 additions & 0 deletions CommonConfig.cmake.in
@@ -0,0 +1,17 @@
# Generated by CommonPackageConfig.cmake

# if target already exists (subproject build), no need to find any dependencies
if(TARGET @PROJECT_NAME@_ALIAS)
list(APPEND CMAKE_MODULE_PATH "@CMAKE_CURRENT_BINARY_DIR@/@CMAKE_MODULE_INSTALL_PATH@")
else()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")

@DEPENDENT_LIBRARIES@
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
endif()
endif()

if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/options.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/options.cmake")
endif()
241 changes: 143 additions & 98 deletions CommonLibrary.cmake
Expand Up @@ -8,13 +8,16 @@
# * NAME_SOURCES for all compilation units
# * NAME_HEADERS for all internal header files
# * NAME_PUBLIC_HEADERS for public, installed header files
# * NAME_LINK_LIBRARIES for dependencies of name
# * NAME_LINK_LIBRARIES for dependencies of name. Use targets rather than
# NAME_LIBRARIES variable. Also use PUBLIC and PRIVATE for declaring transient
# dependencies for export target generation.
# * NAME_LIBRARY_TYPE or COMMON_LIBRARY_TYPE for SHARED or STATIC library, with
# COMMON_LIBRARY_TYPE being an option stored in the CMakeCache.
# * NAME_OMIT_LIBRARY_HEADER when set, no library header (name.h) is generated.
# * NAME_INCLUDE_NAME for the include directory and library header
# * NAME_NAMESPACE for api.h and version.h
# * NAME_OMIT_CHECK_TARGETS do not create cppcheck targets
# * NAME_OMIT_EXPORT do not export target in CommonPackageConfig.cmake
# * VERSION for the API version
# * VERSION_ABI for the ABI version
# * Optional Qt support:
Expand Down Expand Up @@ -44,53 +47,18 @@ set(COMMON_LIBRARY_TYPE SHARED CACHE STRING
"Library type {any combination of SHARED, STATIC}")
set_property(CACHE COMMON_LIBRARY_TYPE PROPERTY STRINGS SHARED STATIC)

macro(generate_library_header NAME)
get_filename_component(__base_name ${INCLUDE_NAME} NAME)

set(__generated_header ${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/${__base_name}.h)
set(__generated_header_in ${__generated_header}.in)

file(WRITE ${__generated_header_in}
"// generated by CommonLibrary.cmake, do not edit\n"
"#ifndef ${NAME}_H\n"
"#define ${NAME}_H\n")
foreach(PUBLIC_HEADER ${PUBLIC_HEADERS})
if(IS_ABSOLUTE ${PUBLIC_HEADER})
get_filename_component(PUBLIC_HEADER ${PUBLIC_HEADER} NAME)
endif()
if(NOT PUBLIC_HEADER MATCHES "defines.+\\.h" AND
(PUBLIC_HEADER MATCHES ".*\\.h$" OR PUBLIC_HEADER MATCHES ".*\\.hpp$"))
file(APPEND ${__generated_header_in}
"#include <${INCLUDE_NAME}/${PUBLIC_HEADER}>\n")
endif()
endforeach()
file(APPEND ${__generated_header_in} "#endif\n")

# configure only touches file if changed, saves compilation after reconfigure
configure_file( ${__generated_header_in} ${__generated_header} COPYONLY)
list(APPEND PUBLIC_HEADERS ${__generated_header})
endmacro()

#-------------------------------------------------------------------------------
# set debug postfix
#-------------------------------------------------------------------------------
macro(COMMON_SET_LIB_NAME_POSTFIX)
if (WIN32)
set(CMAKE_DEBUG_POSTFIX "_D")
elseif(COMMON_LIBRARY_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX "_debug")
else()
set(CMAKE_DEBUG_POSTFIX "")
endif()
endmacro()

# applying CMAKE_C(XX)_FLAGS to add_library only works from parent scope, hence
# the macro calling the function _common_library
macro(COMMON_LIBRARY Name)
macro(common_library Name)
common_compiler_flags()
_common_library(${Name} ${ARGN})
endmacro()

#-------------------------------------------------------------------------------
# Implementation
#-------------------------------------------------------------------------------

function(_common_library Name)
string(TOUPPER ${Name} NAME)

Expand All @@ -110,21 +78,24 @@ function(_common_library Name)
set(PUBLIC_HEADERS ${${NAME}_PUBLIC_HEADERS} ${${NAME}_MOC_PUBLIC_HEADERS})
set(LINK_LIBRARIES ${${NAME}_LINK_LIBRARIES})

configure_file(${CMAKE_SOURCE_DIR}/CMake/common/cpp/api.h
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/api.h @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/CMake/common/cpp/version.h
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/version.h @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/CMake/common/cpp/version.cpp
${CMAKE_CURRENT_BINARY_DIR}/version.cpp @ONLY)

# ${NAMESPACE}_API= -> Fix cppcheck error about not including version.h
list(APPEND CPPCHECK_EXTRA_ARGS
-D${NAME}_STATIC= -D${NAMESPACE}_API=)

list(APPEND PUBLIC_HEADERS
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/api.h
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/version.h)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
# Generate api.h and version.h/cpp for non-interface libraries
if(${NAME}_SOURCES)
configure_file(${CMAKE_SOURCE_DIR}/CMake/common/cpp/api.h
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/api.h @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/CMake/common/cpp/version.h
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/version.h @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/CMake/common/cpp/version.cpp
${CMAKE_CURRENT_BINARY_DIR}/version.cpp @ONLY)

# ${NAMESPACE}_API= -> Fix cppcheck error about not including version.h
list(APPEND CPPCHECK_EXTRA_ARGS
-D${NAME}_STATIC= -D${NAMESPACE}_API=)

list(APPEND PUBLIC_HEADERS
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/api.h
${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/version.h)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
endif()

if(NOT ${NAME}_OMIT_LIBRARY_HEADER)
generate_library_header(${NAME})
Expand Down Expand Up @@ -159,56 +130,71 @@ function(_common_library Name)
if(TARGET ${Name})
set(LibName "${Name}_${LIBRARY_TYPE}")
endif()
add_library(${LibName} ${LIBRARY_TYPE} ${SOURCES} ${HEADERS} ${PUBLIC_HEADERS})
set_target_properties(${LibName} PROPERTIES
VERSION ${VERSION} SOVERSION ${VERSION_ABI}
OUTPUT_NAME ${Name} FOLDER ${PROJECT_NAME})
# append a debug suffix to library name on windows or if user requests it
common_set_lib_name_postfix()
target_link_libraries(${LibName} ${LINK_LIBRARIES})
install(TARGETS ${LibName}
ARCHIVE DESTINATION ${LIBRARY_DIR} COMPONENT dev
RUNTIME DESTINATION bin COMPONENT lib
LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT lib)

if(NOT ${NAME}_OMIT_CHECK_TARGETS)
common_check_targets(${LibName})
endif()

# ignore user-specified targets, e.g. language bindings
list(FIND IGNORE_LIB_TARGETS ${LibName} _ignore_target)

if(_ignore_target EQUAL -1)
# add defines TARGET_DSO_NAME and TARGET_SHARED for dlopen() usage
get_target_property(_compile_definitions ${LibName} COMPILE_DEFINITIONS)
if(NOT _compile_definitions)
set(_compile_definitions) # clear _compile_definitions-NOTFOUND
endif()

if(MSVC OR XCODE_VERSION)
set(_libraryname ${CMAKE_SHARED_LIBRARY_PREFIX}${LibName}${CMAKE_SHARED_LIBRARY_SUFFIX})
else()
if(APPLE)
set(_libraryname ${CMAKE_SHARED_LIBRARY_PREFIX}${LibName}.${VERSION_ABI}${CMAKE_SHARED_LIBRARY_SUFFIX})
else()
set(_libraryname ${CMAKE_SHARED_LIBRARY_PREFIX}${LibName}${CMAKE_SHARED_LIBRARY_SUFFIX}.${VERSION_ABI})
endif()
if(NOT ${NAME}_SOURCES)
# fake add_library(${LibName} INTERFACE) from CMake 3
add_library(${LibName} ${PUBLIC_HEADERS})
set_target_properties(${LibName} PROPERTIES
LINKER_LANGUAGE CXX FOLDER ${PROJECT_NAME})

# declare include directories for this target when using it in the build
# tree; the install tree include directory is declared via install()
target_include_directories(${LibName} INTERFACE
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${OUTPUT_INCLUDE_DIR}>"
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
)
else()
# append a debug suffix to library name on windows or if user requests it
common_set_lib_name_postfix()

add_library(${LibName} ${LIBRARY_TYPE} ${SOURCES} ${HEADERS} ${PUBLIC_HEADERS})
set_target_properties(${LibName} PROPERTIES
VERSION ${VERSION} SOVERSION ${VERSION_ABI}
OUTPUT_NAME ${Name} FOLDER ${PROJECT_NAME})
target_link_libraries(${LibName} ${LINK_LIBRARIES})

# declare include directories for this target when using it in the build
# tree; the install tree include directory is declared via install()
target_include_directories(
${LibName} PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${OUTPUT_INCLUDE_DIR}>"
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
)

if(NOT ${NAME}_OMIT_CHECK_TARGETS)
common_check_targets(${LibName})
endif()

string(TOUPPER ${LibName} LIBNAME)
list(APPEND _compile_definitions
${LIBNAME}_SHARED ${LIBNAME}_DSO_NAME=\"${_libraryname}\")

set_target_properties(${LibName} PROPERTIES
COMPILE_DEFINITIONS "${_compile_definitions}")
common_enable_dlopen_usage(${LibName})

# for DoxygenRule.cmake and SubProject.cmake
set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_ALL_DEP_TARGETS
${LibName})
endif()

# for PackageConfig.cmake
set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_ALL_LIB_TARGETS
${LibName})
# add an alias with PROJECT_NAME to the target to ease detection of
# subproject inclusion in CommonConfig.cmake
if(NOT TARGET ${PROJECT_NAME}_ALIAS)
add_library(${PROJECT_NAME}_ALIAS ALIAS ${LibName})
endif()

# add target to export set if not excluded, written by
# CommonPackageConfig.cmake
if(${NAME}_OMIT_EXPORT)
install(TARGETS ${LibName}
ARCHIVE DESTINATION ${LIBRARY_DIR} COMPONENT dev
RUNTIME DESTINATION bin COMPONENT lib
LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT lib
INCLUDES DESTINATION include)
else()
install(TARGETS ${LibName}
EXPORT ${PROJECT_NAME}Targets
ARCHIVE DESTINATION ${LIBRARY_DIR} COMPONENT dev
RUNTIME DESTINATION bin COMPONENT lib
LIBRARY DESTINATION ${LIBRARY_DIR} COMPONENT lib
INCLUDES DESTINATION include)
endif()
endforeach()

Expand All @@ -223,3 +209,62 @@ function(_common_library Name)
install_files(include/${INCLUDE_NAME}
FILES ${PUBLIC_HEADERS} COMPONENT dev)
endfunction()

macro(generate_library_header NAME)
get_filename_component(__base_name ${INCLUDE_NAME} NAME)

set(__generated_header ${OUTPUT_INCLUDE_DIR}/${INCLUDE_NAME}/${__base_name}.h)
set(__generated_header_in ${__generated_header}.in)

file(WRITE ${__generated_header_in}
"// generated by CommonLibrary.cmake, do not edit\n"
"#ifndef ${NAME}_H\n"
"#define ${NAME}_H\n")
foreach(PUBLIC_HEADER ${PUBLIC_HEADERS})
if(IS_ABSOLUTE ${PUBLIC_HEADER})
get_filename_component(PUBLIC_HEADER ${PUBLIC_HEADER} NAME)
endif()
if(NOT PUBLIC_HEADER MATCHES "defines.+\\.h" AND
(PUBLIC_HEADER MATCHES ".*\\.h$" OR PUBLIC_HEADER MATCHES ".*\\.hpp$"))
file(APPEND ${__generated_header_in}
"#include <${INCLUDE_NAME}/${PUBLIC_HEADER}>\n")
endif()
endforeach()
file(APPEND ${__generated_header_in} "#endif\n")

# configure only touches file if changed, saves compilation after reconfigure
configure_file( ${__generated_header_in} ${__generated_header} COPYONLY)
list(APPEND PUBLIC_HEADERS ${__generated_header})
endmacro()

macro(common_set_lib_name_postfix)
if(WIN32)
set(CMAKE_DEBUG_POSTFIX "_D")
elseif(COMMON_LIBRARY_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX "_debug")
else()
set(CMAKE_DEBUG_POSTFIX "")
endif()
endmacro()

# add defines TARGET_DSO_NAME and TARGET_SHARED for dlopen() usage
function(common_enable_dlopen_usage Target)
get_target_property(_compile_definitions ${Target} COMPILE_DEFINITIONS)
if(NOT _compile_definitions)
set(_compile_definitions) # clear _compile_definitions-NOTFOUND
endif()
if(MSVC OR XCODE_VERSION)
set(_DSOName ${CMAKE_SHARED_LIBRARY_PREFIX}${Target}${CMAKE_SHARED_LIBRARY_SUFFIX})
else()
if(APPLE)
set(_DSOName ${CMAKE_SHARED_LIBRARY_PREFIX}${Target}.${VERSION_ABI}${CMAKE_SHARED_LIBRARY_SUFFIX})
else()
set(_DSOName ${CMAKE_SHARED_LIBRARY_PREFIX}${Target}${CMAKE_SHARED_LIBRARY_SUFFIX}.${VERSION_ABI})
endif()
endif()
string(TOUPPER ${Target} TARGET)
list(APPEND _compile_definitions
${TARGET}_SHARED ${TARGET}_DSO_NAME=\"${_DSOName}\")
set_target_properties(${Target} PROPERTIES
COMPILE_DEFINITIONS "${_compile_definitions}")
endfunction()
2 changes: 1 addition & 1 deletion CommonPackage.cmake
Expand Up @@ -122,7 +122,7 @@ macro(common_package Package_Name)
string(REGEX REPLACE "-" "_" __use_package_define ${__use_package_define})
list(APPEND COMMON_PACKAGE_DEFINES ${__use_package_define})

# for PackageConfig.cmake
# for CommonPackageConfig.cmake
if(NOT COMMON_LIBRARY_TYPE MATCHES "SHARED")
list(APPEND ${UPPER_PROJECT_NAME}_DEPENDENT_LIBRARIES ${Package_Name})
endif()
Expand Down

0 comments on commit 00d7d4a

Please sign in to comment.