Skip to content

Commit

Permalink
Merge pull request #158 from nathan818fr/cmake-config
Browse files Browse the repository at this point in the history
Add libmem-config.cmake
  • Loading branch information
rdbo authored Dec 10, 2023
2 parents 7541d5e + 306c8ed commit e6b0142
Showing 1 changed file with 304 additions and 0 deletions.
304 changes: 304 additions & 0 deletions libmem-config.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
# libmem config to download and use pre-built binaries
#
# Config source: https://github.com/rdbo/libmem
# Config author: Nathan Poirier <[email protected]>
# Config version: 2023-12-10.0
#
# Minimum required CMake version: 3.21
#
# Example usage:
# project(my-project)
# [...]
# include(FetchContent)
# fetchcontent_declare(libmem-config URL "https://raw.githubusercontent.com/rdbo/libmem/config-v1/libmem-config.cmake" DOWNLOAD_NO_EXTRACT TRUE)
# fetchcontent_makeavailable(libmem-config)
# set(CMAKE_PREFIX_PATH "${libmem-config_SOURCE_DIR}" "${CMAKE_PREFIX_PATH}")
#
# set(LIBMEM_DOWNLOAD_VERSION "4.4.0-pre1")
# find_package(libmem CONFIG REQUIRED)
# [...]
# target_link_libraries(my-target PRIVATE libmem::libmem)
#
# Input variables:
# LIBMEM_ROOT (optional)
# Path to the root folder of the pre-built version of libmem (containing include and lib folders).
# If empty, the pre-built version of libmem will be downloaded automatically.
#
# LIBMEM_DOWNLOAD_URL (optional)
# URL for downloading the archive containing the pre-built version of libmem.
# "{{version}}" will be replaced by the value of LIBMEM_DOWNLOAD_VERSION.
# "{{target}}" will be replaced by the value of LIBMEM_DOWNLOAD_TARGET.
# Default: libmem's GitHub releases are used (see https://github.com/rdbo/libmem/releases).
#
# LIBMEM_DOWNLOAD_VERSION (required if LIBMEM_ROOT is empty and LIBMEM_DOWNLOAD_URL contains "{{version}}")
# libmem version to download, used to construct LIBMEM_DOWNLOAD_URL.
# Example: "4.4.0-pre1"
#
# LIBMEM_DOWNLOAD_TARGET (optional)
# libmem target to download, used to construct LIBMEM_DOWNLOAD_URL.
# If empty, the target is detected automatically based on the target platform and architecture.
# Example: "x86_64-windows-msvc-static-md"
#
# LIBMEM_USE_SHARED_LIBS (optional)
# Whether to use libmem as a shared library (ON) or as a static library (OFF).
# Default: OFF
#
# LIBMEM_MSVC_CRT (optional, MSVC only)
# MSVC runtime library to use: "MD" or "MT" (then MDd or MTd are automatically used for debug builds).
# Default: "MD"
#
# Defined variables:
# Optional input variables (see above) will be initialized to their default/auto-detected values.
#
# LIBMEM_FOUND
# Indicate whether libmem was found.
#
# LIBMEM_INCLUDE_DIRS (if LIBMEM_FOUND)
# List of include directories for libmem.
# It is recommended to use the target libmem::libmem instead of this variable (see below).
#
# LIBMEM_LIBRARIES (if LIBMEM_FOUND)
# List of libraries for libmem.
# It is recommended to use the target libmem::libmem instead of this variable (see below).
#
# Defined targets:
# libmem::libmem (if LIBMEM_FOUND)
# CMake target for libmem.
#

if(CMAKE_VERSION VERSION_LESS 3.21.0)
message(WARNING "libmem-config.cmake requires CMake 3.21 or newer (you are using ${CMAKE_VERSION})")
endif()

set(LIBMEM_ROOT "" CACHE PATH "Path to the root folder of the pre-built version of libmem (containing include and lib folders, downloaded automatically if empty)")
set(LIBMEM_DOWNLOAD_URL "https://github.com/rdbo/libmem/releases/download/{{version}}/libmem-{{version}}-{{target}}.tar.gz" CACHE STRING "URL for downloading the archive containing the pre-built version of libmem")
set(LIBMEM_DOWNLOAD_VERSION "" CACHE STRING "libmem version to download, used to construct LIBMEM_DOWNLOAD_URL (e.g. \"4.4.0-pre1\")")
set(LIBMEM_DOWNLOAD_TARGET "" CACHE STRING "libmem target to download, used to construct LIBMEM_DOWNLOAD_URL (detected automatically if empty)")
set(LIBMEM_USE_SHARED_LIBS OFF CACHE BOOL "Whether to use libmem as a shared library (ON) or as a static library (OFF)")

if (MSVC)
set(LIBMEM_MSVC_CRT "MD" CACHE STRING "MSVC runtime library to use: \"MD\" or \"MT\" (ignored if LIBMEM_USE_SHARED_LIBS is ON)")
if (NOT LIBMEM_MSVC_CRT MATCHES "^(MD|MT)$")
message(FATAL_ERROR "LIBMEM_MSVC_CRT must be either \"MD\" or \"MT\" (then MDd or MTd are automatically used for debug builds)")
endif ()
endif ()

function(_libmem_message _libmem_message_TYPE _libmem_message_MESSAGE)
if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) # defined by find_package(libmem [...] QUIET)
message(${_libmem_message_TYPE} ${_libmem_message_MESSAGE})
endif ()
endfunction()

# Download libmem if needed (when LIBMEM_ROOT is not set)
if (NOT LIBMEM_ROOT)
# Construct the final download URL by replacing placeholders
if (LIBMEM_DOWNLOAD_URL MATCHES "{{version}}")
# Version must always be explicitly set
# We could use the latest version from GitHub releases, but it would be less reliable, less reproducible, etc.
if (NOT LIBMEM_DOWNLOAD_VERSION)
message(FATAL_ERROR "Unknown libmem version to download, please set LIBMEM_DOWNLOAD_VERSION")
endif ()
string(REPLACE "{{version}}" "${LIBMEM_DOWNLOAD_VERSION}" LIBMEM_DOWNLOAD_URL "${LIBMEM_DOWNLOAD_URL}")
endif ()

if (LIBMEM_DOWNLOAD_URL MATCHES "{{target}}")
# Target can be explicitly set, or we try to detect it automatically
if (NOT LIBMEM_DOWNLOAD_TARGET)
# 1. Detect target platform
if (MSVC)
set(_LIBMEM_DOWNLOAD_TARGET_PLATFORM "windows-msvc")
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
set(_LIBMEM_DOWNLOAD_TARGET_PLATFORM "linux-gnu")
endif ()

# 2. Detect target architecture
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _LIBMEM_LOWER_SYSTEM_PROCESSOR)
if (_LIBMEM_LOWER_SYSTEM_PROCESSOR MATCHES "(aarch64|arm64)")
set(_LIBMEM_DOWNLOAD_TARGET_ARCH "aarch64")
elseif (_LIBMEM_LOWER_SYSTEM_PROCESSOR MATCHES "(x86.64|amd64|x64)")
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_LIBMEM_DOWNLOAD_TARGET_ARCH "x86_64")
elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
set(_LIBMEM_DOWNLOAD_TARGET_ARCH "i686")
endif ()
elseif (_LIBMEM_LOWER_SYSTEM_PROCESSOR MATCHES "(i.86|x86)")
set(_LIBMEM_DOWNLOAD_TARGET_ARCH "i686")
endif ()

# 3. Detect target variant
if (MSVC)
if (LIBMEM_USE_SHARED_LIBS)
set(_LIBMEM_DOWNLOAD_TARGET_VARIANT "shared-md")
elseif (LIBMEM_MSVC_CRT STREQUAL "MD")
set(_LIBMEM_DOWNLOAD_TARGET_VARIANT "static-md")
else ()
set(_LIBMEM_DOWNLOAD_TARGET_VARIANT "static-mt")
endif ()
elseif (LIBMEM_USE_SHARED_LIBS)
set(_LIBMEM_DOWNLOAD_TARGET_VARIANT "shared")
else ()
set(_LIBMEM_DOWNLOAD_TARGET_VARIANT "static")
endif ()

# Check that all variables were set and construct the final target
if (NOT _LIBMEM_DOWNLOAD_TARGET_ARCH OR NOT _LIBMEM_DOWNLOAD_TARGET_PLATFORM OR NOT _LIBMEM_DOWNLOAD_TARGET_VARIANT)
message(NOTICE "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
message(NOTICE "MSVC: ${MSVC}")
message(NOTICE "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
message(NOTICE "CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}")
message(FATAL_ERROR "Unable to determine libmem target to download, please set LIBMEM_DOWNLOAD_TARGET (e.g. \"x86_64-windows-msvc-static-md\")")
endif ()
set(LIBMEM_DOWNLOAD_TARGET "${_LIBMEM_DOWNLOAD_TARGET_ARCH}-${_LIBMEM_DOWNLOAD_TARGET_PLATFORM}-${_LIBMEM_DOWNLOAD_TARGET_VARIANT}")
_libmem_message(STATUS "Detected libmem target: ${LIBMEM_DOWNLOAD_TARGET}")
endif ()

string(REPLACE "{{target}}" "${LIBMEM_DOWNLOAD_TARGET}" LIBMEM_DOWNLOAD_URL "${LIBMEM_DOWNLOAD_URL}")
endif ()

# Use the download URL as download cache key
# This way, the archive will be re-downloaded if the URL changes
# Since the URL contains the version, it will be re-downloaded if the version changes
string(SHA1 _LIBMEM_DOWNLOAD_NAME "${LIBMEM_DOWNLOAD_URL}")
set(_LIBMEM_DOWNLOAD_NAME "libmem-${_LIBMEM_DOWNLOAD_NAME}")
set(_LIBMEM_DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_LIBMEM_DOWNLOAD_NAME}")

# Download and extract libmem if the download directory doesn't exist or is empty
file(GLOB _LIBMEM_DOWNLOAD_DIR_CHILDREN "${_LIBMEM_DOWNLOAD_DIR}/*")
if (NOT _LIBMEM_DOWNLOAD_DIR_CHILDREN)
# Download the archive if it doesn't exist
set(_LIBMEM_DOWNLOAD_FILE "${_LIBMEM_DOWNLOAD_DIR}.tar.gz")
if (NOT EXISTS "${_LIBMEM_DOWNLOAD_FILE}")
_libmem_message(STATUS "Downloading libmem from ${LIBMEM_DOWNLOAD_URL} (to ${_LIBMEM_DOWNLOAD_FILE})")
file(DOWNLOAD "${LIBMEM_DOWNLOAD_URL}" "${_LIBMEM_DOWNLOAD_FILE}"
SHOW_PROGRESS
STATUS _LIBMEM_DOWNLOAD_STATUS
LOG _LIBMEM_DOWNLOAD_LOG
)
list(GET _LIBMEM_DOWNLOAD_STATUS 0 LIBMEM_DOWNLOAD_STATUS_CODE)
if (NOT LIBMEM_DOWNLOAD_STATUS_CODE EQUAL 0)
file(REMOVE "${_LIBMEM_DOWNLOAD_FILE}")
message(NOTICE "${_LIBMEM_DOWNLOAD_LOG}")
message(FATAL_ERROR "libmem download failed with status: ${_LIBMEM_DOWNLOAD_STATUS}")
endif ()
endif ()

# Extract the archive
_libmem_message(STATUS "Extracting libmem to ${_LIBMEM_DOWNLOAD_DIR}")
file(ARCHIVE_EXTRACT
INPUT "${_LIBMEM_DOWNLOAD_FILE}"
DESTINATION "${_LIBMEM_DOWNLOAD_DIR}"
)
file(GLOB _LIBMEM_DOWNLOAD_DIR_CHILDREN "${_LIBMEM_DOWNLOAD_DIR}/*") # important: update the list of children
endif ()

# Finally, set LIBMEM_ROOT
if (_LIBMEM_DOWNLOAD_DIR_CHILDREN MATCHES ";")
# If there are multiple children, use the download directory as root
set(LIBMEM_ROOT "${_LIBMEM_DOWNLOAD_DIR}")
else ()
# If there is only one child folder, use it as root
set(LIBMEM_ROOT "${_LIBMEM_DOWNLOAD_DIR_CHILDREN}")
endif ()
_libmem_message(STATUS "Using libmem from: ${LIBMEM_DOWNLOAD_URL} (downloaded to ${LIBMEM_ROOT})")
else ()
_libmem_message(STATUS "Using libmem from: ${LIBMEM_ROOT}")
endif ()

# Find intermediate files
function(_libmem_find_library
_libmem_get_library_DEBUG_VAR
_libmem_get_library_RELEASE_VAR
_libmem_get_library_NAME
_libmem_get_library_EXTENSION
)
if (WIN32)
set(_libmem_get_library_debug "${LIBMEM_ROOT}/lib/debug/${_libmem_get_library_NAME}.${_libmem_get_library_EXTENSION}")
set(_libmem_get_library_release "${LIBMEM_ROOT}/lib/release/${_libmem_get_library_NAME}.${_libmem_get_library_EXTENSION}")
else ()
set(_libmem_get_library_debug "${LIBMEM_ROOT}/lib/lib${_libmem_get_library_NAME}.${_libmem_get_library_EXTENSION}")
set(_libmem_get_library_release "${_libmem_get_library_debug}")
endif ()

# Set output variables (empty if the file doesn't exist)
if (EXISTS "${_libmem_get_library_debug}")
set(${_libmem_get_library_DEBUG_VAR} "${_libmem_get_library_debug}" PARENT_SCOPE)
else ()
set(${_libmem_get_library_DEBUG_VAR} "${_libmem_get_library_DEBUG_VAR}-NOTFOUND" PARENT_SCOPE)
endif ()
if (EXISTS "${_libmem_get_library_release}")
set(${_libmem_get_library_RELEASE_VAR} "${_libmem_get_library_release}" PARENT_SCOPE)
else ()
set(${_libmem_get_library_RELEASE_VAR} "${_libmem_get_library_RELEASE_VAR}-NOTFOUND" PARENT_SCOPE)
endif ()
endfunction()

set(_LIBMEM_REQUIRED_VARS "")

list(APPEND _LIBMEM_REQUIRED_VARS "_LIBMEM_INCLUDE_DIR")
if (EXISTS "${LIBMEM_ROOT}/include/libmem/libmem.h")
set(_LIBMEM_INCLUDE_DIR "${LIBMEM_ROOT}/include")
else ()
set(_LIBMEM_INCLUDE_DIR "")
endif ()

list(APPEND _LIBMEM_REQUIRED_VARS "_LIBMEM_DEBUG_LIBRARY" "_LIBMEM_RELEASE_LIBRARY")
if (WIN32)
_libmem_find_library(_LIBMEM_DEBUG_LIBRARY _LIBMEM_RELEASE_LIBRARY "libmem" "lib")
if (LIBMEM_USE_SHARED_LIBS)
# _LIBMEM_DEBUG_LIBRARY and _LIBMEM_RELEASE_LIBRARY point to an import library,
# ensure that the corresponding DLLs are available
list(APPEND _LIBMEM_REQUIRED_VARS "_LIBMEM_DEBUG_DLL" "_LIBMEM_RELEASE_DLL")
_libmem_find_library(_LIBMEM_DEBUG_DLL _LIBMEM_RELEASE_DLL "libmem" "dll")
endif ()
else ()
if (LIBMEM_USE_SHARED_LIBS)
_libmem_find_library(_LIBMEM_DEBUG_LIBRARY _LIBMEM_RELEASE_LIBRARY "libmem" "so")
else ()
_libmem_find_library(_LIBMEM_DEBUG_LIBRARY _LIBMEM_RELEASE_LIBRARY "libmem" "a")
endif ()
endif ()

# Use find_package_handle_standard_args to check find_package conditions
# It will set LIBMEM_FOUND
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(libmem
DEFAULT_MSG
${_LIBMEM_REQUIRED_VARS}
)

# If libmem was found, define output variables and targets
if (LIBMEM_FOUND)
set(LIBMEM_INCLUDE_DIRS "${_LIBMEM_INCLUDE_DIR}")
set(LIBMEM_LIBRARIES
"$<$<CONFIG:DEBUG>:${_LIBMEM_DEBUG_LIBRARY}>"
"$<$<NOT:$<CONFIG:DEBUG>>:${_LIBMEM_RELEASE_LIBRARY}>"
)
_libmem_message(STATUS "Found libmem include dirs: ${LIBMEM_INCLUDE_DIRS}")
_libmem_message(STATUS "Found libmem libraries: ${LIBMEM_LIBRARIES}")

add_library(libmem INTERFACE)
add_library(libmem::libmem ALIAS libmem)

target_include_directories(libmem INTERFACE "${LIBMEM_INCLUDE_DIRS}")
target_link_libraries(libmem INTERFACE "${LIBMEM_LIBRARIES}")
endif ()

# Hide internal variables
mark_as_advanced(
_LIBMEM_LOWER_SYSTEM_PROCESSOR
_LIBMEM_DOWNLOAD_TARGET_ARCH
_LIBMEM_DOWNLOAD_TARGET_PLATFORM
_LIBMEM_DOWNLOAD_TARGET_VARIANT
_LIBMEM_DOWNLOAD_NAME
_LIBMEM_DOWNLOAD_DIR
_LIBMEM_DOWNLOAD_FILE
_LIBMEM_DOWNLOAD_STATUS
_LIBMEM_DOWNLOAD_LOG
_LIBMEM_DOWNLOAD_DIR_CHILDREN
_LIBMEM_REQUIRED_VARS
_LIBMEM_INCLUDE_DIR
_LIBMEM_DEBUG_LIBRARY
_LIBMEM_RELEASE_LIBRARY
_LIBMEM_DEBUG_DLL
_LIBMEM_RELEASE_DLL
)

0 comments on commit e6b0142

Please sign in to comment.