liblbfgs/cmake/Subproject.cmake
Andreas Schuh e4f94fabd2 Enable build configuration with CMake (#18)
* add: CMake configuration files

* doc: Variables affecting CMake configuration installation

* fix: Rename CMake module and project -> subproject macro

The problem is that macro/function definitions in CMake have global scope. Therefore, use a custom macro name rather than replacing the standard CMake command.

* enh: Set LibLBFGS_LIBRARIES variable in LibLBFGSConfig.cmake

For (older) libLBFGS installations that do not contain the CMake package configuration file, a FindLibLBFGS.cmake module will be needed which then sets LibLBFGS_LIBRARIES to the path of the actual library file instead of an imported CMake build target.

* doc: Update CMakeLists.txt comment

* fix: Do not inherit CMake options from parent project

The parent project must explicitly set ${PROJECT_NAME}_${varname} before including the subproject to the value of the same named parent option ${varname} if super- and sub-project should both use the same value.

* fix: Export of targets as part of parent project

* fix: Do not import targets when liblbfgs TARGET exists

* enh: Prefix Subproject module functions, use unique target names and non-imported target ALIAS

* enh: Remove check if PROJECT_NAME is defined

* fix: Update documentation of CMakeLists.txt

* enh: Document LBFGS_INSTALL_HEADERS
2019-06-04 21:04:00 +09:00

361 lines
13 KiB
CMake

# ==============================================================================
# Utility CMake functions for standalone or subproject build configuration.
#
# This CMake source file is released into the public domain.
#
# Author: Andreas Schuh (andreas.schuh.84@gmail.com)
# ==============================================================================
include(CMakeParseArguments)
# ----------------------------------------------------------------------------
## Start (sub-)project
#
# This macro is for CMake versions 2.8.12 which did not have the VERSION
# argument yet. It additionally sets the PROJECT_SOVERSION to either the
# project major version number or ${PROJECT_NAME}_SOVERSION if set.
# When ${PROJECT_NAME}_IS_SUBPROJECT is not defined, PROJECT_IS_SUBPROJECT
# is set to TRUE when the source directory of this project is not the
# top-level source directory, and FALSE otherwise.
#
# Besides the PROJECT_NAME variable, this macro also sets PROJECT_NAME_LOWER
# and PROJECT_NAME_UPPER to the respective all lower- or uppercase strings.
macro (subproject name)
cmake_parse_arguments("" "" "VERSION;SOVERSION" "LANGUAGES" ${ARGN})
if (_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif ()
if (NOT _VERSION)
set(_VERSION 0.0.0) # invalid version number
endif ()
unset(PROJECT_VERSION)
unset(PROJECT_VERSION_MAJOR)
unset(PROJECT_VERSION_MINOR)
unset(PROJECT_VERSION_PATCH)
unset(${name}_VERSION)
unset(${name}_VERSION_MAJOR)
unset(${name}_VERSION_MINOR)
unset(${name}_VERSION_PATCH)
project(${name} ${_LANGUAGES})
set(PROJECT_VERSION "${_VERSION}")
_subproject_split_version_numbers(${PROJECT_VERSION}
PROJECT_VERSION_MAJOR
PROJECT_VERSION_MINOR
PROJECT_VERSION_PATCH
)
set(${name}_VERSION ${PROJECT_VERSION})
set(${name}_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(${name}_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(${name}_VERSION_PATCH ${PROJECT_VERSION_PATCH})
if (NOT _SOVERSION)
set(_SOVERSION ${PROJECT_VERSION_MAJOR})
endif ()
_subproject_set_abi_version(PROJECT_SOVERSION ${_SOVERSION})
set(${name}_SOVERSION ${PROJECT_SOVERSION})
_subproject_check_if_subproject()
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER)
unset(_VERSION)
unset(_SOVERSION)
unset(_LANGUAGES)
unset(_UNPARSED_ARGUMENTS)
if (${PROJECT_NAME}_EXPORT_NAME)
set(PROJECT_EXPORT_NAME ${${PROJECT_NAME}_EXPORT_NAME})
else ()
set(PROJECT_EXPORT_NAME ${PROJECT_NAME})
endif ()
set_property(GLOBAL PROPERTY ${PROJECT_NAME}_HAVE_EXPORT FALSE)
endmacro ()
# ----------------------------------------------------------------------------
## Add configuration variable
#
# The default value of the (cached) configuration value can be overridden
# either on the CMake command-line or the super-project by setting the
# ${PROJECT_NAME}_${varname} variable. When this project is a subproject
# of another project, i.e., PROJECT_IS_SUBPROJECT is TRUE, the variable
# is not added to the CMake cache and set to the value of
# ${PROJECT_NAME}_${varname} regardless if the parent project defines
# a (cached) variable of the same name. Otherwise, when this project is
# a standalone project, the variable is cached.
macro (subproject_define type varname docstring default)
if (ARGC GREATER 5)
message (FATAL_ERROR "Too many macro arguments")
endif ()
if (NOT DEFINED ${PROJECT_NAME}_${varname})
if (PROJECT_IS_SUBPROJECT AND ARGC EQUAL 5)
set(${PROJECT_NAME}_${varname} "${ARGV4}")
else ()
set(${PROJECT_NAME}_${varname} "${default}")
endif ()
endif ()
if (PROJECT_IS_SUBPROJECT)
set(${varname} "${${PROJECT_NAME}_${varname}}")
else ()
set(${varname} "${${PROJECT_NAME}_${varname}}" CACHE ${type} "${docstring}")
endif ()
endmacro ()
# ----------------------------------------------------------------------------
## Set property of (cached) configuration variable
#
# This command does nothing when the previously defined variable was not added
# to the CMake cache because this project is build as subproject unless
# the property to be set is the VALUE of the configuration variable.
#
# @see subproject_define
macro (subproject_set_property varname property value)
_subproject_check_if_cached(_is_cached ${varname})
if (_is_cached)
if (property STREQUAL ADVANCED)
if (${value})
mark_as_advanced(FORCE ${varname})
else ()
mark_as_advanced(CLEAR ${varname})
endif ()
else ()
set_property(CACHE ${varname} PROPERTY "${property}" "${value}")
endif ()
elseif (property STREQUAL VALUE)
set(${varname} "${value}")
endif ()
unset(_is_cached)
endmacro ()
# ----------------------------------------------------------------------------
## Get unique target name
macro (subproject_target_name uid target)
if (${PROJECT_NAME}_${target}_TARGET_NAME)
set(${uid} ${${PROJECT_NAME}_${target}_TARGET_NAME})
elseif (PROJECT_IS_SUBPROJECT)
set(${uid} "${PROJECT_NAME_LOWER}_${target}")
else ()
set(${uid} "${target}")
endif ()
endmacro ()
# ----------------------------------------------------------------------------
## Add executable target
function (subproject_add_executable uid target)
subproject_target_name(_uid ${target})
add_executable(${_uid} ${ARGN})
if (NOT ${PROJECT_NAME}_NO_ALIASES)
add_executable(${PROJECT_NAME}::${target} ALIAS ${_uid})
endif ()
set(${uid} "${_uid}" PARENT_SCOPE)
endfunction ()
# ----------------------------------------------------------------------------
## Add library target
function (subproject_add_library uid target)
subproject_target_name(_uid ${target})
add_library(${_uid} ${ARGN})
if (NOT ${PROJECT_NAME}_NO_ALIASES)
add_library(${PROJECT_NAME}::${target} ALIAS ${_uid})
endif ()
set(${uid} "${_uid}" PARENT_SCOPE)
endfunction ()
# ----------------------------------------------------------------------------
## Install files of library target
function (subproject_install_library target)
# parse arguments
if (NOT TARGET ${target})
message(FATAL_ERROR "Unknown target: ${target}")
endif ()
get_target_property(type ${target} TYPE)
if (NOT PROJECT_IS_SUBPROJECT OR NOT "^${type}$" STREQUAL "^STATIC_LIBRARY$" OR ${PROJECT_NAME}_INSTALL_STATIC_LIBS)
cmake_parse_arguments(""
""
"INCLUDE_DESTINATION;LIBRARY_DESTINATION;RUNTIME_DESTINATION"
"PUBLIC_HEADER_FILES"
${ARGN}
)
if (_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Too many or unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif ()
# override (default) arguments
if (${PROJECT_NAME}_INSTALL_RUNTIME_DIR)
set(_RUNTIME_DESTINATION "${${PROJECT_NAME}_INSTALL_RUNTIME_DIR}")
elseif (NOT _RUNTIME_DESTINATION)
set(_RUNTIME_DESTINATION bin)
endif ()
if (${PROJECT_NAME}_INSTALL_INCLUDE_DIR)
set(_INCLUDE_DESTINATION "${${PROJECT_NAME}_INSTALL_INCLUDE_DIR}")
elseif (NOT _INCLUDE_DESTINATION)
set(_INCLUDE_DESTINATION include)
endif ()
if (${PROJECT_NAME}_INSTALL_LIBRARY_DIR)
set(_LIBRARY_DESTINATION "${${PROJECT_NAME}_INSTALL_LIBRARY_DIR}")
elseif (NOT _LIBRARY_DESTINATION)
set(_LIBRARY_DESTINATION lib)
endif ()
# skip installation of static subproject library
if (_PUBLIC_HEADER_FILES AND (NOT DEFINED ${PROJECT_NAME}_INSTALL_HEADERS OR ${PROJECT_NAME}_INSTALL_HEADERS))
install(FILES ${_PUBLIC_HEADER_FILES} DESTINATION ${_INCLUDE_DESTINATION} COMPONENT Development)
target_include_directories(${target} INTERFACE "$<INSTALL_INTERFACE:${_INCLUDE_DESTINATION}>")
endif ()
install(TARGETS ${target} EXPORT ${PROJECT_EXPORT_NAME}
RUNTIME DESTINATION ${_RUNTIME_DESTINATION} COMPONENT RuntimeLibraries
LIBRARY DESTINATION ${_LIBRARY_DESTINATION} COMPONENT RuntimeLibraries
ARCHIVE DESTINATION ${_LIBRARY_DESTINATION} COMPONENT Development
)
set_property(GLOBAL PROPERTY ${PROJECT_NAME}_HAVE_EXPORT TRUE)
endif ()
endfunction ()
# ----------------------------------------------------------------------------
## Whether to install package configuration files of (sub-)project
macro (subproject_get_install_config_option var)
if (PROJECT_IS_SUBPROJECT AND ${PROJECT_NAME}_INSTALL_CONFIG)
set (${var} 1)
elseif (NOT PROJECT_IS_SUBPROJECT AND (NOT DEFINED ${PROJECT_NAME}_INSTALL_CONFIG OR ${PROJECT_NAME}_INSTALL_CONFIG))
set (${var} 1)
else ()
set (${var} 0)
endif ()
endmacro ()
# ----------------------------------------------------------------------------
## Get relative path of package configuration installation directory
macro (subproject_get_install_config_dir config_dir)
if (${PROJECT_NAME}_INSTALL_CONFIG_DIR)
set(${config_dir} "${${PROJECT_NAME}_INSTALL_CONFIG_DIR}")
elseif (WIN32 AND NOT MINGW AND NOT CYGWIN)
set(${config_dir} "cmake")
else ()
set(${config_dir} "lib/cmake/${PROJECT_NAME_LOWER}")
endif ()
endmacro ()
# ----------------------------------------------------------------------------
## Install package configuration files
function (subproject_install_config_files)
subproject_get_install_config_option (_install_config)
if (_install_config)
# parse arguments
cmake_parse_arguments("" "" "DESTINATION" "FILES" ${ARGN})
if (_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif ()
if (${PROJECT_NAME}_INSTALL_CONFIG_DIR OR NOT _DESTINATION)
subproject_get_install_config_dir(_DESTINATION)
endif ()
# install package configuration files if not overriden
install(FILES ${_FILES} DESTINATION ${_DESTINATION} COMPONENT Development)
endif ()
endfunction ()
# ----------------------------------------------------------------------------
## Generate build tree targets configuration file
function (subproject_export)
cmake_parse_arguments("" "" "" "TARGETS" ${ARGN})
export(TARGETS ${_TARGETS} ${_UNPARSED_ARGUMENTS}
FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake"
NAMESPACE "${PROJECT_NAME}::"
)
endfunction ()
# ----------------------------------------------------------------------------
## Install exported targets configuration files
function (subproject_install_exports)
subproject_get_install_config_option (_install_config)
if (_install_config)
# parse arguments
cmake_parse_arguments("" "" "DESTINATION" "" ${ARGN})
if (_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif ()
if (${PROJECT_NAME}_INSTALL_CONFIG_DIR OR NOT _DESTINATION)
subproject_get_install_config_dir(_DESTINATION)
endif ()
# install export sets
get_property(have_export GLOBAL PROPERTY ${PROJECT_NAME}_HAVE_EXPORT)
if (have_export)
install(EXPORT ${PROJECT_EXPORT_NAME}
FILE "${PROJECT_NAME}Targets.cmake"
NAMESPACE "${PROJECT_NAME}::"
DESTINATION "${_DESTINATION}"
COMPONENT Development
)
endif ()
endif ()
endfunction ()
# ==============================================================================
# Private auxiliary functions
# ==============================================================================
# ----------------------------------------------------------------------------
# Extract version numbers from version string
function (_subproject_split_version_numbers version major minor patch)
if (version MATCHES "([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(rc[1-9][0-9]*|[a-z]+)?")
if (CMAKE_MATCH_1)
set(_major ${CMAKE_MATCH_1})
else ()
set(_major 0)
endif ()
if (CMAKE_MATCH_2)
set(_minor ${CMAKE_MATCH_2})
string (REGEX REPLACE "^\\." "" _minor "${_minor}")
else ()
set(_minor 0)
endif ()
if (CMAKE_MATCH_3)
set(_patch ${CMAKE_MATCH_3})
string(REGEX REPLACE "^\\." "" _patch "${_patch}")
else ()
set(_patch 0)
endif ()
else ()
set(_major 0)
set(_minor 0)
set(_patch 0)
endif ()
set("${major}" "${_major}" PARENT_SCOPE)
set("${minor}" "${_minor}" PARENT_SCOPE)
set("${patch}" "${_patch}" PARENT_SCOPE)
endfunction ()
# ----------------------------------------------------------------------------
# Set ABI version number
#
# When the variable ${PROJECT_NAME}_SOVERSION is set, it overrides the ABI
# version number argument.
macro (_subproject_set_abi_version varname number)
if (${PROJECT_NAME}_SOVERSION)
set(${varname} "${${PROJECT_NAME}_SOVERSION}")
else ()
set(${varname} "${number}")
endif ()
endmacro ()
# ----------------------------------------------------------------------------
# Determine if project is build as subproject
#
# When included as subproject (e.g., as Git submodule/subtree) in the source
# tree of a project that uses it, no variables should be added to the CMake cache;
# users may set the (non-cached) variable ${PROJECT_NAME}_IS_SUBPROJECT before
# the add_subdirectory command that adds this subdirectory to the build.
#
# @returns Sets PROJECT_IS_SUBPROJECT to either TRUE or FALSE.
macro (_subproject_check_if_subproject)
if (DEFINED ${PROJECT_NAME}_IS_SUBPROJECT)
set(PROJECT_IS_SUBPROJECT ${PROJECT_NAME}_IS_SUBPROJECT)
elseif ("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
set(PROJECT_IS_SUBPROJECT FALSE)
else ()
set(PROJECT_IS_SUBPROJECT TRUE)
endif ()
endmacro ()
# ----------------------------------------------------------------------------
# Determine if cache entry exists
macro (_subproject_check_if_cached retvar varname)
if (DEFINED ${varname})
get_property(${retvar} CACHE ${varname} PROPERTY TYPE SET)
else ()
set(${retvar} FALSE)
endif ()
endmacro ()