Correct usage of find_package_handle_standard_args full signature to check REQUIRED VARS

Hi,

I’m confused by the correct usage of find_package_handle_standard_args:

Lets say I want to write a simple Findmypackage.cmake to find a certain lib that is usually placed in e.g. /opt/mypackage, and has include dirs /opt/mypackage/include and lib dirs /opt/mypackage/lib.

I want to generate the variables
${mypackage_INCLUDE_DIRS} ${mypackage_LIBRARY_DIRS}
and the target mypackage::mypackage

include(FindPackageHandleStandardArgs)

if(EXISTS "/opt/mypackage/include" AND EXISTS "/opt/mypackage/lib")
  set(mypackage_INCLUDE_DIRS "/opt/mypackage/include")
  set(mypackage_LIBRARY_DIRS "/opt/mypackage/lib")
  if(EXISTS "/opt/mypackage/lib/libmypackage.so")
    set(mypackage_FOUND TRUE)
    if (NOT TARGET mypackage::mypackage)
      # maybe find_library would be better?
      add_library(mypackage::mypackage SHARED IMPORTED)
      set_target_properties(mypackage::mypackage PROPERTIES 
        INTERFACE_INCLUDE_DIRECTORIES "${mypackage_INCLUDE_DIRS}"
        IMPORTED_LOCATION "${mypackage_LIBRARY_DIRS}/libmypackage.so"
      )
    endif()
  endif()
endif()

find_package_handle_standard_args(
  mypackage
  FOUND_VAR mypackage_FOUND
  REQUIRED_VARS mypackage_INCLUDE_DIRS mypackage_LIBRARY_DIRS
  VERSION_VAR mypackage_VERSION
)

mark_as_advanced(mypackage_INCLUDE_DIRS mypackage_LIBRARY_DIRS)

My questions are:

  1. Is this a correct looking Findmypackage.cmake? It’s quite hard to find best practices other than the example given in https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html
  2. I read that REQUIRED_VARS should be cache (possibly input?) variables (usually mypackage_LIBRARY_DIR and mypackage_INCLUDE_DIR) - I interpret this as giving a method for advanced users to set/hint the locations of these folders directly by setting the variable before calling Findmypackage.cmake, and then in the Findmypackage.cmake I can check whether these folders exist and forward these to mypackage_LIBRARY_DIRS and mypackage_INCLUDE_DIRS. Is this interpretation correct?
  3. However, I don’t expect most users to actually have to set those variables, and I want to only consider mypackage to be found if mypackage_INCLUDE_DIRS is set - how can I do this without putting mypackage_INCLUDE_DIRS in the REQUIRED_VARS of find_package_handle_standard_args?

Thank you!

Something like this is how I’d do it (with comments on why I changed them):

find_library(mypackage_LIBRARY # Search for specific items; plural "output" variables can be made later
  NAMES mypackage
  PATHS
    /opt/mypackage # Typical install location
  DOC "mypackage library location") # Use `find_library` to find libraries instead of manually searching
mark_as_advanced(mypackage_LIBRARY) # Mark your cache variables as advanced

find_path(mypackage_INCLUDE_DIR
  NAMES
    mypackage/some/key/header.h # use the name as `#include` uses
  PATHS
    /opt/mypackage
  SUFFIXES
    include
  DOC "mypackage include directory")
mark_as_advanced(mypackage_INCLUDE_DIR)

include(FindPackageHandleStandardArgs) # Include right before usage
find_package_handle_standard_args(
  mypackage
  FOUND_VAR mypackage_FOUND
  REQUIRED_VARS mypackage_INCLUDE_DIR mypackage_LIBRARY_DIR
  # VERSION_VAR mypackage_VERSION # use this if you can extract it from the headers or something, but nothing writes to it above, so it's not meaningful
)

if (mypackage_FOUND) # only set outputs if the package is found
  set(mypackage_INCLUDE_DIRS "${mypackage_INCLUDE_DIR}") # not required IMO
  set(mypackage_LIBRARIES "${mypackage_LIBRARY}") # same; the directory doesn't matter that much
  if (NOT TARGET mypackage::mypackage)
      add_library(mypackage::mypackage SHARED IMPORTED) # might want to detect the shared/static
      set_target_properties(mypackage::mypackage PROPERTIES 
        INTERFACE_INCLUDE_DIRECTORIES "${mypackage_INCLUDE_DIR}"
        IMPORTED_LOCATION "${mypackage_LIBRARY}"
      )
  endif ()
endif ()
2 Likes