Merge topic 'ImprovedCOMPONENTSSupportInFindPackage2'
6d100f9
find_package: Test rejection of required+optional componentsd81d83c
add macro check_required_components() to configure_package_config_file()b15c0b4
FPHSA(): add HANDLE_COMPONENTS option34108cd
find_package: add documentation for OPTIONAL_COMPONENTScdabde8
FPHSA(): add missing "]" to documentationf2e0a18
find_package: add OPTIONAL_COMPONENTS keyword
This commit is contained in:
commit
349ea3f6e6
|
@ -2,7 +2,8 @@
|
|||
#
|
||||
# CONFIGURE_PACKAGE_CONFIG_FILE(<input> <output> INSTALL_DESTINATION <path>
|
||||
# [PATH_VARS <var1> <var2> ... <varN>]
|
||||
# [NO_SET_AND_CHECK_MACRO] )
|
||||
# [NO_SET_AND_CHECK_MACRO]
|
||||
# [NO_CHECK_REQUIRED_COMPONENTS_MACRO])
|
||||
#
|
||||
# CONFIGURE_PACKAGE_CONFIG_FILE() should be used instead of the plain
|
||||
# CONFIGURE_FILE() command when creating the <Name>Config.cmake or <Name>-config.cmake
|
||||
|
@ -49,8 +50,10 @@
|
|||
# For absolute locations it works only if the absolute location is a subdirectory
|
||||
# of CMAKE_INSTALL_PREFIX.
|
||||
#
|
||||
# By default configure_package_config_file() also generates a macro set_and_check()
|
||||
# into the FooConfig.cmake file. This should be used instead of the normal set()
|
||||
# By default configure_package_config_file() also generates two helper macros,
|
||||
# set_and_check() and check_required_components() into the FooConfig.cmake file.
|
||||
#
|
||||
# set_and_check() should be used instead of the normal set()
|
||||
# command for setting directories and file locations. Additionally to setting the
|
||||
# variable it also checks that the referenced file or directory actually exists
|
||||
# and fails with a FATAL_ERROR otherwise. This makes sure that the created
|
||||
|
@ -58,6 +61,16 @@
|
|||
# When using the NO_SET_AND_CHECK_MACRO, this macro is not generated into the
|
||||
# FooConfig.cmake file.
|
||||
#
|
||||
# check_required_components(<package_name>) should be called at the end of the
|
||||
# FooConfig.cmake file if the package supports components.
|
||||
# This macro checks whether all requested, non-optional components have been found,
|
||||
# and if this is not the case, sets the Foo_FOUND variable to FALSE, so that the package
|
||||
# is considered to be not found.
|
||||
# It does that by testing the Foo_<Component>_FOUND variables for all requested
|
||||
# required components.
|
||||
# When using the NO_CHECK_REQUIRED_COMPONENTS option, this macro is not generated
|
||||
# into the FooConfig.cmake file.
|
||||
#
|
||||
# For an example see below the documentation for WRITE_BASIC_PACKAGE_VERSION_FILE().
|
||||
#
|
||||
#
|
||||
|
@ -114,6 +127,8 @@
|
|||
# ...
|
||||
# set_and_check(FOO_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
# set_and_check(FOO_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
|
||||
#
|
||||
# check_required_components(Foo)
|
||||
|
||||
|
||||
#=============================================================================
|
||||
|
@ -139,7 +154,7 @@ endmacro()
|
|||
|
||||
|
||||
function(CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
|
||||
set(options NO_SET_AND_CHECK_MACRO)
|
||||
set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||
set(oneValueArgs INSTALL_DESTINATION )
|
||||
set(multiValueArgs PATH_VARS )
|
||||
|
||||
|
@ -189,6 +204,21 @@ endmacro()
|
|||
")
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||
macro(check_required_components _NAME)
|
||||
foreach(comp \${\${_NAME}_FIND_COMPONENTS})
|
||||
if(NOT \${_NAME}_\${comp}_FOUND)
|
||||
if(\${_NAME}_FIND_REQUIRED_\${comp})
|
||||
set(\${_NAME}_FOUND FALSE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach(comp)
|
||||
endmacro()
|
||||
")
|
||||
endif()
|
||||
|
||||
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||
####################################################################################")
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
#
|
||||
# The second mode is more powerful and also supports version checking:
|
||||
# FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME [REQUIRED_VARS <var1>...<varN>]
|
||||
# [VERSION_VAR <versionvar>
|
||||
# [VERSION_VAR <versionvar>]
|
||||
# [HANDLE_COMPONENTS]
|
||||
# [CONFIG_MODE]
|
||||
# [FAIL_MESSAGE "Custom failure message"] )
|
||||
#
|
||||
|
@ -32,6 +33,11 @@
|
|||
# in the find_package() call. The EXACT keyword is also handled. The default
|
||||
# messages include information about the required version and the version
|
||||
# which has been actually found, both if the version is ok or not.
|
||||
# If the package supports components, use the HANDLE_COMPONENTS option to enable
|
||||
# handling them. In this case, find_package_handle_standard_args() will report
|
||||
# which components have been found and which are missing, and the <NAME>_FOUND
|
||||
# variable will be set to FALSE if any of the required components (i.e. not the
|
||||
# ones listed after OPTIONAL_COMPONENTS) are missing.
|
||||
# Use the option CONFIG_MODE if your FindXXX.cmake module is a wrapper for
|
||||
# a find_package(... NO_MODULE) call. In this case VERSION_VAR will be set
|
||||
# to <NAME>_VERSION and the macro will automatically check whether the
|
||||
|
@ -128,7 +134,7 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
|
|||
|
||||
# set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in
|
||||
# new extended or in the "old" mode:
|
||||
SET(options CONFIG_MODE)
|
||||
SET(options CONFIG_MODE HANDLE_COMPONENTS)
|
||||
SET(oneValueArgs FAIL_MESSAGE VERSION_VAR)
|
||||
SET(multiValueArgs REQUIRED_VARS)
|
||||
SET(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} )
|
||||
|
@ -189,6 +195,36 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
|
|||
ENDIF(NOT ${_CURRENT_VAR})
|
||||
ENDFOREACH(_CURRENT_VAR)
|
||||
|
||||
# component handling
|
||||
UNSET(FOUND_COMPONENTS_MSG)
|
||||
UNSET(MISSING_COMPONENTS_MSG)
|
||||
|
||||
IF(FPHSA_HANDLE_COMPONENTS)
|
||||
FOREACH(comp ${${_NAME}_FIND_COMPONENTS})
|
||||
IF(${_NAME}_${comp}_FOUND)
|
||||
|
||||
IF(NOT DEFINED FOUND_COMPONENTS_MSG)
|
||||
SET(FOUND_COMPONENTS_MSG "found components: ")
|
||||
ENDIF()
|
||||
SET(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}")
|
||||
|
||||
ELSE()
|
||||
|
||||
IF(NOT DEFINED MISSING_COMPONENTS_MSG)
|
||||
SET(MISSING_COMPONENTS_MSG "missing components: ")
|
||||
ENDIF()
|
||||
SET(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}")
|
||||
|
||||
IF(${_NAME}_FIND_REQUIRED_${comp})
|
||||
SET(${_NAME_UPPER}_FOUND FALSE)
|
||||
SET(MISSING_VARS "${MISSING_VARS} ${comp}")
|
||||
ENDIF()
|
||||
|
||||
ENDIF()
|
||||
ENDFOREACH(comp)
|
||||
SET(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}")
|
||||
SET(DETAILS "${DETAILS}[c${COMPONENT_MSG}]")
|
||||
ENDIF(FPHSA_HANDLE_COMPONENTS)
|
||||
|
||||
# version handling:
|
||||
SET(VERSION_MSG "")
|
||||
|
@ -240,7 +276,7 @@ FUNCTION(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
|
|||
|
||||
# print the result:
|
||||
IF (${_NAME_UPPER}_FOUND)
|
||||
FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG}" "${DETAILS}")
|
||||
FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}")
|
||||
ELSE (${_NAME_UPPER}_FOUND)
|
||||
|
||||
IF(FPHSA_CONFIG_MODE)
|
||||
|
|
|
@ -57,7 +57,7 @@ For example:
|
|||
# - This is a cool module
|
||||
# This module does really cool stuff.
|
||||
# It can do even more than you think.
|
||||
#
|
||||
#
|
||||
# It even needs to paragraphs to tell you about it.
|
||||
# And it defines the following variables:
|
||||
# VAR_COOL - this is great isn't it?
|
||||
|
@ -119,17 +119,31 @@ able to find the package. If the
|
|||
REQUIRED option is given to the command it will set the variable
|
||||
XXX_FIND_REQUIRED to true before loading the FindXXX.cmake module. If
|
||||
this variable is set the module should issue a FATAL_ERROR if the
|
||||
package cannot be found. For each package-specific component, say
|
||||
YYY, listed after the REQUIRED option a variable XXX_FIND_REQUIRED_YYY
|
||||
to true. The set of components listed after either the REQUIRED
|
||||
option or the COMPONENTS option will be specified in a
|
||||
XXX_FIND_COMPONENTS variable. This can be used by the FindXXX.cmake
|
||||
module to determine which sub-components of the package must be found.
|
||||
package cannot be found.
|
||||
If neither the QUIET nor REQUIRED options are given then the
|
||||
FindXXX.cmake module should look for the package and complain without
|
||||
error if the module is not found.
|
||||
|
||||
To get this behaviour you can use the FIND_PACKAGE_HANDLE_STANDARD_ARGS()
|
||||
A package can be provide sub-components.
|
||||
Those components can be listed after the COMPONENTS (or REQUIRED)
|
||||
or OPTIONAL_COMPONENTS keywords. The set of all listed components will be
|
||||
specified in a XXX_FIND_COMPONENTS variable.
|
||||
For each package-specific component, say Yyy, a variable XXX_FIND_REQUIRED_Yyy
|
||||
will be set to true if it listed after COMPONENTS and it will be set to false
|
||||
if it was listed after OPTIONAL_COMPONENTS.
|
||||
Using those variables a FindXXX.cmake module and also a XXXConfig.cmake package
|
||||
configuration file can determine whether and which components have been requested,
|
||||
and whether they were requested as required or as optional.
|
||||
For each of the requested components a XXX_Yyy_FOUND variable should be set
|
||||
accordingly.
|
||||
The per-package XXX_FOUND variable should be only set to true if all requested
|
||||
required components have been found. A missing optional component should not
|
||||
keep the XXX_FOUND variable from being set to true.
|
||||
If the package provides XXX_INCLUDE_DIRS and XXX_LIBRARIES variables, the include
|
||||
dirs and libraries for all components which were requested and which have been
|
||||
found should be added to those two variables.
|
||||
|
||||
To get this behaviour you can use the FIND_PACKAGE_HANDLE_STANDARD_ARGS()
|
||||
macro, as an example see FindJPEG.cmake.
|
||||
|
||||
For internal implementation, it's a generally accepted convention that variables starting with
|
||||
|
|
|
@ -90,6 +90,7 @@ void cmFindPackageCommand::GenerateDocumentation()
|
|||
this->CommandDocumentation =
|
||||
" find_package(<package> [version] [EXACT] [QUIET] [MODULE]\n"
|
||||
" [[REQUIRED|COMPONENTS] [components...]]\n"
|
||||
" [OPTIONAL_COMPONENTS components...]\n"
|
||||
" [NO_POLICY_SCOPE])\n"
|
||||
"Finds and loads settings from an external project. "
|
||||
"<package>_FOUND will be set to indicate whether the package was found. "
|
||||
|
@ -98,10 +99,14 @@ void cmFindPackageCommand::GenerateDocumentation()
|
|||
"The QUIET option disables messages if the package cannot be found. "
|
||||
"The MODULE option disables the second signature documented below. "
|
||||
"The REQUIRED option stops processing with an error message if the "
|
||||
"package cannot be found. "
|
||||
"A package-specific list of components may be listed after the "
|
||||
"REQUIRED option or after the COMPONENTS option if no REQUIRED "
|
||||
"option is given. "
|
||||
"package cannot be found."
|
||||
"\n"
|
||||
"A package-specific list of required components may be listed after the "
|
||||
"COMPONENTS option or directly after the REQUIRED option. "
|
||||
"Additional optional components may be listed after OPTIONAL_COMPONENTS. "
|
||||
"Available components and their influence on whether a package is "
|
||||
"considered to be found are defined by the target package."
|
||||
"\n"
|
||||
"The [version] argument requests a version with which the package found "
|
||||
"should be compatible (format is major[.minor[.patch[.tweak]]]). "
|
||||
"The EXACT option requests that the version be matched exactly. "
|
||||
|
@ -410,6 +415,8 @@ bool cmFindPackageCommand
|
|||
this->Name = args[0];
|
||||
std::string components;
|
||||
const char* components_sep = "";
|
||||
std::set<std::string> requiredComponents;
|
||||
std::set<std::string> optionalComponents;
|
||||
|
||||
// Check ancient compatibility.
|
||||
this->Compatibility_1_6 =
|
||||
|
@ -420,8 +427,8 @@ bool cmFindPackageCommand
|
|||
this->SearchPathSuffixes.push_back("");
|
||||
|
||||
// Parse the arguments.
|
||||
enum Doing { DoingNone, DoingComponents, DoingNames, DoingPaths,
|
||||
DoingPathSuffixes, DoingConfigs, DoingHints };
|
||||
enum Doing { DoingNone, DoingComponents, DoingOptionalComponents, DoingNames,
|
||||
DoingPaths, DoingPathSuffixes, DoingConfigs, DoingHints };
|
||||
Doing doing = DoingNone;
|
||||
cmsys::RegularExpression version("^[0-9.]+$");
|
||||
bool haveVersion = false;
|
||||
|
@ -465,6 +472,11 @@ bool cmFindPackageCommand
|
|||
this->Compatibility_1_6 = false;
|
||||
doing = DoingComponents;
|
||||
}
|
||||
else if(args[i] == "OPTIONAL_COMPONENTS")
|
||||
{
|
||||
this->Compatibility_1_6 = false;
|
||||
doing = DoingOptionalComponents;
|
||||
}
|
||||
else if(args[i] == "NAMES")
|
||||
{
|
||||
configArgs.insert(i);
|
||||
|
@ -528,12 +540,23 @@ bool cmFindPackageCommand
|
|||
this->Compatibility_1_6 = false;
|
||||
doing = DoingNone;
|
||||
}
|
||||
else if(doing == DoingComponents)
|
||||
else if((doing == DoingComponents) || (doing == DoingOptionalComponents))
|
||||
{
|
||||
// Set a variable telling the find script this component
|
||||
// Set a variable telling the find script whether this component
|
||||
// is required.
|
||||
const char* isRequired = "1";
|
||||
if (doing == DoingOptionalComponents)
|
||||
{
|
||||
isRequired = "0";
|
||||
optionalComponents.insert(args[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
requiredComponents.insert(args[i]);
|
||||
}
|
||||
|
||||
std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
|
||||
this->AddFindDefinition(req_var.c_str(), "1");
|
||||
this->AddFindDefinition(req_var.c_str(), isRequired);
|
||||
|
||||
// Append to the list of required components.
|
||||
components += components_sep;
|
||||
|
@ -584,6 +607,22 @@ bool cmFindPackageCommand
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> doubledComponents;
|
||||
std::set_intersection(requiredComponents.begin(), requiredComponents.end(),
|
||||
optionalComponents.begin(), optionalComponents.end(),
|
||||
std::back_inserter(doubledComponents));
|
||||
if(!doubledComponents.empty())
|
||||
{
|
||||
cmOStringStream e;
|
||||
e << "called with components that are both required and optional:\n";
|
||||
for(unsigned int i=0; i<doubledComponents.size(); ++i)
|
||||
{
|
||||
e << " " << doubledComponents[i] << "\n";
|
||||
}
|
||||
this->SetError(e.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Maybe choose one mode exclusively.
|
||||
this->UseFindModules = configArgs.empty();
|
||||
this->UseConfigFiles = moduleArgs.empty();
|
||||
|
|
|
@ -37,6 +37,12 @@ FIND_PACKAGE(VersionTestB 1.2)
|
|||
FIND_PACKAGE(VersionTestC 1.2.3)
|
||||
FIND_PACKAGE(VersionTestD 1.2.3.4)
|
||||
|
||||
|
||||
FIND_PACKAGE(LotsOfComponents COMPONENTS AComp OPTIONAL_COMPONENTS BComp CComp)
|
||||
IF(NOT LOTSOFCOMPONENTS_FOUND)
|
||||
MESSAGE(SEND_ERROR "LotsOfComponents not found !")
|
||||
ENDIF()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test system package registry if possible.
|
||||
SET(CMakeTestSystemPackage "")
|
||||
|
@ -330,6 +336,8 @@ configure_package_config_file(RelocatableConfig.cmake.in "${CMAKE_CURRENT_BINARY
|
|||
PATH_VARS INCLUDE_INSTALL_DIR SHARE_INSTALL_DIR CURRENT_BUILD_DIR
|
||||
)
|
||||
|
||||
set(Relocatable_FIND_COMPONENTS AComp BComp CComp)
|
||||
set(Relocatable_FIND_REQUIRED_BComp 1)
|
||||
include("${CMAKE_CURRENT_BINARY_DIR}/RelocatableConfig.cmake")
|
||||
|
||||
if(NOT "${RELOC_INCLUDE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
|
@ -344,6 +352,14 @@ if(NOT "${RELOC_BUILD_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
|
|||
message(SEND_ERROR "RELOC_BUILD_DIR set by configure_package_config_file() is set to \"${RELOC_BUILD_DIR}\" (expected \"${CMAKE_CURRENT_BINARY_DIR}\")")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED Relocatable_FOUND)
|
||||
message(SEND_ERROR "Relocatable_FOUND not defined !")
|
||||
endif()
|
||||
|
||||
if(Relocatable_FOUND)
|
||||
message(SEND_ERROR "Relocatable_FOUND set to TRUE !")
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test write_basic_config_version_file().
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
set(LOC_FOO TRUE)
|
||||
|
||||
set(LotsOfComponents_AComp_FOUND TRUE)
|
||||
set(LotsOfComponents_BComp_FOUND FALSE)
|
||||
set(LotsOfComponents_CComp_FOUND TRUE)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(LotsOfComponents REQUIRED_VARS LOC_FOO
|
||||
HANDLE_COMPONENTS)
|
|
@ -3,3 +3,9 @@
|
|||
set(RELOC_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
set(RELOC_SHARE_DIR "@PACKAGE_SHARE_INSTALL_DIR@")
|
||||
set_and_check(RELOC_BUILD_DIR "@PACKAGE_CURRENT_BUILD_DIR@")
|
||||
|
||||
set(Relocatable_AComp_FOUND TRUE)
|
||||
set(Relocatable_BComp_FOUND FALSE)
|
||||
set(Relocatable_CComp_FOUND FALSE)
|
||||
|
||||
check_required_components(Relocatable)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -0,0 +1,8 @@
|
|||
CMake Error at ComponentRequiredAndOptional.cmake:1 \(find_package\):
|
||||
find_package called with components that are both required and optional:
|
||||
|
||||
CompA
|
||||
CompB
|
||||
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1 @@
|
|||
find_package(NotHere REQUIRED CompA CompB CompC OPTIONAL_COMPONENTS CompA CompB CompD)
|
|
@ -1,5 +1,6 @@
|
|||
include(RunCMake)
|
||||
|
||||
run_cmake(ComponentRequiredAndOptional)
|
||||
run_cmake(MissingNormal)
|
||||
run_cmake(MissingNormalRequired)
|
||||
run_cmake(MissingNormalVersion)
|
||||
|
|
Loading…
Reference in New Issue