On 24/12/2015 12:07, Stephen Kelly wrote:
Roger Leigh wrote:

On 21/12/2015 15:07, Stephen Kelly wrote:
Roger Leigh wrote:
I can understand why REQUIRED and related
arguments are omitted--that is why find_dependency exists--but I'd quite
like to be able to specify COMPONENTS where needed e.g. with FindBoost,
and this is not currently possible.

I don't remember whether this was discussed when designing
find_dependency. Perhaps COMPONENTS could be added to the macro now.

Currently, for my own needs, COMPONENTS is the single missing piece.  If
it would be possible to add this, it would certainly be very useful.

I'd be happy to provide a patch for COMPONENTS if you like.

I think the reason that the macro does not support COMPONENTS is that I
don't understand how COMPONENTS is supposed to work. For example:

  find_package(Qt5 COMPONENTS Widgets)
  # Is Qt5_FOUND set to true or false?
  find_package(Qt5 COMPONENTS Quick)
  # ... what about now?

What does <Package>_FOUND ever mean if the find_package is called multiple
times with different components? Does it mean anything?

I would think that it would mean whether the last call succeeded or failed for the requested components at that time. It wouldn't have any greater or more permanent meaning. To the best of my knowledge, that's what the status quo is for find_package.

Should CMake disallow multiple use of find_package with the same package and
with different components?

If user code has

  find_package(Qt5 COMPONENTS Widgets)
  find_package(FooBar REQUIRED)

and FooBarConfig.cmake has

  find_dependency(Qt5 COMPONENTS Quick)

then the if(Qt5_FOUND) in the macro will mean it will not try to find the
Quick component.

So, there may be a need to establish a convention such as

  <Package>_<Component>_FOUND

and check that for each component in the macro.

Right now, it's certainly possible for this to happen. I'm seeing this myself with multiple packages (or individual components' config scripts) requiring different sets of Boost components. Each invokes find_dependency with its own needed set of components.

You would also need to think through everything and find out whether that is
really a full solution. I haven't thought more deeply about it than the
above.

Note that the above example with Qt 5 can not currently happen in practice
because the find_dependency macro does not support COMPONENTS, so config
files must instead use

  find_dependency(Qt5Quick)

if they want to use the macro currently. This is a good thing.

So, I would like to know if

* adding COMPONENTS support to the find_dependency macro is the right thing
   to do
* or if the right thing to do is to move away from support for COMPONENTS
   generally because of its messy semantics
* or if find_package should change to not support multiple calls for the
   same package.

I really don't know the answer - I think someone needs to do deep thinking
about it.

I would prefer the macro not be changed before that deep thinking about
COMPONENTS generally is done.

I've attached a patch for a very simple modification to find_dependency. I'm not proposing that it be merged, it's just a suggestion for further discussion. I've tested it with my own packages with multiple find_dependency(Boost COMPONENTS ...) calls. Some rationale and thoughts follow:

Regarding the messy semantics of find_package, I agree it's not ideal. However, I find the differing behaviour of find_dependency and find_package even less so. Ideally I should be able to call both multiple times, or neither. While as you mentioned for Qt5, it's possible to work around this by calling find_dependency for each component by hand, but that's not possible for all packages. Boost for example does not work this way, making this a major blocker to using find_dependency with Boost. I'm basically blocked by this right now, and the patch here removes the single call restriction and passes through COMPONENTS, making it work. That would at the very least make things consistent and functional.

I don't think it's in general going to be possible to avoid multiple calls. Complex projects including or depending upon other projects are going to do this inevitably, and they may have differing version requirements as well as different sets of components. And it's already in established use. I don't think for find_dependency that returning immediately if ${dep}_FOUND is true is correct behaviour--it skips the version check, and I think it should be calling find_package to do this check under all circumstances. I think it's trading correctness for speed, but I really would prefer to have the version check performed correctly every time.

Regarding having <Package>_<Component>_FOUND, I think this would be useful irrespective of any find_dependency changes. And coupled with the above patch, find_dependency could be extended to return immediately if the version check is satisfied *and* all the requested components are found (if specified).


Kind regards,
Roger
>From c329228dca2b44d480d6fa889bff7ec24c77ecc8 Mon Sep 17 00:00:00 2001
From: Roger Leigh <r.le...@dundee.ac.uk>
Date: Wed, 24 Feb 2016 22:11:14 +0000
Subject: [PATCH] find_dependency: Allow use of the find_package COMPONENTS
 option

- Parse the COMPONENTS option with cmake_parse_arguments after
  the optional <version> and EXACT arguments are parsed.  This
  was done so that additional options could be added in the
  future.
- Pass through the COMPONENTS to find_package
- Drop the "if (NOT ${dep}_FOUND)" check.  This prevented
  find_dependency from being invoked multiple times.  For
  example, different <package>Config.cmake scripts may
  call find_dependency(Boost) with different sets of
  required components.
- Update documentation.  EXACT is not a forwarded parameter.
  COMPONENTS is allowed.  Minor markup enhancements.
---
 Modules/CMakeFindDependencyMacro.cmake | 142 +++++++++++++++++++++------------
 1 file changed, 90 insertions(+), 52 deletions(-)

diff --git a/Modules/CMakeFindDependencyMacro.cmake 
b/Modules/CMakeFindDependencyMacro.cmake
index 73efaae..898755b 100644
--- a/Modules/CMakeFindDependencyMacro.cmake
+++ b/Modules/CMakeFindDependencyMacro.cmake
@@ -4,18 +4,26 @@
 #
 # ::
 #
-#     find_dependency(<dep> [<version> [EXACT]])
+#     find_dependency(<dep> [<version> [EXACT]] [COMPONENTS components...])
 #
 #
-# ``find_dependency()`` wraps a :command:`find_package` call for a package
-# dependency. It is designed to be used in a <package>Config.cmake file, and it
-# forwards the correct parameters for EXACT, QUIET and REQUIRED which were
-# passed to the original :command:`find_package` call.  It also sets an
-# informative diagnostic message if the dependency could not be found.
+# ``find_dependency()`` wraps a :command:`find_package` call for a
+# package dependency. It is designed to be used in a
+# :file:`<package>Config.cmake` file, and it forwards the correct
+# parameters for QUIET and REQUIRED which were passed to the original
+# :command:`find_package` call.  It also sets an informative
+# diagnostic message if the dependency could not be found.
+#
+# The allowed arguments are a subset of the :command:`find_package`
+# arguments.  ``<dep>`` is the name of the dependency (package) to
+# find.  The package version ``<version>`` (and its ``EXACT`` option),
+# and the ``COMPONENTS`` component list are forwarded to
+# :command:`find_package` unmodified.
 #
 
 #=============================================================================
 # Copyright 2013 Stephen Kelly <steve...@gmail.com>
+# Copyright 2016 Roger Leigh <rle...@codelibre.net>
 #
 # Distributed under the OSI-approved BSD License (the "License");
 # see accompanying file Copyright.txt for details.
@@ -27,59 +35,89 @@
 # (To distribute this file outside of CMake, substitute the full
 #  License text for the above reference.)
 
+include(CMakeParseArguments)
+
 macro(find_dependency dep)
-  if (NOT ${dep}_FOUND)
-    set(cmake_fd_version)
-    if (${ARGC} GREATER 1)
-      if ("${ARGV1}" STREQUAL "")
-        message(FATAL_ERROR "Invalid arguments to find_dependency. VERSION is 
empty")
-      endif()
-      if ("${ARGV1}" STREQUAL EXACT)
-        message(FATAL_ERROR "Invalid arguments to find_dependency. EXACT may 
only be specified if a VERSION is specified")
-      endif()
-      set(cmake_fd_version ${ARGV1})
-    endif()
-    set(cmake_fd_exact_arg)
-    if(${ARGC} GREATER 2)
-      if (NOT "${ARGV2}" STREQUAL EXACT)
-        message(FATAL_ERROR "Invalid arguments to find_dependency")
-      endif()
-      set(cmake_fd_exact_arg EXACT)
-    endif()
-    if(${ARGC} GREATER 3)
-      message(FATAL_ERROR "Invalid arguments to find_dependency")
+  set(options)
+  set(oneValueArgs)
+  set(multiValueArgs COMPONENTS)
+  set(cmake_fd_option_names ${options} ${oneValueArgs} ${multiValueArgs})
+  set(cmake_fd_args ${ARGN})
+
+  set(cmake_fd_version)
+  if (${ARGC} GREATER 1)
+    if ("${ARGV1}" STREQUAL "")
+      message(FATAL_ERROR "Invalid arguments to find_dependency. VERSION is 
empty")
     endif()
-    set(cmake_fd_quiet_arg)
-    if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
-      set(cmake_fd_quiet_arg QUIET)
+    if ("${ARGV1}" STREQUAL EXACT)
+      message(FATAL_ERROR "Invalid arguments to find_dependency. EXACT may 
only be specified if a VERSION is specified")
     endif()
-    set(cmake_fd_required_arg)
-    if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
-      set(cmake_fd_required_arg REQUIRED)
+    list(FIND cmake_fd_option_names ${ARGV1} cmake_fd_option_found)
+    if (cmake_fd_option_found EQUAL -1)
+      set(cmake_fd_version ${ARGV1})
+      # Pop version
+      list(REMOVE_AT cmake_fd_args 0)
+      if (${ARGC} GREATER 2)
+        if("${ARGV2}" STREQUAL "EXACT")
+          # Pop EXACT
+          set(cmake_fd_exact_arg "EXACT")
+          list(REMOVE_AT CMAKE_FD_ARGS 0)
+        else()
+          set(cmake_fd_exact_arg)
+        endif()
+      endif()
     endif()
+    set(cmake_fd_option_found)
+  endif()
 
-    get_property(cmake_fd_alreadyTransitive GLOBAL PROPERTY
-      _CMAKE_${dep}_TRANSITIVE_DEPENDENCY
-    )
+  # Parse remaining args (if any)
+  cmake_parse_arguments(cmake_fd "${options}" "${oneValueArgs}" 
"${multiValueArgs}" ${cmake_fd_args})
 
-    find_package(${dep} ${cmake_fd_version}
-        ${cmake_fd_exact_arg}
-        ${cmake_fd_quiet_arg}
-        ${cmake_fd_required_arg}
-    )
+  if(cmake_fd_UNPARSED_ARGUMENTS)
+    message(FATAL_ERROR "Unknown keywords given to FIND_DEPENDENCY(): 
\"${cmake_fd_UNPARSED_ARGUMENTS}\"")
+  endif()
 
-    if(NOT DEFINED cmake_fd_alreadyTransitive OR cmake_fd_alreadyTransitive)
-      set_property(GLOBAL PROPERTY _CMAKE_${dep}_TRANSITIVE_DEPENDENCY TRUE)
-    endif()
+  set(cmake_fd_quiet_arg)
+  if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
+    set(cmake_fd_quiet_arg QUIET)
+  endif()
+  set(cmake_fd_required_arg)
+  if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
+    set(cmake_fd_required_arg REQUIRED)
+  endif()
+  if(cmake_fd_COMPONENTS)
+    list(INSERT cmake_fd_COMPONENTS 0 COMPONENTS)
+  endif()
 
-    if (NOT ${dep}_FOUND)
-      set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE 
"${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency ${dep} could 
not be found.")
-      set(${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
-      return()
-    endif()
-    set(cmake_fd_version)
-    set(cmake_fd_required_arg)
-    set(cmake_fd_quiet_arg)
-    set(cmake_fd_exact_arg)
+  get_property(cmake_fd_alreadyTransitive GLOBAL PROPERTY
+    _CMAKE_${dep}_TRANSITIVE_DEPENDENCY
+  )
+
+  find_package(${dep} ${cmake_fd_version}
+    ${cmake_fd_exact_arg}
+    ${cmake_fd_quiet_arg}
+    ${cmake_fd_required_arg}
+    ${cmake_fd_COMPONENTS}
+  )
+
+  if(NOT DEFINED cmake_fd_alreadyTransitive OR cmake_fd_alreadyTransitive)
+    set_property(GLOBAL PROPERTY _CMAKE_${dep}_TRANSITIVE_DEPENDENCY TRUE)
+  endif()
+
+  if (NOT ${dep}_FOUND)
+    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE 
"${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency ${dep} could 
not be found.")
+    set(${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
+    return()
   endif()
+
+  set(cmake_fd_version)
+  set(cmake_fd_required_arg)
+  set(cmake_fd_quiet_arg)
+  set(cmake_fd_exact_arg)
+  set(cmake_fd_COMPONENTS)
+  set(options)
+  set(oneValueArgs)
+  set(multiValueArgs)
+  set(cmake_fd_option_names)
+  set(cmake_fd_args)
 endmacro()
-- 
2.7.2

-- 

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more 
information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake-developers

Reply via email to