Draft: Resolve "Add dedicated storage backend for NVM"

  • Add PMDK dependency to CMake
  • Add PMDK dependencies to docker images
  • Update dl_dep.sh and compile_dep.sh scripts with PMDK library
  • Enable runtime configuration of data backends
  • Design common interface for data backends
  • Implement PMDK backend
    • Add abstraction to manage PMDK pools
    • Implement pmdk::ChunkStorage class
  • Enable integration tests to run also for PMDK backends

Closes #124

Edited by Alberto Miranda

Merge request reports

Loading

CMake/FindPMDK.cmake

0 → 100644
+1055 −0
Changes for CMake/FindPMDK.cmake: 1055 added lines, 0 removed lines.
Original line number Diff line number Diff line
################################################################################
# Copyright 2018-2021, Barcelona Supercomputing Center (BSC), Spain            #
# Copyright 2015-2021, Johannes Gutenberg Universitaet Mainz, Germany          #
#                                                                              #
# This software was partially supported by the                                 #
# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).    #
#                                                                              #
# This software was partially supported by the                                 #
# ADA-FS project under the SPPEXA project funded by the DFG.                   #
#                                                                              #
# This file is part of GekkoFS.                                                #
#                                                                              #
# GekkoFS is free software: you can redistribute it and/or modify              #
# it under the terms of the GNU General Public License as published by         #
# the Free Software Foundation, either version 3 of the License, or            #
# (at your option) any later version.                                          #
#                                                                              #
# GekkoFS is distributed in the hope that it will be useful,                   #
# but WITHOUT ANY WARRANTY; without even the implied warranty of               #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
# GNU General Public License for more details.                                 #
#                                                                              #
# You should have received a copy of the GNU General Public License            #
# along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.            #
#                                                                              #
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

#[=======================================================================[.rst:

FindPMDK
---------

Find PMDK include directories and libraries.

Introduction
^^^^^^^^^^^^

This module can be used by invoking :command:`find_package` with the form::

  find_package(PMDK
    [version] [EXACT]       # Minimum or EXACT version e.g. 1.9.1
    [REQUIRED]              # Fail with error if PMDK is not found
    [COMPONENTS <libs>...]  # PMDK libraries by their canonical name
                            # e.g. "pmemobj" for "libpmemobj"
    )

This module finds headers and requested component libraries and reports the
results in the following variables::

  PMDK_FOUND               - True if headers and requested libraries were found
  PMDK_INCLUDE_DIRS        - PMDK include directories
  PMDK_LIBRARY_DIRS        - Link directories for PMDK libraries
  PMDK_LIBRARIES           - PMDK component libraries to be linked

  PMDK_<C>_FOUND           - True if component <C> was found
                             (<C> is upper-case, e.g. PMDK_PMEM_FOUND)
  PMDK_<C>_LIBRARY         - Libraries to link for component <C>
                             (may include target_link_libraries debug/optimized
                             keywords)

  PMDK_VERSION_STRING      - PMDK version number in x.y.z format
  PMDK_VERSION             - Same as PMDK_VERSION_STRING
  PMDK_VERSION_MAJOR       - PMDK major version number (e.g. 1 in 1.9.1-rc1)
  PMDK_VERSION_MINOR       - PMDK minor version number (e.g. 9 in 1.9.1-rc1)
  PMDK_VERSION_PATCH       - PMDK subminor version number (e.g. 1 in 1.9.1-rc1)
  PMDK_VERSION_TWEAK       - PMDK tweak version number (e.g. rc1 in 1.9.1-rc1)
  PMDK_VERSION_COUNT       - Amount of version components (e.g. 2 in 1.9)

.. note::
  Since PMDK does not yet provide version information in the headers but does
  install ``pkg-config`` description files, version matching is done using
  CMake's ``PkgConfig`` module. Thus, ``pkg-config`` needs to be available in
  the system and the ``PKG_CONFIG_PATH`` environment variable may need to be
  appropriately set (see ``pkg-config``'s man page for more details).

This module reads hints about search locations from variables::

  PMDK_ROOT                - Preferred installation prefix
   (or PMDKROOT)
  PMDK_INCLUDEDIR          - Preferred include directory e.g. <prefix>/include
  PMDK_LIBRARYDIR          - Preferred library directory e.g. <prefix>/lib
  PMDK_NO_SYSTEM_PATHS     - Set to ON to disable searching in locations not
                             specified by these hint variables. Default is OFF.
  PMDK_ADDITIONAL_VERSIONS - List of PMDK versions not known to this module.

and saves search results persistently in CMake cache entries::

  PMDK_INCLUDE_DIR         - Directory containing PMDK headers
  PMDK_LIBRARY_DIR_RELEASE - Directory containing release PMDK libraries
  PMDK_LIBRARY_DIR_DEBUG   - Directory containing debug PMDK libraries
  PMDK_<C>_LIBRARY_DEBUG   - Component <C> library debug variant
  PMDK_<C>_LIBRARY_RELEASE - Component <C> library release variant

Users may set these hints or results as ``CACHE`` entries.  Projects should
not read these entries directly but instead use the above result variables.
One may specify these as environment variables if they are not specified as
CMake variables or cache entries.


Imported Targets
^^^^^^^^^^^^^^^^

This module also defines the following :prop_tgt:`IMPORTED` targets, if found::

  PMDK::PMDK               - Target for the complete PMDK library
  PMDK::<C>                - Target for specific component dependencies (shared
                             or static libraries). Note that <C> is lower-case

Implicit dependencies such as ``PMDK::libpmemobj`` requiring ``PMDK::pmem``
will be automatically detected and satisfied, even if ``pmem`` is not
specified when using :command:`find_package` and if ``PMDK::pmem`` is not
added to :command:`target_link_libraries`.

It is important to note that the imported targets behave differently
than variables created by this module: multiple calls to
:command:`find_package(PMDK)` in the same directory or sub-directories with
different options (e.g. static or shared) will not override the
values of the targets created by the first call.

Lookup Details
^^^^^^^^^^^^^^

This module first searches for the ``PMDK`` header files using the above
hint variables (excluding ``PMDK_LIBRARYDIR``) and saves the result in
``PMDK_INCLUDE_DIR``.  Then it searches for requested component libraries
using the above hints (excluding ``PMDK_INCLUDEDIR`` and
``PMDK_ADDITIONAL_VERSIONS``) and "lib" directories near ``PMDK_INCLUDE_DIR``.
It saves the library directories in ``PMDK_LIBRARY_DIR_DEBUG`` and
``PMDK_LIBRARY_DIR_RELEASE`` and individual library locations in
``PMDK_<C>_LIBRARY_DEBUG`` and ``PMDK_<C>_LIBRARY_RELEASE``.
When one changes settings used by previous searches in the same build
tree (excluding environment variables) this module discards previous
search results affected by the changes and searches again.

PMDK libraries come in RELEASE and DEBUG variants which (if installed) are
stored in separate locations. Users or projects may tell this module which
variant to find by setting variables::

  PMDK_USE_DEBUG_LIBS      - Set to ON or OFF to specify whether to search
                             and use the debug libraries.  Default is ON.
  PMDK_USE_RELEASE_LIBS    - Set to ON or OFF to specify whether to search
                             and use the release libraries.  Default is ON.
  PMDK_USE_STATIC_LIBS     - Set to ON to force the use of the static
                             libraries.  Default is OFF.

Other variables one may set to control this module are::

  PMDK_DEBUG               - Set to ON to enable debug output from FindPMDK.
                             Please enable this before filing any bug report.
  PMDK_LIBRARY_DIR         - Default value for PMDK_LIBRARY_DIR_RELEASE and
                             PMDK_LIBRARY_DIR_DEBUG.

Examples
^^^^^^^^

Example to find PMDK headers only:

.. code-block:: cmake

  find_package(PMDK 1.36.0)
  if(PMDK_FOUND)
    include_directories(${PMDK_INCLUDE_DIRS})
    add_executable(foo foo.cc)
  endif()

Example to find PMDK libraries and use imported targets:

.. code-block:: cmake

  find_package(PMDK 1.9.1 REQUIRED
                 COMPONENTS
                   pmemobj pmempool rpmem)
  add_executable(foo foo.cc)
  target_link_libraries(foo PMDK::pmemobj PMDK::pmempool
                            PMDK::rpmem)

Example to find PMDK headers and some *static* (release only) libraries:

.. code-block:: cmake

  set(PMDK_USE_STATIC_LIBS        ON)  # only find static libs
  set(PMDK_USE_DEBUG_LIBS         OFF) # ignore debug libs and
  set(PMDK_USE_RELEASE_LIBS       ON)  # only find release libs
  find_package(PMDK 1.9.1
                 COMPONENTS
                   pmemobj pmempool rpmem ...)
  if(PMDK_FOUND)
    include_directories(${PMDK_INCLUDE_DIRS})
    add_executable(foo foo.cc)
    target_link_libraries(foo ${PMDK_LIBRARIES})
  endif()

#]=======================================================================]

include(CMakePrintHelpers)

# The FPHSA helper provides standard way of reporting final search results to
# the user including the version and component checks.
include(FindPackageHandleStandardArgs)



#-------------------------------------------------------------------------------
#  FindPMDK functions & macros
#

#
# Print debug text if PMDK_DEBUG is set.
# Call example:
# _PMDK_DEBUG_PRINT("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "debug message")
#
function(_PMDK_DEBUG_PRINT file line text)
  if(PMDK_DEBUG)
    message(STATUS "[ ${file}:${line} ] ${text}")
  endif()
endfunction()


#
# _PMDK_DEBUG_PRINT_VAR(file line variable_name [ENVIRONMENT]
#                        [SOURCE "short explanation of origin of var value"])
#
#   ENVIRONMENT - look up environment variable instead of CMake variable
#
# Print variable name and its value if PMDK_DEBUG is set.
# Call example:
# _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" PMDK_ROOT)
#
function(_PMDK_DEBUG_PRINT_VAR file line name)
  if(PMDK_DEBUG)
    cmake_parse_arguments(_args "ENVIRONMENT" "SOURCE" "" ${ARGN})

    unset(source)
    if(_args_SOURCE)
      set(source " (${_args_SOURCE})")
    endif()

    if(_args_ENVIRONMENT)
      if(DEFINED ENV{${name}})
        set(value "\"$ENV{${name}}\"")
      else()
        set(value "<unset>")
      endif()
      set(_name "ENV{${name}}")
    else()
      if(DEFINED "${name}")
        set(value "\"${${name}}\"")
      else()
        set(value "<unset>")
      endif()
      set(_name "${name}")
    endif()

    _PMDK_DEBUG_PRINT("${file}" "${line}" "${_name} = ${value}${source}")
  endif()
endfunction()


#
# Get component headers.  This is the primary header (or headers) for
# a given component, and is used to check that the headers are present
# as well as the library itself as an extra sanity check of the build
# environment.
#
# component - the component to check
# _hdrs
#
function(_PMDK_COMPONENT_HEADERS component _hdrs)

  # Note: new PMDK components will require adding here.  The header
  # must be present in all versions of PMDK providing a library.
  set(_PMDK_PMEM_HEADERS     "libpmem.h")
  set(_PMDK_PMEMBLK_HEADERS  "libpmemblk.h")
  set(_PMDK_PMEMLOG_HEADERS  "libpmemlog.h")
  set(_PMDK_PMEMOBJ_HEADERS  "libpmemobj.h")
  set(_PMDK_PMEMPOOL_HEADERS "libpmempool.h")
  set(_PMDK_RPMEM_HEADERS    "librpmem.h")
  set(_PMDK_PMEM2_HEADERS    "libpmem2.h")

  string(TOUPPER ${component} uppercomponent)
  set(${_hdrs} ${_PMDK_${uppercomponent}_HEADERS} PARENT_SCOPE)

  string(REGEX REPLACE ";" " " _pmdk_HDRS_STRING "${_PMDK_${uppercomponent}_HEADERS}")
  if (NOT _pmdk_HDRS_STRING)
    set(_pmdk_HDRS_STRING "(none)")
  endif()

  _PMDK_DEBUG_PRINT("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}"
    "Headers for PMDK::${component}: ${_pmdk_HDRS_STRING}")
endfunction()


#
# Get component dependencies.  Requires the dependencies to have been
# defined for the PMDK release version.
#
# component - the component to check
# _ret - list of library dependencies
#
function(_PMDK_COMPONENT_DEPENDENCIES component _ret)

  set(_PMDK_IMPORTED_TARGETS TRUE)
  if(PMDK_VERSION_STRING)
    if(PMDK_VERSION_STRING VERSION_LESS 1.9.1)
      message(WARNING "Imported targets and dependency information not "
        "available for PMDK version ${PMDK_VERSION_STRING} "
        "(all versions older than 1.9.1)")
    else()
      set(_PMDK_PMEMBLK_DEPENDENCIES pmem)
      set(_PMDK_PMEMLOG_DEPENDENCIES pmem)
      set(_PMDK_PMEMOBJ_DEPENDENCIES pmem)
      set(_PMDK_PMEMPOOL_DEPENDENCIES pmem)
      set(_PMDK_RPMEM_DEPENDENCIES pmem)
      if(PMDK_VERSION_STRING VERSION_GREATER_EQUAL 1.10.0)
        message(WARNING "New PMDK version ${PMDK_VERSION_STRING} may have "
          "incorrect or missing dependencies and imported "
          "targets")
      endif()
    endif()
  endif()

  string(TOUPPER ${component} uppercomponent)
  set(${_ret} ${_PMDK_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
  set(_PMDK_IMPORTED_TARGETS ${_PMDK_IMPORTED_TARGETS} PARENT_SCOPE)

  string(REGEX REPLACE ";" " " _pmdk_DEPS_STRING "${_PMDK_${uppercomponent}_DEPENDENCIES}")
  if (NOT _pmdk_DEPS_STRING)
    set(_pmdk_DEPS_STRING "(none)")
  endif()

  message(STATUS "Dependencies for PMDK::${component}: ${_pmdk_DEPS_STRING}")

  _PMDK_DEBUG_PRINT("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}"
    "Dependencies for PMDK::${component}: ${_pmdk_DEPS_STRING}")

endfunction()


#
# Determine if any missing dependencies require adding to the component list.
#
# Sets _PMDK_${COMPONENT}_DEPENDENCIES for each required component,
# plus _PMDK_IMPORTED_TARGETS (TRUE if imported targets should be
# defined; FALSE if dependency information is unavailable).
#
# componentvar - the component list variable name
# extravar - the indirect dependency list variable name
#
#
function(_PMDK_MISSING_DEPENDENCIES componentvar extravar)
  # _pmdk_unprocessed_components - list of components requiring processing
  # _pmdk_processed_components - components already processed (or currently being processed)
  # _pmdk_new_components - new components discovered for future processing
  #
  list(APPEND _pmdk_unprocessed_components ${${componentvar}})

  while(_pmdk_unprocessed_components)
    list(APPEND _pmdk_processed_components ${_pmdk_unprocessed_components})
    foreach(component ${_pmdk_unprocessed_components})
      string(TOUPPER ${component} uppercomponent)
      set(${_ret} ${_PMDK_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
      _PMDK_COMPONENT_DEPENDENCIES("${component}" _PMDK_${uppercomponent}_DEPENDENCIES)
      set(_PMDK_${uppercomponent}_DEPENDENCIES ${_PMDK_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
      set(_PMDK_IMPORTED_TARGETS ${_PMDK_IMPORTED_TARGETS} PARENT_SCOPE)
      foreach(componentdep ${_PMDK_${uppercomponent}_DEPENDENCIES})
        if (NOT ("${componentdep}" IN_LIST _pmdk_processed_components OR "${componentdep}" IN_LIST _pmdk_new_components))
          list(APPEND _pmdk_new_components ${componentdep})
        endif()
      endforeach()
    endforeach()
    set(_pmdk_unprocessed_components ${_pmdk_new_components})
    unset(_pmdk_new_components)
  endwhile()
  set(_pmdk_extra_components ${_pmdk_processed_components})
  if(_pmdk_extra_components AND ${componentvar})
    list(REMOVE_ITEM _pmdk_extra_components ${${componentvar}})
  endif()
  set(${componentvar} ${_pmdk_processed_components} PARENT_SCOPE)
  set(${extravar} ${_pmdk_extra_components} PARENT_SCOPE)
endfunction()


#
# Find the given library (var).
# Use 'build_type' to support different lib paths for RELEASE or DEBUG builds
#
macro(_PMDK_FIND_LIBRARY var build_type)

  find_library(${var} ${ARGN})

  if(${var})
    # If this is the first library found then save PMDK_LIBRARY_DIR_[RELEASE,DEBUG].
    if(NOT PMDK_LIBRARY_DIR_${build_type})
      get_filename_component(_dir "${${var}}" PATH)
      set(PMDK_LIBRARY_DIR_${build_type} "${_dir}" CACHE PATH "PMDK library directory ${build_type}" FORCE)
    endif()
  endif()

  # If PMDK_LIBRARY_DIR_[RELEASE,DEBUG] is known then search only there.
  if(PMDK_LIBRARY_DIR_${build_type})
    set(_PMDK_LIBRARY_SEARCH_DIRS_${build_type} ${PMDK_LIBRARY_DIR_${build_type}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
    _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}"
      "PMDK_LIBRARY_DIR_${build_type}")
    #    _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}"
    #                          "_PMDK_LIBRARY_SEARCH_DIRS_${build_type}")
  endif()
endmacro()

# Detect changes in used variables.
# Compares the current variable value with the last one.
# In short form:
# v != v_LAST                      -> CHANGED = 1
# v is defined, v_LAST not         -> CHANGED = 1
# v is not defined, but v_LAST is  -> CHANGED = 1
# otherwise                        -> CHANGED = 0
# CHANGED is returned in variable named ${changed_var}
macro(_PMDK_CHANGE_DETECT changed_var)
  set(${changed_var} 0)
  foreach(v ${ARGN})
    if(DEFINED _PMDK_COMPONENTS_SEARCHED)
      if(${v})
        if(_${v}_LAST)
          string(COMPARE NOTEQUAL "${${v}}" "${_${v}_LAST}" _${v}_CHANGED)
        else()
          set(_${v}_CHANGED 1)
        endif()
      elseif(_${v}_LAST)
        set(_${v}_CHANGED 1)
      endif()
      if(_${v}_CHANGED)
        set(${changed_var} 1)
      endif()
    else()
      set(_${v}_CHANGED 0)
    endif()
  endforeach()
endmacro()

#
# Check the existence of the libraries.
#
macro(_PMDK_ADJUST_LIB_VARS basename)
  if(PMDK_INCLUDE_DIR)
    if(PMDK_${basename}_LIBRARY_DEBUG AND PMDK_${basename}_LIBRARY_RELEASE)
      # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for
      # single-config generators, set optimized and debug libraries
      get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
      if(_isMultiConfig OR CMAKE_BUILD_TYPE)
        set(PMDK_${basename}_LIBRARY optimized ${PMDK_${basename}_LIBRARY_RELEASE} debug ${PMDK_${basename}_LIBRARY_DEBUG})
      else()
        # For single-config generators where CMAKE_BUILD_TYPE has no value,
        # just use the release libraries
        set(PMDK_${basename}_LIBRARY ${PMDK_${basename}_LIBRARY_RELEASE} )
      endif()
      # FIXME: This probably should be set for both cases
      set(PMDK_${basename}_LIBRARIES optimized ${PMDK_${basename}_LIBRARY_RELEASE} debug ${PMDK_${basename}_LIBRARY_DEBUG})
    endif()

    # if only the release version was found, set the debug variable also to the release version
    if(PMDK_${basename}_LIBRARY_RELEASE AND NOT PMDK_${basename}_LIBRARY_DEBUG)
      set(PMDK_${basename}_LIBRARY_DEBUG ${PMDK_${basename}_LIBRARY_RELEASE})
      set(PMDK_${basename}_LIBRARY       ${PMDK_${basename}_LIBRARY_RELEASE})
      set(PMDK_${basename}_LIBRARIES     ${PMDK_${basename}_LIBRARY_RELEASE})
    endif()

    # if only the debug version was found, set the release variable also to the debug version
    if(PMDK_${basename}_LIBRARY_DEBUG AND NOT PMDK_${basename}_LIBRARY_RELEASE)
      set(PMDK_${basename}_LIBRARY_RELEASE ${PMDK_${basename}_LIBRARY_DEBUG})
      set(PMDK_${basename}_LIBRARY         ${PMDK_${basename}_LIBRARY_DEBUG})
      set(PMDK_${basename}_LIBRARIES       ${PMDK_${basename}_LIBRARY_DEBUG})
    endif()

    # If the debug & release library ends up being the same, omit the keywords
    if("${PMDK_${basename}_LIBRARY_RELEASE}" STREQUAL "${PMDK_${basename}_LIBRARY_DEBUG}")
      set(PMDK_${basename}_LIBRARY   ${PMDK_${basename}_LIBRARY_RELEASE} )
      set(PMDK_${basename}_LIBRARIES ${PMDK_${basename}_LIBRARY_RELEASE} )
    endif()

    if(PMDK_${basename}_LIBRARY AND PMDK_${basename}_HEADER)
      set(PMDK_${basename}_FOUND ON)
    endif()
  endif()

  # Make variables changeable to the advanced user
  mark_as_advanced(
    PMDK_${basename}_LIBRARY_RELEASE
    PMDK_${basename}_LIBRARY_DEBUG
  )
endmacro()

#
# End functions/macros
#
#-------------------------------------------------------------------------------


#-------------------------------------------------------------------------------
# main.
#-------------------------------------------------------------------------------

# If the user sets PMDK_LIBRARY_DIR, use it as the default for both
# configurations.
if(NOT PMDK_LIBRARY_DIR_RELEASE AND PMDK_LIBRARY_DIR)
  set(PMDK_LIBRARY_DIR_RELEASE "${PMDK_LIBRARY_DIR}")
endif()

if(NOT PMDK_LIBRARY_DIR_DEBUG AND PMDK_LIBRARY_DIR)
  set(PMDK_LIBRARY_DIR_DEBUG "${PMDK_LIBRARY_DIR}")
endif()

if(NOT DEFINED PMDK_USE_DEBUG_LIBS)
  set(PMDK_USE_DEBUG_LIBS TRUE)
endif()

if(NOT DEFINED PMDK_USE_RELEASE_LIBS)
  set(PMDK_USE_RELEASE_LIBS TRUE)
endif()

# Check the version of PMDK against the requested version.
if(PMDK_FIND_VERSION AND NOT PMDK_FIND_VERSION_MINOR)
  message(SEND_ERROR "When requesting a specific version of PMDK, you must "
    "provide at least the major and minor version numbers, "
    "e.g., 1.9")
endif()

if(PMDK_FIND_VERSION_EXACT)
  # The version may appear in a directory with or without the patch level,
  # even when the patch level is non-zero
  set(_PMDK_TESTS_VERSIONS
    "${PMDK_FIND_VERSION_MAJOR}.${PMDK_FIND_VERSION_MINOR}.${PMDK_FIND_VERSION_PATCH}"
    "${PMDK_FIND_VERSION_MAJOR}.${PMDK_FIND_VERSION_MINOR}")
else()
  # The user has not requested an exact version. Among known versions, find
  # those that are acceptable to the user request.
  #
  # Note: When adding a new PMDK release, also update the dependency
  # information in _PMDK_COMPONENT_DEPENDENCIES and
  # _PMDK_COMPONENT_HEADERS.  See the instructions at the top of
  # _PMDK_COMPONENT_DEPENDENCIES.
  set(_PMDK_KNOWN_VERSIONS ${PMDK_ADDITIONAL_VERSIONS}
    "1.9.1" "1.9" "1.9-rc2" "1.9-rc1" "1.8.1" "1.8" "1.8-rc2" "1.8-rc1" "1.7.1"
    "1.7" "1.7-rc2" "1.7-rc1" "1.6.1" "1.6.1-rc1" "1.6" "1.6-rc3" "1.6-rc2"
    "1.6-rc1" "1.5.2" "1.5.2-rc1" "1.5.1" "1.5.1-rc3" "1.5.1-rc2" "1.5.1-rc1"
    "1.5" "1.5-rc2" "1.5-rc1" "1.4.3" "1.4.3-rc1" "1.4.2" "1.4.2-rc1" "1.4.1"
    "1.4.1-rc4" "1.4.1-rc3" "1.4.1-rc2" "1.4.1-rc1" "1.4" "1.4-rc4" "1.4-rc3"
    "1.4-rc2" "1.4-rc1" "1.3.3" "1.3.3-rc1" "1.3+b2" "1.3+b1" "1.3" "1.3-rc2"
    "1.3-rc1" "1.2.4" "1.2.4-rc1" "1.2+wtp1" "1.2+b3" "1.2+b2" "1.2+b1" "1.2"
    "1.2-rc3" "1.2-rc2" "1.2-rc1" "1.1" "1.1-rc3" "1.1-rc2" "1.1-rc1" "1.0+b2"
    "1.0+b1" "1.0" "1.0-rc3" "1.0-rc2" "1.0-rc1" "0.4+b3" "0.4+b2" "0.4+b1"
    "0.4" "0.4-rc2" "0.4-rc1" "0.3+b3" "0.3+b2" "0.3+b1" "0.3" "0.3-rc2"
    "0.3-rc1" "0.2+b4" "0.2+b3" "0.2+b2" "0.2+b1" "0.2" "0.2-rc3" "0.2-rc2"
    "0.2-rc1" "0.1+b16" "0.1+b15" "0.1+b14" "0.1+b13" "0.1+b12" "0.1+b11"
    "0.1+b10" "0.1+b9" "0.1+b8" "0.1+b7" "0.1+b6" "0.1+b5" "0.1+b4" "0.1+b3"
    "0.1+b2" "0.1+b1" "0.1"
    )

  set(_PMDK_TESTS_VERSIONS)
  if(PMDK_FIND_VERSION)
    set(_PMDK_FIND_VERSION_SHORT "${PMDK_FIND_VERSION_MAJOR}.${PMDK_FIND_VERSION_MINOR}")

    # select acceptable versions
    foreach(version ${_PMDK_KNOWN_VERSIONS})
      if(NOT "${version}" VERSION_LESS "${PMDK_FIND_VERSION}")
        # This version is high enough
        list(APPEND _PMDK_TESTS_VERSIONS "${version}")
      elseif("${version}.99" VERSION_EQUAL "${_PMDK_FIND_VERSION_SHORT}.99")
        # This version is a short-form for the requested version with
        # the patch level dropped.
        list(APPEND _PMDK_TESTS_VERSIONS "${version}")
      endif()
    endforeach()
  else()
    # Any version is acceptable
    set(_PMDK_TESTS_VERSIONS "${_PMDK_KNOWN_VERSIONS}")
  endif()
endif()

_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_TEST_VERSIONS")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_USE_STATIC_LIBS")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_ADDITIONAL_VERSIONS")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_NO_SYSTEM_PATHS")

# Collect environment variable inputs as hints.
foreach(v PMDKROOT PMDK_ROOT PMDK_INCLUDEDIR PMDK_LIBRARYDIR)
  set(_env $ENV{${v}})
  if(_env)
    file(TO_CMAKE_PATH "${_env}" _ENV_${v})
  else()
    set(_ENV_${v} "")
  endif()
endforeach()

if(NOT _ENV_PMDK_ROOT AND _ENV_PMDKROOT)
  set(_ENV_PMDK_ROOT "${_ENV_PMDKROOT}")
endif()

# Collect inputs and cached results.
if(NOT PMDK_ROOT AND PMDKROOT)
  set(PMDK_ROOT "${PMDKROOT}")
endif()

set(_PMDK_VARS_DIR
  PMDK_ROOT
  PMDK_NO_SYSTEM_PATHS
  )

_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_ROOT")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_ROOT" ENVIRONMENT)
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_INCLUDEDIR")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_INCLUDEDIR" ENVIRONMENT)
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_LIBRARYDIR")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_LIBRARYDIR" ENVIRONMENT)


# ------------------------------------------------------------------------
#  Search for PMDK include DIR
# ------------------------------------------------------------------------

set(_PMDK_VARS_INC PMDK_INCLUDEDIR PMDK_INCLUDE_DIR PMDK_ADDITIONAL_VERSIONS)
_PMDK_CHANGE_DETECT(_PMDK_CHANGE_INCDIR ${_PMDK_VARS_DIR} ${_PMDK_VARS_INC})

# Clear PMDK_INCLUDE_DIR if it did not change but other input affecting the
# location did.  We will find a new one based on the new inputs.
if(_PMDK_CHANGE_INCDIR AND NOT _PMDK_INCLUDE_DIR_CHANGED)
  unset(PMDK_INCLUDE_DIR CACHE)
endif()

if(NOT PMDK_INCLUDE_DIR)
  set(_PMDK_INCLUDE_SEARCH_DIRS "")

  if(PMDK_INCLUDEDIR)
    list(APPEND _PMDK_INCLUDE_SEARCH_DIRS ${PMDK_INCLUDEDIR})
  elseif(_ENV_PMDK_INCLUDEDIR)
    list(APPEND _PMDK_INCLUDE_SEARCH_DIRS ${_ENV_PMDK_INCLUDEDIR})
  endif()

  if(PMDK_ROOT)
    list(APPEND _PMDK_INCLUDE_SEARCH_DIRS ${PMDK_ROOT}/include ${PMDK_ROOT})
  elseif(_ENV_PMDK_ROOT)
    list(APPEND _PMDK_INCLUDE_SEARCH_DIRS ${_ENV_PMDK_ROOT}/include ${_ENV_PMDK_ROOT})
  endif()

  if(PMDK_NO_SYSTEM_PATHS)
    list(APPEND _PMDK_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
  endif()

  _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_INCLUDE_SEARCH_DIRS")

  # Look for a standard PMDK header file
  find_path(PMDK_INCLUDE_DIR
    NAMES libpmem.h
    HINTS ${_PMDK_INCLUDE_SEARCH_DIRS}
    )
endif()

_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_INCLUDE_DIR")


# ------------------------------------------------------------------------
#  Extract version information using pkg-config
#
# TODO: This will fail if pkg-config is not available, but version
#       information is unfortunately only contained in the installed .pc
#       files. In the future it would be more portable to extract version
#       information from a installed header, if the developers end up add
#       providing this information.
# ------------------------------------------------------------------------

find_package(PkgConfig)

if(PMDK_FIND_VERSION)

  if(NOT PKG_CONFIG_FOUND)
    message(FATAL_ERROR "ERROR: pkg-config is required to determine PMDK's "
      "installed version. If pkg-config cannot be installed "
      "and you are certain that PMDK's installed version "
      "is the correct one, you can disable this check by "
      "removing the version number in the call to "
      "find_package(PMDK ...)")
  endif()

  pkg_get_variable(PMDK_VERSION_STRING libpmem version)

  if(NOT PMDK_VERSION_STRING)
    message(FATAL_ERROR "ERROR: Failed to determine PMDK version.")
  endif()

  set(PMDK_VERSION ${PMDK_VERSION_STRING})

  string(REGEX MATCH "([0-9]+).([0-9]+)(.([0-9]+))?([+-](((rc)|b)[0-9]+))?" _ ${PMDK_VERSION_STRING})
  set(PMDK_VERSION_MAJOR ${CMAKE_MATCH_1})
  set(PMDK_VERSION_MINOR ${CMAKE_MATCH_2})
  set(PMDK_VERSION_PATCH ${CMAKE_MATCH_4})
  set(PMDK_VERSION_TWEAK ${CMAKE_MATCH_6})

  set(PMDK_VERSION_COUNT 2)

  if(PMDK_VERSION_PATCH)
    math(EXPR PMDK_VERSION_COUNT "${PMDK_VERSION_COUNT} + 1")
  endif()

  if(PMDK_VERSION_TWEAK)
    math(EXPR PMDK_VERSION_COUNT "${PMDK_VERSION_COUNT} + 1")
  endif()

endif()

_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION_STRING")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION_MAJOR")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION_MINOR")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION_PATCH")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION_TWEAK")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_VERSION_COUNT")


# ------------------------------------------------------------------------
#  Begin finding PMDK libraries
# ------------------------------------------------------------------------

set(_PMDK_VARS_NAME
  PMDK_USE_STATIC_LIBS
  )
_PMDK_CHANGE_DETECT(_PMDK_CHANGE_LIBNAME ${_PMDK_VARS_NAME})

set(_PMDK_VARS_LIB "")
foreach(c DEBUG RELEASE)
  set(_PMDK_VARS_LIB_${c} PMDK_LIBRARYDIR PMDK_LIBRARY_DIR_${c})
  list(APPEND _PMDK_VARS_LIB ${_PMDK_VARS_LIB_${c}})
  _PMDK_CHANGE_DETECT(_PMDK_CHANGE_LIBDIR_${c} ${_PMDK_VARS_DIR} ${_PMDK_VARS_LIB_${c}} PMDK_INCLUDE_DIR)

  # Clear PMDK_LIBRARY_DIR_${c} if it did not change but other input affecting
  # the location did.  We will find a new one based on the new inputs.
  if(_PMDK_CHANGE_LIBDIR_${c} AND NOT _PMDK_LIBRARY_DIR_${c}_CHANGED)
    unset(PMDK_LIBRARY_DIR_${c} CACHE)
  endif()

  # If PMDK_LIBRARY_DIR_[RELEASE,DEBUG] is set, prefer its value.
  if(PMDK_LIBRARY_DIR_${c})
    set(_PMDK_LIBRARY_SEARCH_DIRS_${c} ${PMDK_LIBRARY_DIR_${c}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
  else()
    set(_PMDK_LIBRARY_SEARCH_DIRS_${c} "")
    if(PMDK_LIBRARYDIR)
      list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_${c} ${PMDK_LIBRARYDIR})
    elseif(_ENV_PMDK_LIBRARYDIR)
      list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_${c} ${_ENV_PMDK_LIBRARYDIR})
    endif()

    if(PMDK_ROOT)
      list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_${c} ${PMDK_ROOT}/lib ${PMDK_ROOT}/stage/lib)
    elseif(_ENV_PMDK_ROOT)
      list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_${c} ${_ENV_PMDK_ROOT}/lib ${_ENV_PMDK_ROOT}/stage/lib)
    endif()

    list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_${c}
      ${PMDK_INCLUDE_DIR}/lib
      ${PMDK_INCLUDE_DIR}/../lib
      ${PMDK_INCLUDE_DIR}/stage/lib
      )

    if("${c}" STREQUAL "DEBUG")
      # PMDK debug libraries are installed in a `pmdk_debug` subdir
      list(TRANSFORM _PMDK_LIBRARY_SEARCH_DIRS_${c} APPEND "/pmdk_debug")
    endif()

    if(PMDK_NO_SYSTEM_PATHS)
      list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_${c} NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
    endif()

  endif()
endforeach()

_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_LIBRARY_SEARCH_DIRS_RELEASE")
_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_LIBRARY_SEARCH_DIRS_DEBUG")

# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(PMDK_USE_STATIC_LIBS)
  set( _PMDK_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
  if(WIN32)
    list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a)
  else()
    set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
  endif()
endif()

_PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_FIND_COMPONENTS")

# If the user didn't ask for a particular component to be searched,
# assume that they wanted all the components in the library
if(NOT PMDK_FIND_COMPONENTS)
  set(PMDK_FIND_COMPONENTS
    "pmem" "pmemblk" "pmemlog" "pmempool" "rpmem" "pmem2"
    )
endif()

# Additional components may be required via component dependencies.
# Add any missing components to the list.
_PMDK_MISSING_DEPENDENCIES(PMDK_FIND_COMPONENTS _PMDK_EXTRA_FIND_COMPONENTS)

foreach(COMPONENT ${PMDK_FIND_COMPONENTS})

  string(TOUPPER ${COMPONENT} UPPERCOMPONENT)

  set( _pmdk_docstring_release "PMDK ${COMPONENT} library (release)")
  set( _pmdk_docstring_debug   "PMDK ${COMPONENT} library (debug)")

  #
  # Find headers
  #
  _PMDK_COMPONENT_HEADERS("${COMPONENT}" PMDK_${UPPERCOMPONENT}_HEADER_NAME)

  # Look for a standard PMDK header file.
  if(PMDK_${UPPERCOMPONENT}_HEADER_NAME)
    if(EXISTS "${PMDK_INCLUDE_DIR}/${PMDK_${UPPERCOMPONENT}_HEADER_NAME}")
      set(PMDK_${UPPERCOMPONENT}_HEADER ON)
    else()
      set(PMDK_${UPPERCOMPONENT}_HEADER OFF)
    endif()
  else()
    set(PMDK_${UPPERCOMPONENT}_HEADER ON)
    message(WARNING "No header defined for ${COMPONENT}; skipping header check "
      "(note: header-only libraries have no designated component)")
  endif()

  #
  # Find RELEASE libraries
  #
  unset(_PMDK_RELEASE_NAMES)
  foreach(component IN LISTS COMPONENT)
    list(APPEND _PMDK_RELEASE_NAMES
      ${component}
      )
  endforeach()

  _PMDK_DEBUG_PRINT("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}"
    "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: "
    "${_PMDK_RELEASE_NAMES}")

  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_RELEASE_NAMES")
  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_LIBRARY_SEARCH_DIRS_RELEASE")
  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_pmdk_docstring_release")

  # if PMDK_LIBRARY_DIR_RELEASE is not defined,
  # but PMDK_LIBRARY_DIR_DEBUG is, also look there for RELEASE libs
  if(NOT PMDK_LIBRARY_DIR_RELEASE AND PMDK_LIBRARY_DIR_DEBUG)
    list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_RELEASE ${PMDK_LIBRARY_DIR_DEBUG})
  endif()

  # Avoid passing backslashes to _PMDK_FIND_LIBRARY due to macro re-parsing.
  unset(_PMDK_LIBRARY_SEARCH_DIRS_tmp)
  string(REPLACE "\\" "/" _PMDK_LIBRARY_SEARCH_DIRS_tmp "${_PMDK_LIBRARY_SEARCH_DIRS_RELEASE}")

  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_LIBRARY_SEARCH_DIRS_RELEASE")

  if(PMDK_USE_RELEASE_LIBS)
    _PMDK_FIND_LIBRARY(PMDK_${UPPERCOMPONENT}_LIBRARY_RELEASE RELEASE
      NAMES ${_PMDK_RELEASE_NAMES}
      HINTS ${_PMDK_LIBRARY_SEARCH_DIRS_tmp}
      NAMES_PER_DIR
      DOC "${_pmdk_docstring_release}"
      )

    _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_${UPPERCOMPONENT}_LIBRARY_RELEASE")
  endif()

  #
  # Find DEBUG libraries
  #
  unset(_PMDK_DEBUG_NAMES)
  foreach(component IN LISTS COMPONENT)
    list(APPEND _PMDK_DEBUG_NAMES
      ${component}
      )
  endforeach()

  _PMDK_DEBUG_PRINT("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}"
    "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: "
    "${_PMDK_DEBUG_NAMES}")

  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_DEBUG_NAMES")
  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_LIBRARY_SEARCH_DIRS_DEBUG")
  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_pmdk_docstring_debug")

  # if PMDK_LIBRARY_DIR_DEBUG is not defined,
  # but PMDK_LIBRARY_DIR_RELEASE is, also look there for DEBUG libs
  if(NOT PMDK_LIBRARY_DIR_DEBUG AND PMDK_LIBRARY_DIR_RELEASE)
    list(APPEND _PMDK_LIBRARY_SEARCH_DIRS_DEBUG ${PMDK_LIBRARY_DIR_RELEASE})
  endif()


  # Avoid passing backslashes to _PMDK_FIND_LIBRARY due to macro re-parsing.
  unset(_PMDK_LIBRARY_SEARCH_DIRS_tmp)
  string(REPLACE "\\" "/" _PMDK_LIBRARY_SEARCH_DIRS_tmp "${_PMDK_LIBRARY_SEARCH_DIRS_DEBUG}")

  # _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "_PMDK_LIBRARY_SEARCH_DIRS_DEBUG")

  if(PMDK_USE_DEBUG_LIBS)
    _PMDK_FIND_LIBRARY(PMDK_${UPPERCOMPONENT}_LIBRARY_DEBUG DEBUG
      NAMES ${_PMDK_DEBUG_NAMES}
      HINTS ${_PMDK_LIBRARY_SEARCH_DIRS_tmp}
      NAMES_PER_DIR
      NO_DEFAULT_PATH
      DOC "${_pmdk_docstring_debug}"
      )

    _PMDK_DEBUG_PRINT_VAR("${CMAKE_CURRENT_LIST_FILE}" "${CMAKE_CURRENT_LIST_LINE}" "PMDK_${UPPERCOMPONENT}_LIBRARY_DEBUG")
  endif()

  _PMDK_ADJUST_LIB_VARS(${UPPERCOMPONENT})
endforeach()

# Restore the original find library ordering
if(PMDK_USE_STATIC_LIBS)
  set(CMAKE_FIND_LIBRARY_SUFFIXES ${_PMDK_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()

# ------------------------------------------------------------------------
#  End finding PMDK libraries
# ------------------------------------------------------------------------


set(PMDK_INCLUDE_DIRS ${PMDK_INCLUDE_DIR})
set(PMDK_LIBRARY_DIRS)
if(PMDK_LIBRARY_DIR_RELEASE)
  list(APPEND PMDK_LIBRARY_DIRS ${PMDK_LIBRARY_DIR_RELEASE})
endif()
if(PMDK_LIBRARY_DIR_DEBUG)
  list(APPEND PMDK_LIBRARY_DIRS ${PMDK_LIBRARY_DIR_DEBUG})
endif()
if(PMDK_LIBRARY_DIRS)
  list(REMOVE_DUPLICATES PMDK_LIBRARY_DIRS)
endif()


# ------------------------------------------------------------------------
#  Call FPHSA helper, see https://cmake.org/cmake/help/latest/module/FindPackageHandleStandardArgs.html
# ------------------------------------------------------------------------

# Define aliases as needed by the component handler in the FPHSA helper below
foreach(component IN LISTS PMDK_FIND_COMPONENTS)
  string(TOUPPER ${component} uppercomponent)
  if(DEFINED PMDK_${uppercomponent}_FOUND)
    set(PMDK_${component}_FOUND ${PMDK_${uppercomponent}_FOUND})
  endif()
endforeach()

find_package_handle_standard_args(PMDK
  REQUIRED_VARS PMDK_INCLUDE_DIR
  VERSION_VAR PMDK_VERSION_STRING
  HANDLE_COMPONENTS)


# ------------------------------------------------------------------------
#  Add imported targets
# ------------------------------------------------------------------------
if(PMDK_FOUND)
  foreach(COMPONENT ${PMDK_FIND_COMPONENTS})

    if(_PMDK_IMPORTED_TARGETS AND NOT TARGET PMDK::${COMPONENT})
      string(TOUPPER ${COMPONENT} UPPERCOMPONENT)

      if(PMDK_${UPPERCOMPONENT}_FOUND)
        add_library(PMDK::${COMPONENT} UNKNOWN IMPORTED)

        if(PMDK_INCLUDE_DIRS)
          set_target_properties(PMDK::${COMPONENT} PROPERTIES
            INTERFACE_INCLUDE_DIRECTORIES "${PMDK_INCLUDE_DIRS}")
        endif()

        if(EXISTS "${PMDK_${UPPERCOMPONENT}_LIBRARY}")
          set_target_properties(PMDK::${COMPONENT} PROPERTIES
            IMPORTED_LINK_INTERFACE_LANGUAGES "C"
            IMPORTED_LOCATION "${PMDK_${UPPERCOMPONENT}_LIBRARY}")
        endif()

        if(EXISTS "${PMDK_${UPPERCOMPONENT}_LIBRARY_RELEASE}")
          set_property(TARGET PMDK::${COMPONENT} APPEND PROPERTY
            IMPORTED_CONFIGURATIONS RELEASE)
          set_target_properties(PMDK::${COMPONENT} PROPERTIES
            IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
            IMPORTED_LOCATION_RELEASE "${PMDK_${UPPERCOMPONENT}_LIBRARY_RELEASE}")
        endif()

        if(EXISTS "${PMDK_${UPPERCOMPONENT}_LIBRARY_DEBUG}")
          set_property(TARGET PMDK::${COMPONENT} APPEND PROPERTY
            IMPORTED_CONFIGURATIONS DEBUG)
          set_target_properties(PMDK::${COMPONENT} PROPERTIES
            IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
            IMPORTED_LOCATION_DEBUG "${PMDK_${UPPERCOMPONENT}_LIBRARY_DEBUG}")
        endif()

        if(_PMDK_${UPPERCOMPONENT}_DEPENDENCIES)
          unset(_PMDK_${UPPERCOMPONENT}_TARGET_DEPENDENCIES)
          foreach(dep ${_PMDK_${UPPERCOMPONENT}_DEPENDENCIES})
            list(APPEND _PMDK_${UPPERCOMPONENT}_TARGET_DEPENDENCIES PMDK::${dep})
          endforeach()
          set_target_properties(PMDK::${COMPONENT} PROPERTIES
            INTERFACE_LINK_LIBRARIES "${_PMDK_${UPPERCOMPONENT}_TARGET_DEPENDENCIES}")
        endif()
      endif()
    endif()
  endforeach()
endif()


# ------------------------------------------------------------------------
#  Finalize
# ------------------------------------------------------------------------

# Report PMDK_LIBRARIES
set(PMDK_LIBRARIES "")
foreach(component IN LISTS PMDK_FIND_COMPONENTS)
  string(TOUPPER ${component} uppercomponent)
  if(PMDK_${uppercomponent}_FOUND)
    list(APPEND PMDK_LIBRARIES ${PMDK_${uppercomponent}_LIBRARY})
  endif()
endforeach()

# Configure display of cache entries in GUI.
foreach(v PMDKROOT PMDK_ROOT ${_PMDK_VARS_INC} ${_PMDK_VARS_LIB})
  get_property(_type CACHE ${v} PROPERTY TYPE)
  if(_type)
    set_property(CACHE ${v} PROPERTY ADVANCED 1)
    if("x${_type}" STREQUAL "xUNINITIALIZED")
      if("x${v}" STREQUAL "xPMDK_ADDITIONAL_VERSIONS")
        set_property(CACHE ${v} PROPERTY TYPE STRING)
      else()
        set_property(CACHE ${v} PROPERTY TYPE PATH)
      endif()
    endif()
  endif()
endforeach()

# Record last used values of input variables so we can
# detect on the next run if the user changed them.
foreach(v
  ${_PMDK_VARS_INC} ${_PMDK_VARS_LIB}
  ${_PMDK_VARS_DIR} ${_PMDK_VARS_NAME}
  )
  if(DEFINED ${v})
    set(_${v}_LAST "${${v}}" CACHE INTERNAL "Last used ${v} value.")
  else()
    unset(_${v}_LAST CACHE)
  endif()
endforeach()

# Maintain a persistent list of components requested anywhere since
# the last flush.
set(_PMDK_COMPONENTS_SEARCHED "${_PMDK_COMPONENTS_SEARCHED}")
list(APPEND _PMDK_COMPONENTS_SEARCHED ${PMDK_FIND_COMPONENTS})
list(REMOVE_DUPLICATES _PMDK_COMPONENTS_SEARCHED)
list(SORT _PMDK_COMPONENTS_SEARCHED)
set(_PMDK_COMPONENTS_SEARCHED "${_PMDK_COMPONENTS_SEARCHED}"
  CACHE INTERNAL "Components requested for this build tree.")
 No newline at end of file
+5 −1
Changes for docker/debian_build_env.docker: 5 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ RUN apt-get update && \
		zlib1g-dev \
		# syscall_intercept dependencies
		libcapstone-dev \
        # pmdk dependencies
        libndctl-dev \
        libdaxctl-dev \
        pandoc \
		# GekkoFS
		libboost-filesystem-dev \
		libboost-program-options-dev \
@@ -48,7 +52,7 @@ RUN apt-get update && \
		lsb-release \
		wget \
		software-properties-common \
		gnupg2 \
		gnupg2 && \
    # add clang-10 repos
    wget https://apt.llvm.org/llvm.sh -P /tmp && chmod +x /tmp/llvm.sh && /tmp/llvm.sh 10 && \
    # install clang-format
Compare 0af45bfa to 97820ed8
Changes for external/hermes: 1 added line, 1 removed line.
Original line number Diff line number Diff line
Subproject commit 0af45bfa667f7ff9c78167ef94d975bffbd879f0
Subproject commit 97820ed81aba9ef7c05891ce80c53a8533b78609
+162 −0
Changes for include/daemon/backend/data/pmdk/pool.hpp: 162 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*
  Copyright 2018-2021, Barcelona Supercomputing Center (BSC), Spain
  Copyright 2015-2021, Johannes Gutenberg Universitaet Mainz, Germany

  This software was partially supported by the
  EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).

  This software was partially supported by the
  ADA-FS project under the SPPEXA project funded by the DFG.

  This file is part of GekkoFS.

  GekkoFS is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  GekkoFS is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.

  SPDX-License-Identifier: GPL-3.0-or-later
*/

#ifndef GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP
#define GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP

#include <filesystem>
#include <spdlog/spdlog.h>

namespace fs = std::filesystem;

namespace pmdk {

/**
 * This class implements a RAII-enabled non-volatile memory pool based on
 * Intel's PMDK C library.
 */
class pool {

public:
    /**
     * Create a pool of non-volatile memory of a fixed `size` under
     * `parent_dir`. Give it a random name.
     *
     * @param[in]  parent_dir the parent directory where the memory pool backing
     *                        file will be stored
     * @param[in] size the maximum capacity in bytes of the pool
     * @returns the newly created pool
     */
    pool(const fs::path& parent_dir, std::size_t size) noexcept;

    pool(pool&& rhs) = default;
    pool(const pool& other) = delete;
    pool&
    operator=(pool&& rhs) = default;
    pool&
    operator=(const pool& other) = delete;

    /**
     * Destroy an existing pool.
     */
    ~pool();

    /**
     * Returns the path to a pool's data storage in the file system.
     *
     * @returns If the pool was correctly created, the function returns a
     *          `boost::fylesystem::path` with the path to the file backing the
     *          pool in the file system. Otherwise, `boost::filesystem::path{}`
     *          is returned.
     */
    fs::path
    path() const noexcept;

    /**
     * Returns the address to a pool's data region in non-volatile memory.
     *
     * @returns If the pool was correctly created, the function returns a
     *          void pointer to the non-volatile memory region containing the
     *          pool's data. Otherwise, `nullptr` is returned.
     */
    void*
    data() const noexcept;

    /**
     * Returns the size of a pool's data region in non-volatile memory.
     *
     * @returns If the pool was correctly created, the function returns the
     *          size of the non-volatile memory region containing
     *          the pool's data. Otherwise, `0` is returned.
     */
    std::size_t
    size() const noexcept;

    /**
     * Returns whether a pool's data is actually stored in non-volatile
     * memory.
     *
     * @returns The function returns `true` if the pool was correctly created,
     *          and the pool's data is stored in NVM. Otherwise, `false` is
     *          returned.
     */
    bool
    is_persistent() const noexcept;

    /**
     *
     * Check whether a pool is valid.
     *
     * @returns `true` if the pool was correctly created. Otherwise it
     *           returns `false`.
     */
    explicit operator bool() const noexcept;

    /**
     *
     * Check whether a pool is invalid.
     *
     * @returns `true` if the pool was not correctly created. Otherwise it
     *           returns `false`.
     */
    bool
    operator!() const noexcept;

    /**
     *
     * Check whether a pool is valid.
     *
     * @returns `true` if the pool was correctly created. Otherwise it
     *           returns `false`.
     */
    bool
    valid() const noexcept;

private:
    /// Logger instance
    std::shared_ptr<spdlog::logger> log_;

    /// Path to the pool file in the file system
    fs::path path_;

    /// Address of the pool's non-volatile memory region
    void* data_ = nullptr;

    /// Size of the pool's non-volatile memory region
    std::size_t size_ = 0;

    /// Flag set if the pool file is actually stored in non-volatile memory
    bool is_persistent_ = false;

    /// Flag set if the pool was created correctly
    bool is_valid_ = false;
};

} // namespace pmdk

#endif // GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP
 No newline at end of file
+15 −6
Changes for scripts/compile_dep.sh: 15 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -40,30 +40,30 @@ VALID_DEP_OPTIONS="mogon2 mogon1 ngio direct all ci"

MOGON1_DEPS=(
    "zstd" "lz4" "snappy" "capstone" "ofi" "mercury" "argobots" "margo" "rocksdb"
    "syscall_intercept" "date" "verbs"
    "syscall_intercept" "date" "verbs" "pmdk"
)

MOGON2_DEPS=(
    "bzip2" "zstd" "lz4" "snappy" "capstone" "ofi" "mercury" "argobots" "margo" "rocksdb"
    "syscall_intercept" "date" "psm2"
    "syscall_intercept" "date" "psm2" "pmdk"
)

NGIO_DEPS=(
    "zstd" "lz4" "snappy" "capstone" "ofi" "mercury" "argobots" "margo" "rocksdb"
    "syscall_intercept" "date" "agios" "psm2"
    "syscall_intercept" "date" "agios" "psm2" "pmdk"
)

DIRECT_DEPS=(
  "ofi" "mercury" "argobots" "margo" "rocksdb" "syscall_intercept" "date"
  "ofi" "mercury" "argobots" "margo" "rocksdb" "syscall_intercept" "date" "pmdk"
)

ALL_DEPS=(
    "bzip2" "zstd" "lz4" "snappy" "capstone" "bmi" "ofi" "mercury" "argobots" "margo" "rocksdb"
     "syscall_intercept" "date" "agios"
     "syscall_intercept" "date" "agios" "pmdk"
)

CI_DEPS=(
    "ofi" "mercury" "argobots" "margo" "rocksdb" "syscall_intercept" "date" "agios"
    "ofi" "mercury" "argobots" "margo" "rocksdb" "syscall_intercept" "date" "agios" "pmdk"
)

usage_short() {
@@ -514,4 +514,13 @@ if check_dependency "date" "${DEP_CONFIG[@]}"; then
    make install
fi

# PMDK
if check_dependency "pmdk" "${DEP_CONFIG[@]}"; then
    echo "############################################################ Installing:  PMDK"
    CURR=${SOURCE}/pmdk
    cd "${CURR}"
    make -j"${CORES}"
    make install prefix="${INSTALL}"
fi

echo "Done"
Loading
Loading