# This script provides helper functions for FindModules # Parse and set variables from VERSION dependency file # On return: # MODULENAME_ARCHIVE will be set to parent scope # MODULENAME_VER will be set to parent scope (eg FFMPEG_VER, DAV1D_VER) # MODULENAME_BASE_URL will be set to parent scope if exists in VERSION file (eg FFMPEG_BASE_URL) # MODULENAME_HASH will be set if either SHA256 or SHA512 exists in VERSION file # MODULENAME_BYPRODUCT will be set to parent scope function(get_versionfile_data) # Dependency path set(MODULE_PATH "${PROJECTSOURCE}/tools/depends/${LIB_TYPE}/${MODULE_LC}") if(NOT EXISTS "${MODULE_PATH}/${MODULE}-VERSION") MESSAGE(FATAL_ERROR "${MODULE}-VERSION does not exist at ${MODULE_PATH}.") else() set(${MODULE}_FILE "${MODULE_PATH}/${MODULE}-VERSION") endif() file(STRINGS ${${MODULE}_FILE} ${MODULE}_LNAME REGEX "^[ \t]*LIBNAME=") file(STRINGS ${${MODULE}_FILE} ${MODULE}_VER REGEX "^[ \t]*VERSION=") file(STRINGS ${${MODULE}_FILE} ${MODULE}_ARCHIVE REGEX "^[ \t]*ARCHIVE=") file(STRINGS ${${MODULE}_FILE} ${MODULE}_BASE_URL REGEX "^[ \t]*BASE_URL=") if(WIN32 OR WINDOWS_STORE) file(STRINGS ${${MODULE}_FILE} ${MODULE}_BYPRODUCT REGEX "^[ \t]*BYPRODUCT_WIN=") else() file(STRINGS ${${MODULE}_FILE} ${MODULE}_BYPRODUCT REGEX "^[ \t]*BYPRODUCT=") endif() # Tarball Hash file(STRINGS ${${MODULE}_FILE} ${MODULE}_HASH_SHA256 REGEX "^[ \t]*SHA256=") file(STRINGS ${${MODULE}_FILE} ${MODULE}_HASH_SHA512 REGEX "^[ \t]*SHA512=") string(REGEX REPLACE ".*LIBNAME=([^ \t]*).*" "\\1" ${MODULE}_LNAME "${${MODULE}_LNAME}") string(REGEX REPLACE ".*VERSION=([^ \t]*).*" "\\1" ${MODULE}_VER "${${MODULE}_VER}") string(REGEX REPLACE ".*ARCHIVE=([^ \t]*).*" "\\1" ${MODULE}_ARCHIVE "${${MODULE}_ARCHIVE}") string(REGEX REPLACE ".*BASE_URL=([^ \t]*).*" "\\1" ${MODULE}_BASE_URL "${${MODULE}_BASE_URL}") if(WIN32 OR WINDOWS_STORE) string(REGEX REPLACE ".*BYPRODUCT_WIN=([^ \t]*).*" "\\1" ${MODULE}_BYPRODUCT "${${MODULE}_BYPRODUCT}") else() string(REGEX REPLACE ".*BYPRODUCT=([^ \t]*).*" "\\1" ${MODULE}_BYPRODUCT "${${MODULE}_BYPRODUCT}") endif() string(REGEX REPLACE "\\$\\(LIBNAME\\)" "${${MODULE}_LNAME}" ${MODULE}_ARCHIVE "${${MODULE}_ARCHIVE}") string(REGEX REPLACE "\\$\\(VERSION\\)" "${${MODULE}_VER}" ${MODULE}_ARCHIVE "${${MODULE}_ARCHIVE}") set(${MODULE}_ARCHIVE ${${MODULE}_ARCHIVE} PARENT_SCOPE) set(${MODULE}_VER ${${MODULE}_VER} PARENT_SCOPE) if (${MODULE}_BASE_URL) set(${MODULE}_BASE_URL ${${MODULE}_BASE_URL} PARENT_SCOPE) else() set(${MODULE}_BASE_URL "http://mirrors.kodi.tv/build-deps/sources" PARENT_SCOPE) endif() set(${MODULE}_BYPRODUCT ${${MODULE}_BYPRODUCT} PARENT_SCOPE) # allow user to override the download URL hash with a local tarball hash # needed for offline build envs if (NOT DEFINED ${MODULE}_HASH) if (${MODULE}_HASH_SHA256) set(${MODULE}_HASH ${${MODULE}_HASH_SHA256} PARENT_SCOPE) elseif(${MODULE}_HASH_SHA512) set(${MODULE}_HASH ${${MODULE}_HASH_SHA512} PARENT_SCOPE) endif() endif() endfunction() # Parse and set Version from VERSION dependency file # Used for retrieving version numbers for dependency libs to allow setting # a required version for find_package call # On return: # LIB_MODULENAME_VER will be set to parent scope (eg LIB_FMT_VER) function(get_libversion_data module libtype) # Dependency path set(LIB_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/depends/${libtype}/${module}") string(TOUPPER ${module} MOD_UPPER) if(NOT EXISTS "${LIB_MODULE_PATH}/${MOD_UPPER}-VERSION") MESSAGE(FATAL_ERROR "${MOD_UPPER}-VERSION does not exist at ${LIB_MODULE_PATH}.") else() set(${MOD_UPPER}_FILE "${LIB_MODULE_PATH}/${MOD_UPPER}-VERSION") endif() file(STRINGS ${${MOD_UPPER}_FILE} ${MOD_UPPER}_VER REGEX "^[ \t]*VERSION=") string(REGEX REPLACE ".*VERSION=([^ \t]*).*" "\\1" ${MOD_UPPER}_VER "${${MOD_UPPER}_VER}") set(LIB_${MOD_UPPER}_VER ${${MOD_UPPER}_VER} PARENT_SCOPE) endfunction() # Function to loop through list of patch files (full path) # Sets to a PATCH_COMMAND variable and set to parent scope (caller) # Used to test windows line endings and set appropriate patch commands function(generate_patchcommand _patchlist) # find the path to the patch executable find_package(Patch MODULE REQUIRED) # Loop through patches and add to PATCH_COMMAND # for windows, check CRLF/LF state set(_count 0) foreach(patch ${_patchlist}) if(WIN32 OR WINDOWS_STORE) PATCH_LF_CHECK(${patch}) endif() if(${_count} EQUAL "0") set(_patch_command ${PATCH_EXECUTABLE} -p1 -i ${patch}) else() list(APPEND _patch_command COMMAND ${PATCH_EXECUTABLE} -p1 -i ${patch}) endif() math(EXPR _count "${_count}+1") endforeach() set(PATCH_COMMAND ${_patch_command} PARENT_SCOPE) unset(_count) unset(_patch_command) endfunction() # Macro to factor out the repetitive URL setup macro(SETUP_BUILD_VARS) string(TOUPPER ${MODULE_LC} MODULE) # Fall through to target build module dir if not explicitly set if(NOT DEFINED LIB_TYPE) set(LIB_TYPE "target") endif() # Location for build type, native or target if(LIB_TYPE STREQUAL "target") set(DEP_LOCATION "${DEPENDS_PATH}") else() set(DEP_LOCATION "${NATIVEPREFIX}") endif() # PROJECTSOURCE used in native toolchain to provide core project sourcedir # to externalproject_add targets that have a different CMAKE_SOURCE_DIR (eg jsonschema/texturepacker in-tree) if(NOT PROJECTSOURCE) set(PROJECTSOURCE ${CMAKE_SOURCE_DIR}) endif() # populate variables of data from VERSION file for MODULE get_versionfile_data() # allow user to override the download URL with a local tarball # needed for offline build envs if(${MODULE}_URL) get_filename_component(${MODULE}_URL "${${MODULE}_URL}" ABSOLUTE) else() set(${MODULE}_URL ${${MODULE}_BASE_URL}/${${MODULE}_ARCHIVE}) endif() if(VERBOSE) message(STATUS "MODULE: ${MODULE}") message(STATUS "LIB_TYPE: ${LIB_TYPE}") message(STATUS "DEP_LOCATION: ${DEP_LOCATION}") message(STATUS "PROJECTSOURCE: ${PROJECTSOURCE}") message(STATUS "${MODULE}_URL: ${${MODULE}_URL}") endif() endmacro() macro(CLEAR_BUILD_VARS) # unset all generic variables to insure clean state between macro calls # Potentially an issue with scope when a macro is used inside a dep that uses a macro unset(PROJECTSOURCE) unset(LIB_TYPE) unset(BUILD_NAME) unset(INSTALL_DIR) unset(CMAKE_ARGS) unset(PATCH_COMMAND) unset(CONFIGURE_COMMAND) unset(BUILD_COMMAND) unset(INSTALL_COMMAND) unset(BUILD_IN_SOURCE) unset(BUILD_BYPRODUCTS) unset(WIN_DISABLE_PROJECT_FLAGS) # unset all module specific variables to insure clean state between macro calls # potentially an issue when a native and a target of the same module exists unset(${MODULE}_LIST_SEPARATOR) unset(${MODULE}_GENERATOR) unset(${MODULE}_GENERATOR_PLATFORM) unset(${MODULE}_INSTALL_PREFIX) unset(${MODULE}_TOOLCHAIN_FILE) endmacro() # Macro to create externalproject_add target # # Common usage # # CMAKE_ARGS: cmake(required) # PATCH_COMMAND: ALL(optional) # CONFIGURE_COMMAND: autoconf(required), meson(required) # BUILD_COMMAND: autoconf(required), meson(required), cmake(optional) # INSTALL_COMMAND: autoconf(required), meson(required), cmake(optional) # BUILD_IN_SOURCE: ALL(optional) # BUILD_BYPRODUCTS: ALL(optional) # # Windows Specific # WIN_DISABLE_PROJECT_FLAGS - Set to not use core compiler flags for externalproject_add target # This removes CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS # from the externalproject_add target. Primarily used for HOST build # tools that may have different arch/build requirements to the core app # target (eg flatc) # macro(BUILD_DEP_TARGET) include(ExternalProject) # Remove cmake warning when Xcode generator used with "New" build system if(CMAKE_GENERATOR STREQUAL Xcode) # Policy CMP0114 is not set to NEW. In order to support the Xcode "new build # system", this project must be updated to set policy CMP0114 to NEW. if(CMAKE_XCODE_BUILD_SYSTEM STREQUAL 12) cmake_policy(SET CMP0114 NEW) else() cmake_policy(SET CMP0114 OLD) endif() endif() if(CMAKE_ARGS) set(CMAKE_ARGS CMAKE_ARGS ${CMAKE_ARGS} -DCMAKE_INSTALL_LIBDIR=lib -DPROJECTSOURCE=${PROJECTSOURCE} "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") # We dont have a toolchain for windows, so manually add all the cmake # build arguments we may want # We can disable adding them with WIN_DISABLE_PROJECT_FLAGS. This is potentially required # for host build tools (eg flatc) that may be a different arch to the core app if(WIN32 OR WINDOWS_STORE) if(NOT DEFINED WIN_DISABLE_PROJECT_FLAGS) list(APPEND CMAKE_ARGS "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} $<$:${CMAKE_C_FLAGS_DEBUG}> $<$:${CMAKE_C_FLAGS_RELEASE}> ${${MODULE}_C_FLAGS}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} $<$:${CMAKE_CXX_FLAGS_DEBUG}> $<$:${CMAKE_CXX_FLAGS_RELEASE}> ${${MODULE}_CXX_FLAGS}" "-DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} $<$:${CMAKE_EXE_LINKER_FLAGS_DEBUG}> $<$:${CMAKE_EXE_LINKER_FLAGS_RELEASE}> ${${MODULE}_EXE_LINKER_FLAGS}") endif() endif() if(${MODULE}_INSTALL_PREFIX) list(APPEND CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${${MODULE}_INSTALL_PREFIX}) else() list(APPEND CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DEP_LOCATION}) endif() if(DEFINED ${MODULE}_TOOLCHAIN_FILE) list(APPEND CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${${MODULE}_TOOLCHAIN_FILE}) elseif(CMAKE_TOOLCHAIN_FILE) list(APPEND CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) endif() # Set build type for dep build. # if MODULE has set a manual build type, use it, otherwise use project build type if(${MODULE}_BUILD_TYPE) list(APPEND CMAKE_ARGS "-DCMAKE_BUILD_TYPE=${${MODULE}_BUILD_TYPE}") # Build_type is forced, so unset the opposite _LIBRARY_ to only give # select_library_configurations one library name to choose from if(${MODULE}_BUILD_TYPE STREQUAL "Release") unset(${MODULE}_LIBRARY_DEBUG) else() unset(${MODULE}_LIBRARY_RELEASE) endif() else() # single config generator (ie Make, Ninja) if(CMAKE_BUILD_TYPE) list(APPEND CMAKE_ARGS "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") else() # Multi-config generators (eg VS, Xcode, Ninja Multi-Config) will not have CMAKE_BUILD_TYPE # Use config genex to generate the types # Potential issue if Build type isnt supported by lib project # eg lib supports Debug/Release, however users selects RelWithDebInfo in project list(APPEND CMAKE_ARGS "-DCMAKE_BUILD_TYPE=$") endif() endif() # Xcode - Default sub projects to makefile builds. More consistent # Windows - Default to same generator version used in parent if(CMAKE_GENERATOR STREQUAL Xcode) if(NOT ${MODULE}_GENERATOR) set(${MODULE}_GENERATOR CMAKE_GENERATOR "Unix Makefiles") endif() elseif(MSVC) if(NOT ${MODULE}_GENERATOR) set(${MODULE}_GENERATOR CMAKE_GENERATOR "${CMAKE_GENERATOR}") endif() if(NOT ${MODULE}_GENERATOR_PLATFORM) set(${MODULE}_GENERATOR_PLATFORM CMAKE_GENERATOR_PLATFORM ${CMAKE_GENERATOR_PLATFORM}) endif() endif() endif() if(PATCH_COMMAND) set(PATCH_COMMAND PATCH_COMMAND ${PATCH_COMMAND}) endif() if(CONFIGURE_COMMAND) if(NOT CMAKE_ARGS AND DEP_BUILDENV) # DEP_BUILDENV only used for non cmake externalproject_add builds # iterate through CONFIGURE_COMMAND looking for multiple COMMAND, we need to # add DEP_BUILDENV for each distinct COMMAND set(tmp_config_command ${DEP_BUILDENV}) foreach(item ${CONFIGURE_COMMAND}) list(APPEND tmp_config_command ${item}) if(item STREQUAL "COMMAND") list(APPEND tmp_config_command ${DEP_BUILDENV}) endif() endforeach() set(CONFIGURE_COMMAND CONFIGURE_COMMAND ${tmp_config_command}) unset(tmp_config_command) else() set(CONFIGURE_COMMAND CONFIGURE_COMMAND ${CONFIGURE_COMMAND}) endif() endif() if(BUILD_COMMAND) set(BUILD_COMMAND BUILD_COMMAND ${BUILD_COMMAND}) endif() if(INSTALL_COMMAND) set(INSTALL_COMMAND INSTALL_COMMAND ${INSTALL_COMMAND}) endif() if(BUILD_IN_SOURCE) set(BUILD_IN_SOURCE BUILD_IN_SOURCE ${BUILD_IN_SOURCE}) endif() # Set Library names. if(DEFINED ${MODULE}_DEBUG_POSTFIX) set(_POSTFIX ${${MODULE}_DEBUG_POSTFIX}) string(REGEX REPLACE "\\.[^.]*$" "" _LIBNAME ${${MODULE}_BYPRODUCT}) string(REGEX REPLACE "^.*\\." "" _LIBEXT ${${MODULE}_BYPRODUCT}) set(${MODULE}_LIBRARY_DEBUG ${DEP_LOCATION}/lib/${_LIBNAME}${${MODULE}_DEBUG_POSTFIX}.${_LIBEXT}) endif() # set _LIBRARY_RELEASE for use of select_library_configurations # any modules that dont use select_library_configurations, we set _LIBRARY # No harm in having either set for both potential paths set(${MODULE}_LIBRARY_RELEASE ${DEP_LOCATION}/lib/${${MODULE}_BYPRODUCT}) set(${MODULE}_LIBRARY ${${MODULE}_LIBRARY_RELEASE}) if(NOT ${MODULE}_INCLUDE_DIR) set(${MODULE}_INCLUDE_DIR ${DEP_LOCATION}/include) endif() if(BUILD_BYPRODUCTS) set(BUILD_BYPRODUCTS BUILD_BYPRODUCTS ${BUILD_BYPRODUCTS}) else() if(DEFINED ${MODULE}_LIBRARY_DEBUG) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") set(BUILD_BYPRODUCTS BUILD_BYPRODUCTS "$,${${MODULE}_LIBRARY_DEBUG},${${MODULE}_LIBRARY_RELEASE}>") else() if(DEFINED CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Release" AND DEFINED ${MODULE}_LIBRARY_DEBUG) set(BUILD_BYPRODUCTS BUILD_BYPRODUCTS "${${MODULE}_LIBRARY_DEBUG}") else() set(BUILD_BYPRODUCTS BUILD_BYPRODUCTS "${${MODULE}_LIBRARY}") endif() else() message(FATAL_ERROR "MultiConfig Generator usage requires CMake >= 3.20.0 - Generator Expressions in BYPRODUCT option") endif() endif() else() set(BUILD_BYPRODUCTS BUILD_BYPRODUCTS "${${MODULE}_LIBRARY}") endif() endif() if(NOT BUILD_NAME) set(BUILD_NAME ${MODULE_LC}) endif() if(NOT INSTALL_DIR) set(INSTALL_DIR ${DEP_LOCATION}) endif() # Allow a target to supply in-tree source location. eg TexturePacker, JsonSchemaBuilder if(${MODULE}_SOURCE_DIR) set(BUILD_DOWNLOAD_STEPS SOURCE_DIR "${${MODULE}_SOURCE_DIR}") else() set(BUILD_DOWNLOAD_STEPS URL ${${MODULE}_URL} URL_HASH ${${MODULE}_HASH} DOWNLOAD_DIR ${TARBALL_DIR} DOWNLOAD_NAME ${${MODULE}_ARCHIVE}) endif() externalproject_add(${BUILD_NAME} ${BUILD_DOWNLOAD_STEPS} PREFIX ${CORE_BUILD_DIR}/${BUILD_NAME} INSTALL_DIR ${INSTALL_DIR} ${${MODULE}_LIST_SEPARATOR} ${CMAKE_ARGS} ${${MODULE}_GENERATOR} ${${MODULE}_GENERATOR_PLATFORM} ${PATCH_COMMAND} ${CONFIGURE_COMMAND} ${BUILD_COMMAND} ${INSTALL_COMMAND} ${BUILD_BYPRODUCTS} ${BUILD_IN_SOURCE}) set_target_properties(${BUILD_NAME} PROPERTIES FOLDER "External Projects") CLEAR_BUILD_VARS() endmacro() # Macro to test format of line endings of a patch # Windows Specific macro(PATCH_LF_CHECK patch) if(CMAKE_HOST_WIN32) # On Windows "patch.exe" can only handle CR-LF line-endings. # Our patches have LF-only line endings - except when they # have been checked out as part of a dependency hosted on Git # and core.autocrlf=true. file(READ ${ARGV0} patch_content_hex HEX) # Force handle LF-only line endings if(NOT patch_content_hex MATCHES "0d0a") if (NOT "--binary" IN_LIST PATCH_EXECUTABLE) list(APPEND PATCH_EXECUTABLE --binary) endif() else() if ("--binary" IN_LIST PATCH_EXECUTABLE) list(REMOVE_ITEM PATCH_EXECUTABLE --binary) endif() endif() endif() unset(patch_content_hex) endmacro()