From c04dcc2e7d834218ef2d4194331e383402495ae1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:22 +0200 Subject: Adding upstream version 2:20.4+dfsg. Signed-off-by: Daniel Baumann --- cmake/scripts/common/AddOptions.cmake | 78 +++ cmake/scripts/common/AddonHelpers.cmake | 437 ++++++++++++ cmake/scripts/common/AddonHelpers.dox | 59 ++ cmake/scripts/common/ArchSetup.cmake | 201 ++++++ cmake/scripts/common/CMakeHelpers.cmake | 54 ++ cmake/scripts/common/CheckCommits.cmake | 75 ++ cmake/scripts/common/CheckTargetPlatform.cmake | 70 ++ cmake/scripts/common/CompilerSettings.cmake | 5 + cmake/scripts/common/DependencyOptions.cmake | 23 + cmake/scripts/common/GenerateVersionedFiles.cmake | 35 + cmake/scripts/common/GeneratorSetup.cmake | 49 ++ cmake/scripts/common/HandleDepends.cmake | 301 +++++++++ cmake/scripts/common/Macros.cmake | 789 ++++++++++++++++++++++ cmake/scripts/common/ModuleHelpers.cmake | 424 ++++++++++++ cmake/scripts/common/PathSetup.cmake | 13 + cmake/scripts/common/Platform.cmake | 57 ++ cmake/scripts/common/PrepareEnv.cmake | 107 +++ cmake/scripts/common/ProjectMacros.cmake | 89 +++ cmake/scripts/common/StaticAnalysis.cmake | 39 ++ cmake/scripts/common/Uninstall.cmake | 58 ++ 20 files changed, 2963 insertions(+) create mode 100644 cmake/scripts/common/AddOptions.cmake create mode 100644 cmake/scripts/common/AddonHelpers.cmake create mode 100644 cmake/scripts/common/AddonHelpers.dox create mode 100644 cmake/scripts/common/ArchSetup.cmake create mode 100644 cmake/scripts/common/CMakeHelpers.cmake create mode 100644 cmake/scripts/common/CheckCommits.cmake create mode 100644 cmake/scripts/common/CheckTargetPlatform.cmake create mode 100644 cmake/scripts/common/CompilerSettings.cmake create mode 100644 cmake/scripts/common/DependencyOptions.cmake create mode 100644 cmake/scripts/common/GenerateVersionedFiles.cmake create mode 100644 cmake/scripts/common/GeneratorSetup.cmake create mode 100644 cmake/scripts/common/HandleDepends.cmake create mode 100644 cmake/scripts/common/Macros.cmake create mode 100644 cmake/scripts/common/ModuleHelpers.cmake create mode 100644 cmake/scripts/common/PathSetup.cmake create mode 100644 cmake/scripts/common/Platform.cmake create mode 100644 cmake/scripts/common/PrepareEnv.cmake create mode 100644 cmake/scripts/common/ProjectMacros.cmake create mode 100644 cmake/scripts/common/StaticAnalysis.cmake create mode 100644 cmake/scripts/common/Uninstall.cmake (limited to 'cmake/scripts/common') diff --git a/cmake/scripts/common/AddOptions.cmake b/cmake/scripts/common/AddOptions.cmake new file mode 100644 index 0000000..96837c1 --- /dev/null +++ b/cmake/scripts/common/AddOptions.cmake @@ -0,0 +1,78 @@ +# - Add options without repeating them on the command line +# +# Synopsis: +# +# add_options (lang build opts) +# +# where: +# +# lang Name of the language whose compiler should receive the +# options, e.g. CXX. If a comma-separated list is received +# then the option is added for all those languages. Use the +# special value ALL_LANGUAGES for these languages: CXX, C +# and Fortran +# +# build Kind of build to which this options should apply, +# such as DEBUG and RELEASE. This can also be a comma- +# separated list. Use the special value ALL_BUILDS to apply +# to all builds. +# +# opts List of options to add. Each should be quoted. +# +# Example: +# +# add_options (CXX RELEASE "-O3" "-DNDEBUG" "-Wall") + +function(add_options langs builds) + # special handling of empty language specification + if("${langs}" STREQUAL "ALL_LANGUAGES") + set(langs CXX C Fortran) + endif() + foreach(lang IN LISTS langs) + # prepend underscore if necessary + foreach(build IN LISTS builds) + if(NOT ("${build}" STREQUAL "ALL_BUILDS")) + set(_bld "_${build}") + string(TOUPPER "${_bld}" _bld) + else() + set(_bld "") + endif() + foreach(_opt IN LISTS ARGN) + set(_var "CMAKE_${lang}_FLAGS${_bld}") + #message(STATUS "Adding \"${_opt}\" to \${${_var}}") + # remove it first + string(REPLACE "${_opt}" "" _without "${${_var}}") + string(STRIP "${_without}" _without) + # we need to strip this one as well, so they are comparable + string(STRIP "${${_var}}" _stripped) + # if it wasn't there, then add it at the end + if("${_without}" STREQUAL "${_stripped}") + # don't add any extra spaces if no options yet are set + if(NOT ${_stripped} STREQUAL "") + set(${_var} "${_stripped} ${_opt}") + else() + set(${_var} "${_opt}") + endif() + set(${_var} "${${_var}}" PARENT_SCOPE) + endif() + endforeach() + endforeach() + endforeach() +endfunction() + +# set varname to flag unless user has specified something that matches regex +function(set_default_option varname flag regex) + if(NOT "$ENV{CXXFLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}" MATCHES "${regex}") + set(${varname} ${flag} PARENT_SCOPE) + else() + set(${varname} PARENT_SCOPE) + endif() +endfunction() + +# note: this must be called before project() +macro(no_default_options) + # prevent the platform probe to set options + set(CMAKE_NOT_USING_CONFIG_FLAGS TRUE) +endmacro() diff --git a/cmake/scripts/common/AddonHelpers.cmake b/cmake/scripts/common/AddonHelpers.cmake new file mode 100644 index 0000000..c541ad7 --- /dev/null +++ b/cmake/scripts/common/AddonHelpers.cmake @@ -0,0 +1,437 @@ +# Workaround for the fact that cpack's filenames are not customizable. +# Each add-on is added as a separate component to facilitate zip/tgz packaging. +# The filenames are always of the form basename-component, which is +# incompatible with the addonid-version scheme we want. This hack renames +# the files from the file names generated by the 'package' target. +# Sadly we cannot extend the 'package' target, as it is a builtin target, see +# http://public.kitware.com/Bug/view.php?id=8438 +# Thus, we have to add an 'addon-package' target. +get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(_isMultiConfig) + add_custom_target(addon-package DEPENDS PACKAGE) +else() + add_custom_target(addon-package + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target package) +endif() + +macro(add_cpack_workaround target version ext) + if(NOT PACKAGE_DIR) + set(PACKAGE_DIR "${CMAKE_INSTALL_PREFIX}/zips") + endif() + + add_custom_command(TARGET addon-package POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PACKAGE_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_PACKAGE_DIRECTORY}/addon-${target}-${version}-${PLATFORM_TAG}.${ext} ${PACKAGE_DIR}/${target}+${PLATFORM_TAG}/${target}-${version}.${ext}) +endmacro() + +# Grab the version from a given add-on's addon.xml +macro (addon_version dir prefix) + if(EXISTS ${PROJECT_SOURCE_DIR}/${dir}/addon.xml.in) + file(READ ${PROJECT_SOURCE_DIR}/${dir}/addon.xml.in ADDONXML) + else() + file(READ ${dir}/addon.xml ADDONXML) + endif() + + string(REGEX MATCH "]*version.?=.?.[0-9\\.]+" VERSION_STRING ${ADDONXML}) + string(REGEX REPLACE ".*version=.([0-9\\.]+).*" "\\1" ${prefix}_VERSION ${VERSION_STRING}) + message(STATUS ${prefix}_VERSION=${${prefix}_VERSION}) +endmacro() + +# Build, link and optionally package an add-on +macro (build_addon target prefix libs) + addon_version(${target} ${prefix}) + + # Below comes the generation of a list with used sources where the includes to + # kodi's headers becomes checked. + # This goes the following steps to identify them: + # 1. Check headers are at own depended on addon + # - If so, it is checked whether the whole folder is already inserted, if + # not, it is added. + # 2. If headers are not defined independently and there is more as one source + # file. + # - If yes, it is checked whether the headers with the sources together + # - In case no headers are inserted and more than one source file exists, + # the whole addon folder is searched for headers. + # 3. As a last step, the actual source files are checked. + if(${prefix}_SOURCES) + # Read used headers from addon, needed to identitfy used kodi addon interface headers + if(${prefix}_HEADERS) + # Add the used header files defined with CMakeLists.txt from addon itself + string(FIND "${${prefix}_HEADERS}" "${PROJECT_SOURCE_DIR}" position) + if(position GREATER -1) + # include path name already complete + list(APPEND USED_SOURCES ${${prefix}_HEADERS}) + else() + # add the complete include path to begin + foreach(hdr_file ${${prefix}_HEADERS}) + list(APPEND USED_SOURCES ${PROJECT_SOURCE_DIR}/${hdr_file}) + endforeach() + endif() + else() + list(LENGTH ${prefix}_SOURCES _length) + if(${_length} GREATER 1) + string(REGEX MATCHALL "[.](h)" _length ${${prefix}_SOURCES}}) + if(NOT _length) + file(GLOB_RECURSE USED_SOURCES ${PROJECT_SOURCE_DIR}/*.h*) + if(USED_SOURCES) + message(AUTHOR_WARNING "Header files not defined in your CMakeLists.txt. Please consider defining ${prefix}_HEADERS as list of all headers used by this addon. Falling back to recursive scan for *.h.") + endif() + endif() + endif() + endif() + + # Add the used source files defined with CMakeLists.txt from addon itself + string(FIND "${${prefix}_SOURCES}" "${PROJECT_SOURCE_DIR}" position) + if(position GREATER -1) + # include path name already complete + list(APPEND USED_SOURCES ${${prefix}_SOURCES}) + else() + # add the complete include path to begin + foreach(src_file ${${prefix}_SOURCES}) + list(APPEND USED_SOURCES ${PROJECT_SOURCE_DIR}/${src_file}) + endforeach() + endif() + + message(STATUS "Addon dependency check ...") + # Set defines used in addon.xml.in and read from versions.h to set add-on + # version parts automatically + file(STRINGS ${KODI_INCLUDE_DIR}/versions.h BIN_ADDON_PARTS) + foreach(loop_var ${BIN_ADDON_PARTS}) + # Only pass strings with "#define ADDON_" from versions.h + if(loop_var MATCHES "#define ADDON_") + string(REGEX REPLACE "\\\n" " " loop_var ${loop_var}) # remove header line breaks + string(REGEX REPLACE "#define " "" loop_var ${loop_var}) # remove the #define name from string + string(REGEX MATCHALL "[//a-zA-Z0-9._-]+" loop_var "${loop_var}") # separate the define values to a list + + # Get the definition name + list(GET loop_var 0 include_name) + # Check definition are depends who is a bigger list + if("${include_name}" MATCHES "_DEPENDS") + # Use start definition name as base for other value type + list(GET loop_var 0 list_name) + string(REPLACE "_DEPENDS" "_MIN" depends_minver ${list_name}) + string(REPLACE "_DEPENDS" "" depends_ver ${list_name}) + string(REPLACE "_DEPENDS" "_XML_ID" xml_entry_name ${list_name}) + string(REPLACE "_DEPENDS" "_USED" used_type_name ${list_name}) + + # remove the first value, not needed and wrong on "for" loop + list(REMOVE_AT loop_var 0) + + foreach(depend_header ${loop_var}) + string(STRIP ${depend_header} depend_header) + foreach(src_file ${USED_SOURCES}) + file(STRINGS ${src_file} BIN_ADDON_SRC_PARTS) + foreach(loop_var ${BIN_ADDON_SRC_PARTS}) + string(REGEX MATCH "^[ \t]*#[ \t]*(include|import)[ \t]*[<\"](kodi\/)?(.+)[\">]" include_name "${loop_var}") + if(include_name AND CMAKE_MATCH_3 MATCHES ^${depend_header}) + get_directory_property(CURRENT_DEFS COMPILE_DEFINITIONS) + if(NOT used_type_name IN_LIST CURRENT_DEFS) + set(ADDON_DEPENDS "${ADDON_DEPENDS}\n") + # Inform with them the addon header about used type, if not present before + add_definitions(-D${used_type_name}) + message(STATUS " - Added API usage definition: ${used_type_name} (Version: \"${${depends_ver}}\", Min. Version: \"${${depends_minver}}\")") + set(FOUND_HEADER_USAGE 1) + endif() + endif() + endforeach() + if(FOUND_HEADER_USAGE EQUAL 1) # break this loop if found but not unset, needed in parts where includes muddled up on addon + break() + endif() + endforeach() + # type is found and round becomes broken for next round with other type + if(FOUND_HEADER_USAGE EQUAL 1) + unset(FOUND_HEADER_USAGE) + break() + endif() + endforeach() + else() + # read the definition values and make it by the on version.h defined names public + list(GET loop_var 1 include_variable) + string(REGEX REPLACE ".*\"(.*)\"" "\\1" ${include_name} ${include_variable}) + set(${include_name} ${${include_name}}) + endif() + endif() + endforeach() + + add_library(${target} ${${prefix}_SOURCES} ${${prefix}_HEADERS}) + target_link_libraries(${target} ${${libs}}) + set_target_properties(${target} PROPERTIES VERSION ${${prefix}_VERSION} + SOVERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR} + PREFIX "" + POSITION_INDEPENDENT_CODE 1) + if(OS STREQUAL "android") + set_target_properties(${target} PROPERTIES PREFIX "lib") + endif() + elseif(${prefix}_CUSTOM_BINARY) + add_custom_target(${target} ALL) + endif() + + # get the library's location + if(${prefix}_CUSTOM_BINARY) + list(GET ${prefix}_CUSTOM_BINARY 0 LIBRARY_LOCATION) + list(GET ${prefix}_CUSTOM_BINARY 1 LIBRARY_FILENAME) + if(CORE_SYSTEM_NAME STREQUAL android) + set(LIBRARY_FILENAME "lib${LIBRARY_FILENAME}") + endif() + else() + set(LIBRARY_LOCATION $) + # get the library's filename + if(CORE_SYSTEM_NAME STREQUAL android) + # for android we need the filename without any version numbers + set(LIBRARY_FILENAME $) + else() + set(LIBRARY_FILENAME $) + endif() + endif() + + # if there's an addon.xml.in we need to generate the addon.xml + if(EXISTS ${PROJECT_SOURCE_DIR}/${target}/addon.xml.in) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/${target}/addon.xml.in) + + file(READ ${PROJECT_SOURCE_DIR}/${target}/addon.xml.in addon_file) + + # If sources are present must be the depends set + if(${prefix}_SOURCES) + string(FIND "${addon_file}" "\@ADDON_DEPENDS\@" matchres) + if("${matchres}" EQUAL -1) + message(FATAL_ERROR "\"\@ADDON_DEPENDS\@\" not found in addon.xml.in.") + endif() + endif() + + # TODO: remove this hack after v18 + string(REPLACE "\@PLATFORM\@" "\@PLATFORM_TAG\@" addon_file "${addon_file}") + + string(CONFIGURE "${addon_file}" addon_file_conf @ONLY) + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}/addon.xml CONTENT "${addon_file_conf}") + if(${APP_NAME_UC}_BUILD_DIR) + file(GENERATE OUTPUT ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/addon.xml CONTENT "${addon_file_conf}") + endif() + endif() + + # if there's an settings.xml.in we need to generate the settings.xml + if(EXISTS ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in) + + file(READ ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in settings_file) + string(CONFIGURE "${settings_file}" settings_file_conf @ONLY) + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}/resources/settings.xml CONTENT "${settings_file_conf}") + if(${APP_NAME_UC}_BUILD_DIR) + file(GENERATE OUTPUT ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/resources/settings.xml CONTENT "${settings_file_conf}") + endif() + endif() + + # set zip as default if addon-package is called without PACKAGE_XXX + set(CPACK_GENERATOR "ZIP") + set(ext "zip") + if(PACKAGE_ZIP OR PACKAGE_TGZ) + if(PACKAGE_TGZ) + set(CPACK_GENERATOR "TGZ") + set(ext "tar.gz") + endif() + set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) + set(CPACK_PACKAGE_FILE_NAME addon) + if(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CPACK_STRIP_FILES TRUE) + endif() + set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) + set(CPACK_COMPONENTS_IGNORE_GROUPS 1) + list(APPEND CPACK_COMPONENTS_ALL ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + # Pack files together to create an archive + install(DIRECTORY ${target} ${CMAKE_CURRENT_BINARY_DIR}/${target} DESTINATION ./ + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG} + REGEX ".+\\.xml\\.in(clude)?$" EXCLUDE) + if(WIN32) + if(NOT CPACK_PACKAGE_DIRECTORY) + # determine the temporary path + file(TO_CMAKE_PATH "$ENV{TEMP}" WIN32_TEMP_PATH) + string(LENGTH "${WIN32_TEMP_PATH}" WIN32_TEMP_PATH_LENGTH) + string(LENGTH "${PROJECT_BINARY_DIR}" PROJECT_BINARY_DIR_LENGTH) + + # check if the temporary path is shorter than the default packaging directory path + if(WIN32_TEMP_PATH_LENGTH GREATER 0 AND WIN32_TEMP_PATH_LENGTH LESS PROJECT_BINARY_DIR_LENGTH) + # set the directory used by CPack for packaging to the temp directory + set(CPACK_PACKAGE_DIRECTORY ${WIN32_TEMP_PATH}) + endif() + endif() + + if(${prefix}_SOURCES) + # install the generated DLL file + install(PROGRAMS ${LIBRARY_LOCATION} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + + # for debug builds also install the PDB file + install(FILES $ DESTINATION ${target} + CONFIGURATIONS Debug RelWithDebInfo + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_CUSTOM_BINARY) + install(FILES ${LIBRARY_LOCATION} DESTINATION ${target} RENAME ${LIBRARY_FILENAME} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_CUSTOM_DATA) + install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${target}/resources + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY) + install(FILES ${${prefix}_ADDITIONAL_BINARY} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY_EXE) + install(PROGRAMS ${${prefix}_ADDITIONAL_BINARY_EXE} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY_PARTS) + install(FILES ${${prefix}_ADDITIONAL_BINARY_PARTS} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY_DIRS) + install(DIRECTORY ${${prefix}_ADDITIONAL_BINARY_DIRS} DESTINATION ${target} USE_SOURCE_PERMISSIONS + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + else() # NOT WIN32 + if(NOT CPACK_PACKAGE_DIRECTORY) + set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}) + endif() + if(${prefix}_SOURCES) + install(TARGETS ${target} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_CUSTOM_BINARY) + install(FILES ${LIBRARY_LOCATION} DESTINATION ${target} RENAME ${LIBRARY_FILENAME} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_CUSTOM_DATA) + install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${target}/resources + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY) + install(FILES ${${prefix}_ADDITIONAL_BINARY} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY_EXE) + install(PROGRAMS ${${prefix}_ADDITIONAL_BINARY_EXE} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY_PARTS) + install(FILES ${${prefix}_ADDITIONAL_BINARY_PARTS} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + if(${prefix}_ADDITIONAL_BINARY_DIRS) + install(DIRECTORY ${${prefix}_ADDITIONAL_BINARY_DIRS} DESTINATION ${target} USE_SOURCE_PERMISSIONS + COMPONENT ${target}-${${prefix}_VERSION}-${PLATFORM_TAG}) + endif() + endif() + add_cpack_workaround(${target} ${${prefix}_VERSION} ${ext}) + else() + if(CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) + if(NOT OVERRIDE_PATHS) + if(CMAKE_INSTALL_PREFIX AND NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND NOT CMAKE_INSTALL_PREFIX STREQUAL "${${APP_NAME_UC}_PREFIX}") + message(WARNING "CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} differs from ${APP_NAME} prefix, changing to ${${APP_NAME_UC}_PREFIX}. Please pass -DOVERRIDE_PATHS=1 to skip this check") + endif() + if(CMAKE_INSTALL_LIBDIR AND NOT CMAKE_INSTALL_LIBDIR STREQUAL "${${APP_NAME_UC}_LIB_DIR}") + message(WARNING "CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR} differs from ${APP_NAME} libdir, changing to ${${APP_NAME_UC}_LIB_DIR}. Please pass -DOVERRIDE_PATHS=1 to skip this check") + endif() + if(CMAKE_INSTALL_DATADIR AND NOT CMAKE_INSTALL_DATADIR STREQUAL "${${APP_NAME_UC}_DATA_DIR}") + message(WARNING "CMAKE_INSTALL_DATADIR ${CMAKE_INSTALL_DATADIR} differs from ${APP_NAME} datadir, changing to ${${APP_NAME_UC}_DATA_DIR}. Please pass -DOVERRIDE_PATHS=1 to skip this check") + endif() + set(CMAKE_INSTALL_PREFIX "${${APP_NAME_UC}_PREFIX}" CACHE PATH "${APP_NAME} install prefix" FORCE) + set(CMAKE_INSTALL_LIBDIR "${${APP_NAME_UC}_LIB_DIR}" CACHE PATH "${APP_NAME} install libdir" FORCE) + set(CMAKE_INSTALL_DATADIR "${${APP_NAME_UC}_DATA_DIR}" CACHE PATH "${APP_NAME} install datadir" FORCE) + else() + if(NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib/${APP_NAME_LC}") + endif() + if(NOT CMAKE_INSTALL_DATADIR) + set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share/${APP_NAME_LC}") + endif() + endif() + else() + set(CMAKE_INSTALL_LIBDIR "lib/${APP_NAME_LC}") + set(CMAKE_INSTALL_DATADIR "share/${APP_NAME_LC}") + endif() + if(${prefix}_SOURCES) + install(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target}) + endif() + if (${prefix}_CUSTOM_BINARY) + install(FILES ${LIBRARY_LOCATION} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target} RENAME ${LIBRARY_FILENAME}) + endif() + install(DIRECTORY ${target} ${CMAKE_CURRENT_BINARY_DIR}/${target} DESTINATION ${CMAKE_INSTALL_DATADIR}/addons + REGEX ".+\\.xml\\.in(clude)?$" EXCLUDE) + if(${prefix}_CUSTOM_DATA) + install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${CMAKE_INSTALL_DATADIR}/addons/${target}/resources) + endif() + if(${prefix}_ADDITIONAL_BINARY) + install(FILES ${${prefix}_ADDITIONAL_BINARY} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target}) + endif() + if(${prefix}_ADDITIONAL_BINARY_EXE) + install(PROGRAMS ${${prefix}_ADDITIONAL_BINARY_EXE} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target}) + endif() + if(${prefix}_ADDITIONAL_BINARY_PARTS) + install(FILES ${${prefix}_ADDITIONAL_BINARY_PARTS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target}) + endif() + if(${prefix}_ADDITIONAL_BINARY_DIRS) + install(DIRECTORY ${${prefix}_ADDITIONAL_BINARY_DIRS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target} USE_SOURCE_PERMISSIONS) + endif() + endif() + if(${APP_NAME_UC}_BUILD_DIR) + file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/${target}/*) + if(${prefix}_CUSTOM_DATA) + get_filename_component(dname ${${prefix}_CUSTOM_DATA} NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${${prefix}_CUSTOM_DATA} + ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/resources/${dname}) + endif() + foreach(file ${files}) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/${target}/" "" name "${file}") + # A good way to deal with () in filenames + if(NOT ${file} MATCHES xml.in) + configure_file(${file} ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/${name} COPYONLY) + endif() + endforeach() + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${LIBRARY_LOCATION} + ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/${LIBRARY_FILENAME}) + if(${prefix}_ADDITIONAL_BINARY) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${${prefix}_ADDITIONAL_BINARY} + ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}) + endif() + endif() +endmacro() + +# finds a path to a given file (recursive) +function (kodi_find_path var_name filename search_path strip_file) + file(GLOB_RECURSE PATH_TO_FILE ${search_path} ${filename}) + if(strip_file) + string(REPLACE ${filename} "" PATH_TO_FILE ${PATH_TO_FILE}) + endif() + set (${var_name} ${PATH_TO_FILE} PARENT_SCOPE) +endfunction() + +# Cmake build options +include(AddOptions) +include(TestCXXAcceptsFlag) +option(PACKAGE_ZIP "Package Zip file?" OFF) +option(PACKAGE_TGZ "Package TGZ file?" OFF) +option(BUILD_SHARED_LIBS "Build shared libs?" ON) + +# LTO support? +CHECK_CXX_ACCEPTS_FLAG("-flto" HAVE_LTO) +if(HAVE_LTO) + option(USE_LTO "use link time optimization" OFF) + if(USE_LTO) + add_options(ALL_LANGUAGES ALL_BUILDS "-flto") + endif() +endif() + +# set this to try linking dependencies as static as possible +if(ADDONS_PREFER_STATIC_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) +endif() + +if(${APP_NAME_UC}_BUILD_DIR) + list(APPEND CMAKE_PREFIX_PATH ${${APP_NAME_UC}_BUILD_DIR}/build) +endif() diff --git a/cmake/scripts/common/AddonHelpers.dox b/cmake/scripts/common/AddonHelpers.dox new file mode 100644 index 0000000..522e0e7 --- /dev/null +++ b/cmake/scripts/common/AddonHelpers.dox @@ -0,0 +1,59 @@ +/*! +\addtogroup cpp_cmake + +Kodi which uses it as a library for its binary addons has a special build +system for this. + +To implement this, a CMake macro brought by Kodi is used, this is +"build_addon (...)". This processes various definitions passed by the addon to +process the construction. + + +-------------------------------------------------------------------------------- + +Here's a minimal example of the addon used for CMakeLists.txt: + +~~~~~~~~~~~~~{.cmake} +cmake_minimum_required(VERSION 3.5) +project(example.addon) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) + +find_package(Kodi REQUIRED) + +include_directories(${KODI_INCLUDE_DIR} + +set(DEPLIBS ) # Here empty +set(EXAMPLE_SOURCES src/main.cpp) +set(EXAMPLE_HEADERS src/main.h) + +build_addon((example.addon EXAMPLE DEPLIBS) + +include(CPack) +~~~~~~~~~~~~~ + + +-------------------------------------------------------------------------------- + +This is a list of special variables that can be passed to the macro. +The parts defined with "*" must be given the second name given to the macro. + +Here to define the necessary creation and installation files on addon CMakeLists.txt: +| Name | Description +|-----------------------------|------------------------------------------------- +| *_SOURCES | List of source code files to be complicated. +| *_HEADERS | List of used source code header files. +| *_CUSTOM_BINARY | For special cases where an already created library from an external source is inserted, the "* _SOURCES" and "* _HEADERS" are unused in this case.
This is currently used primarily on game addons. +| *_CUSTOM_DATA | To add additional required data from a folder, which are stored in the shared folder of the addon.
With a "/" at the end of the content given to the folder is used, without the folder itself. +| *_ADDITIONAL_BINARY | In case the additional library has to be installed for the addon, the path or CMake name can be given here. +| *_ADDITIONAL_BINARY_EXE | In case you need to addon an additional application you can give the path or CMake name, it will be in the same folder as the addon library.
The mode bits are set there as EXE. +| *_ADDITIONAL_BINARY_DIRS | To add complete folders additionally to folders containing the addon library.
With a "/" at the end of the content given to the folder is used, without the folder itself. + +External creation Options, given by `-D...`: +| Name | Description +|-----------------------------|------------------------------------------------- +| PACKAGE_ZIP | To create a package as a ZIP file. This is also used to install locally addon together.
Default is OFF. +| PACKAGE_TGZ | To create a package as a TGZ file.
Default is OFF. +| BUILD_SHARED_LIBS | To define if addon library is shared or static.
Default is ON to have shared. +| USE_LTO | Use link time optimization.
Default is OFF. +*/ diff --git a/cmake/scripts/common/ArchSetup.cmake b/cmake/scripts/common/ArchSetup.cmake new file mode 100644 index 0000000..a59fcb5 --- /dev/null +++ b/cmake/scripts/common/ArchSetup.cmake @@ -0,0 +1,201 @@ +# This script configures the build for a given architecture. +# Flags and stringified arch is set up. +# General compiler tests belongs here. +# +# On return, the following variables are set: +# CMAKE_SYSTEM_NAME - a lowercased system name +# CPU - the CPU on the target +# ARCH - the system architecture +# ARCH_DEFINES - list of compiler definitions for this architecture +# SYSTEM_DEFINES - list of compiler definitions for this system +# DEP_DEFINES - compiler definitions for system dependencies (e.g. LIRC) +# + the results of compiler tests etc. + +# workaround a bug in older cmake, where binutils wouldn't be set after deleting CMakeCache.txt +include(CMakeFindBinUtils) + +include(CheckCXXSourceCompiles) +include(CheckSymbolExists) +include(CheckFunctionExists) +include(CheckIncludeFile) +include(CheckTypeSize) + +# Macro to check if a given builtin function exists +# Arguments: +# func the function to check +# var the compiler definition to set if type exists +# On return: +# If type was found, the definition is added to SYSTEM_DEFINES +macro(check_builtin func var) + check_cxx_source_compiles(" + int main() + { + ${func}; + }" ${var}) + if(${var}) + list(APPEND SYSTEM_DEFINES -D${var}=1) + endif() +endmacro() + + +# -------- Main script --------- +message(STATUS "System type: ${CMAKE_SYSTEM_NAME}") + +if(WITH_CPU) + set(CPU ${WITH_CPU}) +elseif(NOT KODI_DEPENDSBUILD) + set(CPU ${CMAKE_SYSTEM_PROCESSOR}) +endif() + +if(CMAKE_TOOLCHAIN_FILE) + if(NOT EXISTS "${CMAKE_TOOLCHAIN_FILE}") + message(FATAL_ERROR "Toolchain file ${CMAKE_TOOLCHAIN_FILE} does not exist.") + elseif(KODI_DEPENDSBUILD AND (NOT DEPENDS_PATH OR NOT NATIVEPREFIX)) + message(FATAL_ERROR "Toolchain did not define DEPENDS_PATH or NATIVEPREFIX. Possibly outdated depends.") + endif() +endif() + +# While CMAKE_CROSSCOMPILING is set unconditionally if there's a toolchain file, +# this variable is set if we can execute build artefacts on the host system (for example unit tests). +if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_SYSTEM_PROCESSOR AND + CMAKE_HOST_SYSTEM_NAME STREQUAL CMAKE_SYSTEM_NAME) + if(NOT DEFINED HOST_CAN_EXECUTE_TARGET) + set(HOST_CAN_EXECUTE_TARGET TRUE) + endif() +else() + if(NOT HOST_CAN_EXECUTE_TARGET) + set(HOST_CAN_EXECUTE_TARGET FALSE) + endif() +endif() + +# system specific arch setup +if(NOT EXISTS ${CMAKE_SOURCE_DIR}/cmake/scripts/${CORE_SYSTEM_NAME}/ArchSetup.cmake) + message(FATAL_ERROR "Couldn't find configuration for '${CORE_SYSTEM_NAME}' " + "Either the platform is not (yet) supported " + "or a toolchain file has to be specified. " + "Consult ${CMAKE_SOURCE_DIR}/cmake/README.md for instructions. " + "Note: Specifying a toolchain requires a clean build directory!") +endif() +include(${CMAKE_SOURCE_DIR}/cmake/scripts/${CORE_SYSTEM_NAME}/ArchSetup.cmake) + +# No TARBALL_DIR given, or no arch specific default set +if(NOT TARBALL_DIR) + set(TARBALL_DIR ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/download) +endif() + +message(STATUS "Core system type: ${CORE_SYSTEM_NAME}") +message(STATUS "Platform: ${CORE_PLATFORM_NAME}") +message(STATUS "CPU: ${CPU}, ARCH: ${ARCH}") +message(STATUS "Cross-Compiling: ${CMAKE_CROSSCOMPILING}") +message(STATUS "Execute build artefacts on host: ${CORE_HOST_IS_TARGET}") +message(STATUS "Depends based build: ${KODI_DEPENDSBUILD}") + +check_symbol_exists(posix_fadvise fcntl.h HAVE_POSIX_FADVISE) +check_symbol_exists(PRIdMAX inttypes.h HAVE_INTTYPES_H) +check_builtin("long* temp=0; long ret=__sync_add_and_fetch(temp, 1)" HAS_BUILTIN_SYNC_ADD_AND_FETCH) +check_builtin("long* temp=0; long ret=__sync_sub_and_fetch(temp, 1)" HAS_BUILTIN_SYNC_SUB_AND_FETCH) +check_builtin("long* temp=0; long ret=__sync_val_compare_and_swap(temp, 1, 1)" HAS_BUILTIN_SYNC_VAL_COMPARE_AND_SWAP) +check_include_file(sys/inotify.h HAVE_INOTIFY) +if(HAVE_INOTIFY) + list(APPEND SYSTEM_DEFINES -DHAVE_INOTIFY=1) +endif() +if(HAVE_POSIX_FADVISE) + list(APPEND SYSTEM_DEFINES -DHAVE_POSIX_FADVISE=1) +endif() +check_function_exists(localtime_r HAVE_LOCALTIME_R) +if(HAVE_LOCALTIME_R) + list(APPEND SYSTEM_DEFINES -DHAVE_LOCALTIME_R=1) +endif() +check_function_exists(gmtime_r HAVE_GMTIME_R) +if(HAVE_GMTIME_R) +list(APPEND SYSTEM_DEFINES -DHAVE_GMTIME_R=1) +endif() +if(HAVE_INTTYPES_H) + list(APPEND SYSTEM_DEFINES -DHAVE_INTTYPES_H=1) +endif() + +set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") +check_symbol_exists("STATX_BTIME" "linux/stat.h" HAVE_STATX) +if(HAVE_STATX) + check_function_exists("statx" FOUND_STATX_FUNCTION) + if(FOUND_STATX_FUNCTION) + message(STATUS "statx is available") + list(APPEND ARCH_DEFINES "-DHAVE_STATX=1") + else() + message(STATUS "statx flags found but no linkable function : C library too old ?") + endif() +else() + message(STATUS "statx() not found") +endif() +set(CMAKE_REQUIRED_DEFINITIONS "") + +find_package(SSE) +foreach(_sse SSE SSE2 SSE3 SSSE3 SSE4_1 SSE4_2 AVX AVX2) + if(${${_sse}_FOUND}) + # enable SSE versions up to 4.1 by default, if available + if(NOT ${_sse} MATCHES "AVX" AND NOT ${_sse} STREQUAL "SSE4_2") + option(ENABLE_${_sse} "Enable ${_sse}" ON) + else() + option(ENABLE_${_sse} "Enable ${_sse}" OFF) + endif() + endif() + if(ENABLE_${_sse}) + set(HAVE_${_sse} TRUE CACHE STRING "${_sse} enabled") + list(APPEND ARCH_DEFINES -DHAVE_${_sse}=1) + endif() +endforeach() + +if(NOT DEFINED NEON OR NEON) + option(ENABLE_NEON "Enable NEON optimization" ${NEON}) + if(ENABLE_NEON) + message(STATUS "NEON optimization enabled") + add_definitions(-DHAS_NEON) + if(NEON_FLAGS) + add_options(ALL_LANGUAGES ALL_BUILDS ${NEON_FLAGS}) + endif() + endif() +endif() + +if(NOT MSVC) + # these options affect all code built by cmake including external projects. + add_options(ALL_LANGUAGES ALL_BUILDS + -Wall + -Wdouble-promotion + -Wmissing-field-initializers + -Wsign-compare + -Wextra + -Wno-unused-parameter # from -Wextra + ) + + if(CMAKE_COMPILER_IS_GNUCXX) + add_options(ALL_LANGUAGES ALL_BUILDS + -Wno-cast-function-type # from -Wextra + ) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_options(ALL_LANGUAGES ALL_BUILDS + -Wno-bad-function-cast + -Wno-deprecated + ) + endif() + + add_options(CXX ALL_BUILDS + -Wnon-virtual-dtor + ) + + add_options(ALL_LANGUAGES DEBUG + -g + -D_DEBUG + ) + + # these options affect only core code + if(NOT CORE_COMPILE_OPTIONS) + set(CORE_COMPILE_OPTIONS + -Werror=double-promotion + -Werror=missing-field-initializers + -Werror=sign-compare + ) + endif() +endif() + +# set for compile info to help detect binary addons +set(APP_SHARED_LIBRARY_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}") diff --git a/cmake/scripts/common/CMakeHelpers.cmake b/cmake/scripts/common/CMakeHelpers.cmake new file mode 100644 index 0000000..995c38a --- /dev/null +++ b/cmake/scripts/common/CMakeHelpers.cmake @@ -0,0 +1,54 @@ +# This file contains functions that support the debugging of the CMake files. + +# This file shouldn't be included per default in any CMake file. It should be +# included and used only on demand. All functions are prefixed with "debug_". +# +# Usage: +# include(scripts/common/CMakeHelpers.cmake) +# debug_print_variables() + +# Print all CMake variables. +macro(debug_print_variables) + get_cmake_property(_variableNames VARIABLES) + foreach(_variableName ${_variableNames}) + message(STATUS "${_variableName} = ${${_variableName}}") + endforeach() +endmacro() + +# Get all properties that CMake supports and convert them to a list. +function(debug_get_properties VAR) + execute_process(COMMAND cmake --help-property-list + OUTPUT_VARIABLE _properties) + string(REGEX REPLACE ";" "\\\\;" _properties "${_properties}") + string(REGEX REPLACE "\n" ";" _properties "${_properties}") + list(REMOVE_DUPLICATES _properties) + list(REMOVE_ITEM _properties LOCATION) + set(${VAR} ${_properties} PARENT_SCOPE) +endfunction() + +# List all properties. +function(debug_list_properties) + debug_get_properties(_properties) + message("CMake properties = ${_properties}") +endfunction() + +# Print all set properties of a specified target. +function(debug_print_target_properties target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "There is no target named '${target}'") + endif() + + debug_get_properties(_properties) + + # Reading LOCATION property is deprecated and triggers a fatal error. + string(REGEX REPLACE ";LOCATION;|LOCATION" "" _properties "${_properties}") + string(REGEX REPLACE "" "${CMAKE_BUILD_TYPE}" _properties + "${_properties}") + foreach(_property ${_properties}) + get_property(_value TARGET ${target} PROPERTY ${_property} SET) + if(_value) + get_target_property(_value ${target} ${_property}) + message("${target} ${_property} = ${_value}") + endif() + endforeach() +endfunction() diff --git a/cmake/scripts/common/CheckCommits.cmake b/cmake/scripts/common/CheckCommits.cmake new file mode 100644 index 0000000..304e623 --- /dev/null +++ b/cmake/scripts/common/CheckCommits.cmake @@ -0,0 +1,75 @@ +find_package(Git REQUIRED) + +macro(sanity_check message) + if(status_code) + message(FATAL_ERROR "${message}") + endif() +endmacro() + +# Check that there are no changes in working-tree +execute_process(COMMAND ${GIT_EXECUTABLE} diff --quiet + RESULT_VARIABLE status_code) +sanity_check("Cannot run with working tree changes. Commit, stash or drop them.") + +# Setup base of tests +set(check_base $ENV{CHECK_BASE}) +if(NOT check_base) + set(check_base origin/master) +endif() + +# Setup end of tests +set(check_head $ENV{CHECK_HEAD}) +if(NOT check_head) + set(check_head HEAD) +endif() + +# Setup target to build +set(check_target $ENV{CHECK_TARGET}) +if(NOT check_target) + set(check_target check) +endif() + +# Build threads +set(build_threads $ENV{CHECK_THREADS}) +if(NOT build_threads) + if(UNIX) + execute_process(COMMAND nproc + OUTPUT_VARIABLE build_threads) + string(REGEX REPLACE "(\r?\n)+$" "" build_threads "${build_threads}") + endif() +endif() + +# Record current HEAD +execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + OUTPUT_VARIABLE current_branch) + +string(REGEX REPLACE "(\r?\n)+$" "" current_branch "${current_branch}") + +# Grab revision list +execute_process(COMMAND ${GIT_EXECUTABLE} rev-list ${check_base}..${check_head} --reverse + OUTPUT_VARIABLE rev_list) + +string(REPLACE "\n" ";" rev_list ${rev_list}) +foreach(rev ${rev_list}) + # Checkout + message("Testing revision ${rev}") + execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${rev} + RESULT_VARIABLE status_code) + sanity_check("Failed to checkout ${rev}") + + # Build + if(build_threads GREATER 2) + execute_process(COMMAND ${CMAKE_COMMAND} "--build" "${CMAKE_BINARY_DIR}" "--target" "${check_target}" "--use-stderr" "--" "-j${build_threads}" + RESULT_VARIABLE status_code) + else() + execute_process(COMMAND ${CMAKE_COMMAND} "--build" "${CMAKE_BINARY_DIR}" "--target" "${check_target}" "--use-stderr" + RESULT_VARIABLE status_code) + endif() + if(status_code) + execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${current_branch}) + endif() + sanity_check("Failed to build target for revision ${rev}") +endforeach() + +message("Everything checks out fine") +execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${current_branch}) diff --git a/cmake/scripts/common/CheckTargetPlatform.cmake b/cmake/scripts/common/CheckTargetPlatform.cmake new file mode 100644 index 0000000..29206c0 --- /dev/null +++ b/cmake/scripts/common/CheckTargetPlatform.cmake @@ -0,0 +1,70 @@ +# handle target platforms +function(check_target_platform dir target_platform build) + # param[in] dir path/directory of the addon/dependency + # param[in] target_platform target platform of the build + # param[out] build Result whether the addon/dependency should be built for the specified target platform + + set(${build} FALSE) + # check if the given directory exists and contains a platforms.txt + if(EXISTS ${dir} AND EXISTS ${dir}/platforms.txt) + # get all the specified platforms + file(STRINGS ${dir}/platforms.txt platforms) + + list( LENGTH platforms listlen ) + if(${listlen} EQUAL 1) + string(REPLACE " " ";" platforms ${platforms}) + endif() + + # check if the addon/dependency should be built for the current platform + foreach(platform ${platforms}) + if(${platform} STREQUAL "all" OR ${platform} STREQUAL ${target_platform}) + set(${build} TRUE) + else() + # check if the platform is defined as "!" + string(SUBSTRING ${platform} 0 1 platform_first) + if(${platform_first} STREQUAL "!") + # extract the platform + string(LENGTH ${platform} platform_length) + math(EXPR platform_length "${platform_length} - 1") + string(SUBSTRING ${platform} 1 ${platform_length} platform) + + # check if the current platform does not match the extracted platform + if(${platform} STREQUAL ${target_platform}) + set(${build} FALSE) + break() + elseif(NOT ${platform} STREQUAL ${target_platform}) + set(${build} TRUE) + endif() + endif() + endif() + endforeach() + else() + set(${build} TRUE) + endif() + + # make the ${build} variable available to the calling script + set(${build} "${${build}}" PARENT_SCOPE) +endfunction() + +function(check_install_permissions install_dir have_perms) + # param[in] install_dir directory to check for write permissions + # param[out] have_perms whether we have permissions to install to install_dir + + set(testfile_lib ${install_dir}/lib/kodi/.cmake-inst-test) + set(testfile_share ${install_dir}/share/kodi/.cmake-inst-test) + get_filename_component(testdir_lib ${testfile_lib} DIRECTORY) + get_filename_component(testdir_share ${testfile_share} DIRECTORY) + + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${testdir_lib}) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${testdir_share}) + execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${testfile_lib}) + execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${testfile_share}) + + if(EXISTS ${testfile_lib} AND EXISTS ${testfile_share}) + set(${have_perms} True PARENT_SCOPE) + else() + message(STATUS "check_install_permissions ${install_dir}: failed to create files") + set(${have_perms} False PARENT_SCOPE) + endif() + file(REMOVE ${testfile_lib} ${testfile_share}) +endfunction() diff --git a/cmake/scripts/common/CompilerSettings.cmake b/cmake/scripts/common/CompilerSettings.cmake new file mode 100644 index 0000000..bb0af92 --- /dev/null +++ b/cmake/scripts/common/CompilerSettings.cmake @@ -0,0 +1,5 @@ +# Languages and global compiler settings +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp") diff --git a/cmake/scripts/common/DependencyOptions.cmake b/cmake/scripts/common/DependencyOptions.cmake new file mode 100644 index 0000000..a45dcec --- /dev/null +++ b/cmake/scripts/common/DependencyOptions.cmake @@ -0,0 +1,23 @@ +# Set Option varname based on USE_INTERNAL_LIBS status +# +# Alternative to cmake_dependent_option +# cmake_dependent_option is restrictive, in the fact that we cannot override the +# set option value as a cache variable (-Dvar=foo) +# +# This allows us to have the same outcome as cmake_dependent_option whilst still allowing +# user to override for platforms that would normally be forced ON +# +function(dependent_option varname optionmessage) + + # If varname already set, accept that, as it was provided by the user + if(NOT DEFINED ${varname}) + # Generally we only define USE_INTERNAL_LIBS as the exception for platforms + # we explicitly dont want to build internal libs (eg Linux/Freebsd) + if(NOT DEFINED USE_INTERNAL_LIBS) + option(${varname} ${optionmessage} ON) + else() + # Respect Value of USE_INTERNAL_LIBS for ON/OFF + option(${varname} ${optionmessage} ${USE_INTERNAL_LIBS}) + endif() + endif() +endfunction() diff --git a/cmake/scripts/common/GenerateVersionedFiles.cmake b/cmake/scripts/common/GenerateVersionedFiles.cmake new file mode 100644 index 0000000..d54b524 --- /dev/null +++ b/cmake/scripts/common/GenerateVersionedFiles.cmake @@ -0,0 +1,35 @@ +include(${CORE_SOURCE_DIR}/cmake/scripts/common/Macros.cmake) + +core_find_versions() + +# configure_file without dependency tracking +# configure_file would register additional file dependencies that interfere +# with the ones from add_custom_command (and the generation would happen twice) +function(generate_versioned_file _SRC _DEST) + file(READ ${CORE_SOURCE_DIR}/${_SRC} file_content) + string(CONFIGURE "${file_content}" file_content @ONLY) + file(WRITE ${CMAKE_BINARY_DIR}/${_DEST} "${file_content}") +endfunction() + +# add-on xml's +file(GLOB ADDON_XML_IN_FILE ${CORE_SOURCE_DIR}/addons/*/addon.xml.in) + +# remove 'xbmc.json', will be created from 'xbmc/interfaces/json-rpc/schema/CMakeLists.txt' +list(REMOVE_ITEM ADDON_XML_IN_FILE ${CORE_SOURCE_DIR}/addons/xbmc.json/addon.xml.in) + +foreach(loop_var ${ADDON_XML_IN_FILE}) + list(GET loop_var 0 xml_name) + + string(REPLACE "/addon.xml.in" "" source_dir ${xml_name}) + string(REPLACE ${CORE_SOURCE_DIR} ${CMAKE_BINARY_DIR} dest_dir ${source_dir}) + file(MAKE_DIRECTORY ${dest_dir}) + + configure_file(${source_dir}/addon.xml.in ${dest_dir}/addon.xml @ONLY) + + unset(source_dir) + unset(dest_dir) + unset(xml_name) +endforeach() + + +generate_versioned_file(xbmc/CompileInfo.cpp.in ${CORE_BUILD_DIR}/xbmc/CompileInfo.cpp) diff --git a/cmake/scripts/common/GeneratorSetup.cmake b/cmake/scripts/common/GeneratorSetup.cmake new file mode 100644 index 0000000..304b504 --- /dev/null +++ b/cmake/scripts/common/GeneratorSetup.cmake @@ -0,0 +1,49 @@ +# Configure single-/multiconfiguration generators and variables +# +# CORE_BUILD_CONFIG that is set to +# - CMAKE_BUILD_TYPE for single configuration generators such as make, nmake +# - a variable that expands on build time to the current configuration for +# multi configuration generators such as VS or Xcode +if(CMAKE_CONFIGURATION_TYPES) + if(CMAKE_BUILD_TYPE) + message(FATAL_ERROR "CMAKE_BUILD_TYPE must not be defined for multi-configuration generators") + endif() + set(CORE_BUILD_CONFIG ${CMAKE_CFG_INTDIR}) + message(STATUS "Generator: Multi-configuration (${CMAKE_GENERATOR})") +else() + if(CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} + CACHE STRING "Choose build type (${CMAKE_BUILD_TYPES})" FORCE) + else() + # Set default + set(CMAKE_BUILD_TYPE Release + CACHE STRING "Choose build type (${CMAKE_BUILD_TYPES})" FORCE) + endif() + set(CORE_BUILD_CONFIG ${CMAKE_BUILD_TYPE}) + message(STATUS "Generator: Single-configuration: ${CMAKE_BUILD_TYPE} (${CMAKE_GENERATOR})") +endif() + +# Print CMake version +message(STATUS "CMake Version: ${CMAKE_VERSION}") + +# Deal with CMake special cases +if(CMAKE_VERSION VERSION_EQUAL 3.5.1) + message(WARNING "CMake 3.5.1 introduced a crash during configuration. " + "Please consider upgrading to 3.5.2 (cmake.org/Bug/view.php?id=16044)") +endif() + +# Darwin needs CMake 3.4 +if(APPLE AND CMAKE_VERSION VERSION_LESS 3.4) + message(WARNING "Build on Darwin requires CMake 3.4 or later (tdb library support) " + "or the usage of the patched version in depends.") +endif() + +# Windows needs CMake 3.6 (VS_STARTUP_PROJECT) +if(WIN32 AND CMAKE_VERSION VERSION_LESS 3.6) + message(FATAL_ERROR "Build on Windows needs CMake 3.6 or later") +endif() + +# Ninja needs CMake 3.2 due to ExternalProject BUILD_BYPRODUCTS usage +if(CMAKE_GENERATOR STREQUAL Ninja AND CMAKE_VERSION VERSION_LESS 3.2) + message(FATAL_ERROR "Generator: Ninja requires CMake 3.2 or later") +endif() diff --git a/cmake/scripts/common/HandleDepends.cmake b/cmake/scripts/common/HandleDepends.cmake new file mode 100644 index 0000000..dc022ba --- /dev/null +++ b/cmake/scripts/common/HandleDepends.cmake @@ -0,0 +1,301 @@ +include(${CORE_SOURCE_DIR}/cmake/scripts/common/CheckTargetPlatform.cmake) + +# handle addon depends +function(add_addon_depends addon searchpath) + # input: string addon string searchpath + + set(OUTPUT_DIR ${ADDON_DEPENDS_PATH}) + # look for platform-specific dependencies + file(GLOB_RECURSE cmake_input_files ${searchpath}/${CORE_SYSTEM_NAME}/*.txt) + # backward compatibility + if(NOT cmake_input_files AND CORE_SYSTEM_NAME STREQUAL windowsstore) + file(GLOB_RECURSE cmake_input_files ${searchpath}/windows/*.txt) + endif() + file(GLOB_RECURSE cmake_input_files2 ${searchpath}/common/*.txt) + list(APPEND cmake_input_files ${cmake_input_files2}) + + foreach(file ${cmake_input_files}) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) + if(NOT (file MATCHES CMakeLists.txt OR + file MATCHES install.txt OR + file MATCHES noinstall.txt OR + file MATCHES "flags.*[.]txt" OR + file MATCHES deps.txt OR + file MATCHES "[a-z]+-deps[.]txt" OR + file MATCHES platforms.txt)) + message(STATUS "Processing ${file}") + file(STRINGS ${file} def) + string(REPLACE " " ";" def ${def}) + list(LENGTH def deflength) + get_filename_component(dir ${file} DIRECTORY) + + # get the id of the dependency + if(NOT "${def}" STREQUAL "") + # read the id from the file + list(GET def 0 id) + else() + # read the id from the filename + get_filename_component(id ${file} NAME_WE) + endif() + + # check if the dependency has a platforms.txt + set(platform_found FALSE) + check_target_platform(${dir} ${CORE_SYSTEM_NAME} platform_found) + + if(${platform_found} AND NOT TARGET ${id}) + # determine the download URL of the dependency + set(url "") + if(deflength GREATER 1) + list(GET def 1 url) + message(STATUS "${id} url: ${url}") + endif() + + # check if there are any library specific flags that need to be passed on + if(EXISTS ${dir}/flags.txt) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/flags.txt) + file(STRINGS ${dir}/flags.txt extraflags) + string(REPLACE " " ";" extraflags ${extraflags}) + + message(STATUS "${id} extraflags: ${extraflags}") + endif() + + if(EXISTS ${dir}/flags-${CPU}.txt) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/flags-${CPU}.txt) + file(STRINGS ${dir}/flags-${CPU}.txt archextraflags) + string(REPLACE " " ";" archextraflags ${archextraflags}) + + message(STATUS "${id} ${CPU} extraflags: ${archextraflags}") + list(APPEND extraflags ${archextraflags}) + endif() + + set(BUILD_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} + -DOUTPUT_DIR=${OUTPUT_DIR} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_USER_MAKE_RULES_OVERRIDE=${CMAKE_USER_MAKE_RULES_OVERRIDE} + -DCMAKE_USER_MAKE_RULES_OVERRIDE_CXX=${CMAKE_USER_MAKE_RULES_OVERRIDE_CXX} + -DCMAKE_INSTALL_PREFIX=${OUTPUT_DIR} + -DCORE_SYSTEM_NAME=${CORE_SYSTEM_NAME} + -DENABLE_STATIC=1 + -DBUILD_SHARED_LIBS=0) + # windows args + if (CMAKE_SYSTEM_NAME STREQUAL WindowsStore OR CMAKE_SYSTEM_NAME STREQUAL Windows) + list(APPEND BUILD_ARGS -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}) + endif() + # if there are no make rules override files available take care of manually passing on ARCH_DEFINES + if(NOT CMAKE_USER_MAKE_RULES_OVERRIDE AND NOT CMAKE_USER_MAKE_RULES_OVERRIDE_CXX) + # make sure we create strings, not lists + set(TMP_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_DEFINES}") + set(TMP_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_DEFINES}") + set(TMP_EXE_LINKER_FLAGS "-L${OUTPUT_DIR}/lib ${CMAKE_EXE_LINKER_FLAGS}") + list(APPEND BUILD_ARGS -DCMAKE_C_FLAGS=${TMP_C_FLAGS} + -DCMAKE_CXX_FLAGS=${TMP_CXX_FLAGS} + -DCMAKE_EXE_LINKER_FLAGS=${TMP_EXE_LINKER_FLAGS}) + endif() + + if(CMAKE_TOOLCHAIN_FILE) + list(APPEND BUILD_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) + message("toolchain specified") + message(${BUILD_ARGS}) + endif() + + if(ADDON_EXTRA_ARGS) + string(REPLACE " " ";" ADDON_EXTRA_ARGS ${ADDON_EXTRA_ARGS}) + list(APPEND BUILD_ARGS ${ADDON_EXTRA_ARGS}) + message("Addon Extra Args: ${ADDON_EXTRA_ARGS}") + endif() + + # used for addons where need special folders to store there content (if + # not set the addon define it byself). + # e.g. Google Chromium addon where his git bring: + # - "unable to create file" ... "Filename too long" + # see also WARNING by Windows on: https://bitbucket.org/chromiumembedded/cef/wiki/MasterBuildQuickStart + if(THIRD_PARTY_PATH) + message(STATUS "Third party lib path specified") + message(STATUS ${THIRD_PARTY_PATH}) + list(APPEND BUILD_ARGS -DTHIRD_PARTY_PATH=${THIRD_PARTY_PATH}) + endif() + + set(PATCH_COMMAND) + + # if there's a CMakeLists.txt use it to prepare the build + if(EXISTS ${dir}/CMakeLists.txt) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/CMakeLists.txt) + list(APPEND PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy_if_different ${dir}/CMakeLists.txt ${BUILD_DIR}/${id}/src/${id}) + endif() + + # check if we have patches to apply + file(GLOB patches ${dir}/*.patch) + list(SORT patches) + foreach(patch ${patches}) + if(NOT PATCH_PROGRAM OR "${PATCH_PROGRAM}" STREQUAL "") + if(NOT PATCH_EXECUTABLE) + # find the path to the patch executable + + if(WIN32) + # On Windows prioritize Git patch.exe + find_package(Git) + if(Git_FOUND) + get_filename_component(GIT_DIR ${GIT_EXECUTABLE} DIRECTORY) + get_filename_component(GIT_DIR ${GIT_DIR} DIRECTORY) + endif() + find_program(PATCH_EXECUTABLE NAMES patch.exe HINTS ${GIT_DIR} PATH_SUFFIXES usr/bin) + else() + find_program(PATCH_EXECUTABLE NAMES patch) + endif() + if(NOT PATCH_EXECUTABLE) + message(FATAL_ERROR "Missing patch command (we looked in ${CMAKE_PREFIX_PATH})") + endif() + endif() + + set(PATCH_PROGRAM ${PATCH_EXECUTABLE}) + + # 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. + if(WIN32) + file(READ ${patch} patch_content_hex HEX) + # Force handle LF-only line endings + if(NOT patch_content_hex MATCHES "0d0a") + list(APPEND PATCH_PROGRAM --binary) + endif() + endif() + endif() + + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${patch}) + list(APPEND PATCH_COMMAND COMMAND ${PATCH_PROGRAM} -p1 -i ${patch}) + endforeach() + + + # if there's an install.txt use it to properly install the built files + set(INSTALL_COMMAND "") + if(EXISTS ${dir}/install.txt) + set(INSTALL_COMMAND INSTALL_COMMAND ${CMAKE_COMMAND} + -DINPUTDIR=${BUILD_DIR}/${id}/src/${id}-build/ + -DINPUTFILE=${dir}/install.txt + -DDESTDIR=${OUTPUT_DIR} + -DENABLE_STATIC=1 + "${extraflags}" + -P ${PROJECT_SOURCE_DIR}/install.cmake) + elseif(EXISTS ${dir}/noinstall.txt) + set(INSTALL_COMMAND INSTALL_COMMAND "") + endif() + + # check if there's a platform-specific or generic deps.txt containing dependencies on other libraries + if(EXISTS ${dir}/${CORE_SYSTEM_NAME}-deps.txt) + file(STRINGS ${dir}/${CORE_SYSTEM_NAME}-deps.txt deps) + message(STATUS "${id} depends: ${deps}") + # backward compatibility + elseif(CORE_SYSTEM_NAME STREQUAL windowsstore AND EXISTS ${dir}/windows-deps.txt) + file(STRINGS ${dir}/windows-deps.txt deps) + message(STATUS "${id} depends: ${deps}") + elseif(EXISTS ${dir}/deps.txt) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/deps.txt) + file(STRINGS ${dir}/deps.txt deps) + message(STATUS "${id} depends: ${deps}") + else() + set(deps) + endif() + + if(CROSS_AUTOCONF AND AUTOCONF_FILES) + foreach(afile ${AUTOCONF_FILES}) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${afile}) + list(APPEND PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E echo "AUTOCONF: copying ${afile} to ${BUILD_DIR}/${id}/src/${id}") + list(APPEND PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy_if_different ${afile} ${BUILD_DIR}/${id}/src/${id}) + endforeach() + endif() + + # prepare the setup of the call to externalproject_add() + set(EXTERNALPROJECT_SETUP PREFIX ${BUILD_DIR}/${id} + CMAKE_ARGS ${extraflags} ${BUILD_ARGS} + PATCH_COMMAND ${PATCH_COMMAND} + ${INSTALL_COMMAND}) + + if(CMAKE_VERSION VERSION_GREATER 3.5.9) + list(APPEND EXTERNALPROJECT_SETUP GIT_SHALLOW 1) + endif() + + # if there's an url defined we need to pass that to externalproject_add() + if(DEFINED url AND NOT "${url}" STREQUAL "") + # check if there's a third parameter in the file + if(deflength GREATER 2) + # the third parameter is considered as a revision of a git repository + list(GET def 2 revision) + + externalproject_add(${id} + GIT_REPOSITORY ${url} + GIT_TAG ${revision} + ${EXTERNALPROJECT_SETUP}) + + # For patchfiles to work, disable (users globally set) autocrlf=true + if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_GREATER 3.7) + message(AUTHOR_WARNING "Make use of GIT_CONFIG") + endif() + if(WIN32 AND patches) + externalproject_add_step(${id} gitconfig + COMMAND git config core.autocrlf false + COMMAND git rm -rf --cached . + COMMAND git reset --hard HEAD + COMMENT "Performing gitconfig step: Disabling autocrlf to enable patching for '${id}'" + DEPENDERS patch + WORKING_DIRECTORY ) + endif() + else() + set(CONFIGURE_COMMAND "") + if(NOT WIN32) + # manually specify the configure command to be able to pass in the custom PKG_CONFIG_PATH + set(CONFIGURE_COMMAND PKG_CONFIG_PATH=${OUTPUT_DIR}/lib/pkgconfig + ${CMAKE_COMMAND} -DCMAKE_LIBRARY_PATH=${OUTPUT_DIR}/lib ${extraflags} ${BUILD_ARGS} + ${BUILD_DIR}/${id}/src/${id} + -DPACKAGE_CONFIG_PATH=${OUTPUT_DIR}/lib/pkgconfig + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DOUTPUT_DIR=${OUTPUT_DIR} + -DCMAKE_PREFIX_PATH=${OUTPUT_DIR} + -DCMAKE_INSTALL_PREFIX=${OUTPUT_DIR} + -DCMAKE_INCLUDE_PATH=${OUTPUT_DIR}/include) + endif() + + set(DOWNLOAD_DIR ${BUILD_DIR}/download) + if(EXISTS ${dir}/${id}.sha256) + file(STRINGS ${dir}/${id}.sha256 sha256sum) + list(GET sha256sum 0 sha256sum) + set(URL_HASH_COMMAND URL_HASH SHA256=${sha256sum}) + if(TARBALL_DIR) + set(DOWNLOAD_DIR ${TARBALL_DIR}) + endif() + else() + unset(URL_HASH_COMMAND) + message(AUTHOR_WARNING "${dir}/${id}.sha256 is missing") + endif() + + externalproject_add(${id} + URL ${url} + "${URL_HASH_COMMAND}" + DOWNLOAD_DIR ${DOWNLOAD_DIR} + CONFIGURE_COMMAND ${CONFIGURE_COMMAND} + ${EXTERNALPROJECT_SETUP}) + endif() + else() + externalproject_add(${id} + SOURCE_DIR ${dir} + ${EXTERNALPROJECT_SETUP}) + endif() + + if(deps) + add_dependencies(${id} ${deps}) + endif() + endif() + + # if the dependency is available for the target platform add it to the list of the addon's dependencies + # (even if the target already exists as it still has to be built before the addon) + if(${platform_found}) + list(APPEND ${addon}_DEPS ${id}) + endif() + endif() + endforeach() + + # make the ${addon}_DEPS variable available to the calling script + set(${addon}_DEPS "${${addon}_DEPS}" PARENT_SCOPE) +endfunction() + diff --git a/cmake/scripts/common/Macros.cmake b/cmake/scripts/common/Macros.cmake new file mode 100644 index 0000000..49198a1 --- /dev/null +++ b/cmake/scripts/common/Macros.cmake @@ -0,0 +1,789 @@ +# This script holds the main functions used to construct the build system + +# Include system specific macros but only if this file is included from +# kodi main project. It's not needed for kodi-addons project +# If CORE_SOURCE_DIR is set, it was called from kodi-addons project +# TODO: drop check if we ever integrate kodi-addons into kodi project +if(NOT CORE_SOURCE_DIR) + include(${CMAKE_SOURCE_DIR}/cmake/scripts/${CORE_SYSTEM_NAME}/Macros.cmake) +endif() + +# IDEs: Group source files in target in folders (file system hierarchy) +# Source: http://blog.audio-tk.com/2015/09/01/sorting-source-files-and-projects-in-folders-with-cmake-and-visual-studioxcode/ +# Arguments: +# target The target that shall be grouped by folders. +# Optional Arguments: +# RELATIVE allows to specify a different reference folder. +function(source_group_by_folder target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "There is no target named '${target}'") + endif() + + set(SOURCE_GROUP_DELIMITER "/") + + cmake_parse_arguments(arg "" "RELATIVE" "" ${ARGN}) + if(arg_RELATIVE) + set(relative_dir ${arg_RELATIVE}) + else() + set(relative_dir ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + get_property(files TARGET ${target} PROPERTY SOURCES) + if(files) + list(SORT files) + + if(CMAKE_GENERATOR STREQUAL Xcode) + set_target_properties(${target} PROPERTIES SOURCES "${files}") + endif() + endif() + foreach(file ${files}) + if(NOT IS_ABSOLUTE ${file}) + set(file ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + endif() + file(RELATIVE_PATH relative_file ${relative_dir} ${file}) + get_filename_component(dir "${relative_file}" DIRECTORY) + if(NOT dir STREQUAL "${last_dir}") + if(files) + source_group("${last_dir}" FILES ${files}) + endif() + set(files "") + endif() + set(files ${files} ${file}) + set(last_dir "${dir}") + endforeach(file) + if(files) + source_group("${last_dir}" FILES ${files}) + endif() +endfunction() + +# Add sources to main application +# Arguments: +# name name of the library to add +# Implicit arguments: +# ENABLE_STATIC_LIBS Build static libraries per directory +# SOURCES the sources of the library +# HEADERS the headers of the library (only for IDE support) +# OTHERS other library related files (only for IDE support) +# On return: +# Library will be built, optionally added to ${core_DEPENDS} +# Sets CORE_LIBRARY for calls for setting target specific options +function(core_add_library name) + if(ENABLE_STATIC_LIBS) + add_library(${name} STATIC ${SOURCES} ${HEADERS} ${OTHERS}) + set_target_properties(${name} PROPERTIES PREFIX "") + set(core_DEPENDS ${name} ${core_DEPENDS} CACHE STRING "" FORCE) + add_dependencies(${name} ${GLOBAL_TARGET_DEPS}) + set(CORE_LIBRARY ${name} PARENT_SCOPE) + + if(NOT MSVC) + target_compile_options(${name} PUBLIC ${CORE_COMPILE_OPTIONS}) + endif() + + # Add precompiled headers to Kodi main libraries + if(CORE_SYSTEM_NAME MATCHES windows) + add_precompiled_header(${name} pch.h ${CMAKE_SOURCE_DIR}/xbmc/platform/win32/pch.cpp PCH_TARGET kodi) + set_language_cxx(${name}) + target_link_libraries(${name} PUBLIC effects11) + endif() + else() + foreach(src IN LISTS SOURCES HEADERS OTHERS) + get_filename_component(src_path "${src}" ABSOLUTE) + list(APPEND FILES ${src_path}) + endforeach() + target_sources(lib${APP_NAME_LC} PRIVATE ${FILES}) + set(CORE_LIBRARY lib${APP_NAME_LC} PARENT_SCOPE) + endif() +endfunction() + +# Add a test library, and add sources to list for gtest integration macros +function(core_add_test_library name) + if(ENABLE_STATIC_LIBS) + add_library(${name} STATIC ${SOURCES} ${SUPPORTED_SOURCES} ${HEADERS} ${OTHERS}) + set_target_properties(${name} PROPERTIES PREFIX "" + EXCLUDE_FROM_ALL 1 + FOLDER "Build Utilities/tests") + add_dependencies(${name} ${GLOBAL_TARGET_DEPS}) + set(test_archives ${test_archives} ${name} CACHE STRING "" FORCE) + + if(NOT MSVC) + target_compile_options(${name} PUBLIC ${CORE_COMPILE_OPTIONS}) + endif() + + endif() + foreach(src IN LISTS SOURCES SUPPORTED_SOURCES HEADERS OTHERS) + get_filename_component(src_path "${src}" ABSOLUTE) + set(test_sources "${src_path}" ${test_sources} CACHE STRING "" FORCE) + endforeach() +endfunction() + +# Add addon dev kit headers to main application +# Arguments: +# name name of the header part to add +function(core_add_devkit_header name) + if(NOT ENABLE_STATIC_LIBS) + core_add_library(addons_kodi-dev-kit_include_${name}) + endif() +endfunction() + +# Add an dl-loaded shared library +# Arguments: +# name name of the library to add +# Optional arguments: +# WRAPPED wrap this library on POSIX platforms to add VFS support for +# libraries that would otherwise not support it. +# OUTPUT_DIRECTORY where to create the library in the build dir +# (default: system) +# Implicit arguments: +# SOURCES the sources of the library +# HEADERS the headers of the library (only for IDE support) +# OTHERS other library related files (only for IDE support) +# On return: +# Library target is defined and added to LIBRARY_FILES +function(core_add_shared_library name) + cmake_parse_arguments(arg "WRAPPED" "OUTPUT_DIRECTORY" "" ${ARGN}) + if(arg_OUTPUT_DIRECTORY) + set(OUTPUT_DIRECTORY ${arg_OUTPUT_DIRECTORY}) + else() + if(NOT CORE_SYSTEM_NAME STREQUAL windows) + set(OUTPUT_DIRECTORY system) + endif() + endif() + if(CORE_SYSTEM_NAME STREQUAL windows) + set(OUTPUT_NAME lib${name}) + else() + set(OUTPUT_NAME lib${name}-${ARCH}) + endif() + + if(NOT arg_WRAPPED OR CORE_SYSTEM_NAME STREQUAL windows) + add_library(${name} SHARED ${SOURCES} ${HEADERS} ${OTHERS}) + set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${OUTPUT_DIRECTORY} + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${OUTPUT_DIRECTORY} + OUTPUT_NAME ${OUTPUT_NAME} PREFIX "") + foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) + set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUT_DIRECTORY} + RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${OUTPUT_DIRECTORY}) + endforeach() + + set(LIBRARY_FILES ${LIBRARY_FILES} ${CMAKE_BINARY_DIR}/${OUTPUT_DIRECTORY}/${OUTPUT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} CACHE STRING "" FORCE) + add_dependencies(${APP_NAME_LC}-libraries ${name}) + else() + add_library(${name} STATIC ${SOURCES} ${HEADERS} ${OTHERS}) + set_target_properties(${name} PROPERTIES POSITION_INDEPENDENT_CODE 1) + core_link_library(${name} ${OUTPUT_DIRECTORY}/lib${name}) + + if(NOT MSVC) + target_compile_options(${name} PUBLIC ${CORE_COMPILE_OPTIONS}) + endif() + endif() +endfunction() + +# Sets the compile language for all C source files in a target to CXX. +# Needs to be called from the CMakeLists.txt that defines the target. +# Arguments: +# target target +function(set_language_cxx target) + get_property(sources TARGET ${target} PROPERTY SOURCES) + foreach(file IN LISTS sources) + if(file MATCHES "\.c$") + set_source_files_properties(${file} PROPERTIES LANGUAGE CXX) + endif() + endforeach() +endfunction() + +# Add a data file to installation list with a mirror in build tree +# Mirroring files in the buildtree allows to execute the app from there. +# Arguments: +# file full path to file to mirror +# Optional Arguments: +# NO_INSTALL: exclude file from installation target (only mirror) +# DIRECTORY: directory where the file should be mirrored to +# (default: preserve tree structure relative to CMAKE_SOURCE_DIR) +# KEEP_DIR_STRUCTURE: preserve tree structure even when DIRECTORY is set +# On return: +# Files is mirrored to the build tree and added to ${install_data} +# (if NO_INSTALL is not given). +function(copy_file_to_buildtree file) + cmake_parse_arguments(arg "NO_INSTALL" "DIRECTORY;KEEP_DIR_STRUCTURE" "" ${ARGN}) + if(arg_DIRECTORY) + set(outdir ${arg_DIRECTORY}) + if(arg_KEEP_DIR_STRUCTURE) + get_filename_component(srcdir ${arg_KEEP_DIR_STRUCTURE} DIRECTORY) + string(REPLACE "${CMAKE_SOURCE_DIR}/${srcdir}/" "" outfile ${file}) + if(NOT IS_DIRECTORY ${file}) + set(outdir ${outdir}/${outfile}) + endif() + else() + get_filename_component(outfile ${file} NAME) + set(outfile ${outdir}/${outfile}) + endif() + else() + string(REPLACE "${CMAKE_SOURCE_DIR}/" "" outfile ${file}) + get_filename_component(outdir ${outfile} DIRECTORY) + endif() + + if(NOT TARGET export-files) + file(REMOVE ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake) + add_custom_target(export-files ALL COMMENT "Copying files into build tree" + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake) + set_target_properties(export-files PROPERTIES FOLDER "Build Utilities") + file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake "# Export files to build tree\n") + endif() + + # Exclude autotools build artefacts and other blacklisted files in source tree. + if(file MATCHES "(Makefile|\\.in|\\.xbt|\\.so|\\.dylib|\\.gitignore)$") + if(VERBOSE) + message(STATUS "copy_file_to_buildtree - ignoring file: ${file}") + endif() + return() + endif() + + if(NOT file STREQUAL ${CMAKE_BINARY_DIR}/${outfile}) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows" OR NOT IS_SYMLINK "${file}") + if(VERBOSE) + message(STATUS "copy_file_to_buildtree - copying file: ${file} -> ${CMAKE_BINARY_DIR}/${outfile}") + endif() + file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake + "file(COPY \"${file}\" DESTINATION \"${CMAKE_BINARY_DIR}/${outdir}\")\n" ) + else() + if(VERBOSE) + message(STATUS "copy_file_to_buildtree - copying symlinked file: ${file} -> ${CMAKE_BINARY_DIR}/${outfile}") + endif() + file(APPEND ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/ExportFiles.cmake + "execute_process(COMMAND \"\${CMAKE_COMMAND}\" -E copy_if_different \"${file}\" \"${CMAKE_BINARY_DIR}/${outfile}\")\n") + endif() + endif() + + if(NOT arg_NO_INSTALL) + list(APPEND install_data ${outfile}) + set(install_data ${install_data} PARENT_SCOPE) + endif() +endfunction() + +# Add data files to installation list with a mirror in build tree. +# reads list of files to install from a given list of text files. +# Arguments: +# pattern globbing pattern for text files to read +# Optional Arguments: +# NO_INSTALL: exclude files from installation target +# Implicit arguments: +# CMAKE_SOURCE_DIR - root of source tree +# On return: +# Files are mirrored to the build tree and added to ${install_data} +# (if NO_INSTALL is not given). +function(copy_files_from_filelist_to_buildtree pattern) + # copies files listed in text files to the buildtree + # Input: [glob pattern: filepattern] + cmake_parse_arguments(arg "NO_INSTALL" "" "" ${ARGN}) + list(APPEND pattern ${ARGN}) + list(SORT pattern) + if(VERBOSE) + message(STATUS "copy_files_from_filelist_to_buildtree - got pattern: ${pattern}") + endif() + foreach(pat ${pattern}) + file(GLOB filenames ${pat}) + foreach(filename ${filenames}) + string(STRIP ${filename} filename) + core_file_read_filtered(fstrings ${filename}) + foreach(dir ${fstrings}) + string(CONFIGURE ${dir} dir) + string(REPLACE " " ";" dir ${dir}) + list(GET dir 0 src) + list(LENGTH dir len) + if(len EQUAL 1) + set(dest) + elseif(len EQUAL 3) + list(GET dir 1 opt) + if(opt STREQUAL "KEEP_DIR_STRUCTURE") + set(DIR_OPTION ${opt} ${src}) + if(VERBOSE) + message(STATUS "copy_files_from_filelist_to_buildtree - DIR_OPTION: ${DIR_OPTION}") + endif() + endif() + list(GET dir -1 dest) + else() + list(GET dir -1 dest) + endif() + + # If the full path to an existing file is specified then add that single file. + # Don't recursively add all files with the given name. + if(EXISTS ${CMAKE_SOURCE_DIR}/${src} AND (NOT IS_DIRECTORY ${CMAKE_SOURCE_DIR}/${src} OR DIR_OPTION)) + set(files ${src}) + else() + file(GLOB_RECURSE files RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/${src}) + endif() + + foreach(file ${files}) + if(arg_NO_INSTALL) + copy_file_to_buildtree(${CMAKE_SOURCE_DIR}/${file} DIRECTORY ${dest} NO_INSTALL ${DIR_OPTION}) + else() + copy_file_to_buildtree(${CMAKE_SOURCE_DIR}/${file} DIRECTORY ${dest} ${DIR_OPTION}) + endif() + endforeach() + set(DIR_OPTION) + endforeach() + endforeach() + endforeach() + set(install_data ${install_data} PARENT_SCOPE) +endfunction() + +# helper macro to set modified variables in parent scope +macro(export_dep) + set(SYSTEM_INCLUDES ${SYSTEM_INCLUDES} PARENT_SCOPE) + set(DEPLIBS ${DEPLIBS} PARENT_SCOPE) + set(DEP_DEFINES ${DEP_DEFINES} PARENT_SCOPE) + set(${depup}_FOUND ${${depup}_FOUND} PARENT_SCOPE) + mark_as_advanced(${depup}_LIBRARIES) +endmacro() + +# split dependency specification to name and version +# Arguments: +# depspec dependency specification that can optionally include a required +# package version +# syntax: [package name], [package name]>=[version] (minimum version), +# or [package name]=[version] (exact version) +# name_outvar variable that should receive the package name +# version_outvar variable that should receive the package version part (>=[version]) +# On return: +# ${name_outvar} and ${version_outvar} in caller scope are set to respective values. +# ${version_outvar} may be unset if there is no specific version requested. +function(split_dependency_specification depspec name_outvar version_outvar) + if(${depspec} MATCHES "^([^>]*)(>?=[0-9.]+)$") + set(${name_outvar} ${CMAKE_MATCH_1} PARENT_SCOPE) + set(${version_outvar} ${CMAKE_MATCH_2} PARENT_SCOPE) + else() + set(${name_outvar} ${depspec} PARENT_SCOPE) + unset(${version_outvar} PARENT_SCOPE) + endif() +endfunction() + +# helper macro to split version info from req and call find_package +macro(find_package_with_ver package) + set(_find_arguments "${ARGN}") + if("${ARGV1}" MATCHES "^(>)?=([0-9.]+)$") + # We have a version spec, parse it + list(REMOVE_AT _find_arguments 0) + # ">" not present? -> exact match + if(NOT CMAKE_MATCH_1) + list(INSERT _find_arguments 0 "EXACT") + endif() + find_package(${package} ${CMAKE_MATCH_2} ${_find_arguments}) + else() + find_package(${package} ${_find_arguments}) + endif() + unset(_find_arguments) +endmacro() + +# add required dependencies of main application +# Arguments: +# dep_list One or many dependency specifications (see split_dependency_specification) +# for syntax). The dependency name is used uppercased as variable prefix. +# On return: +# dependencies added to ${SYSTEM_INCLUDES}, ${DEPLIBS} and ${DEP_DEFINES} +function(core_require_dep) + foreach(depspec ${ARGN}) + split_dependency_specification(${depspec} dep version) + find_package_with_ver(${dep} ${version} REQUIRED) + string(TOUPPER ${dep} depup) + list(APPEND SYSTEM_INCLUDES ${${depup}_INCLUDE_DIRS}) + list(APPEND DEPLIBS ${${depup}_LIBRARIES}) + list(APPEND DEP_DEFINES ${${depup}_DEFINITIONS}) + export_dep() + endforeach() +endfunction() + +# helper macro for optional deps +macro(setup_enable_switch) + string(TOUPPER ${dep} depup) + if(${ARGV1}) + set(enable_switch ${ARGV1}) + else() + set(enable_switch ENABLE_${depup}) + endif() + # normal options are boolean, so we override set our ENABLE_FOO var to allow "auto" handling + set(${enable_switch} "AUTO" CACHE STRING "Enable ${depup} support?") +endmacro() + +# add optional dependencies of main application +# Arguments: +# dep_list One or many dependency specifications (see split_dependency_specification) +# for syntax). The dependency name is used uppercased as variable prefix. +# On return: +# dependency optionally added to ${SYSTEM_INCLUDES}, ${DEPLIBS} and ${DEP_DEFINES} +function(core_optional_dep) + foreach(depspec ${ARGN}) + set(_required False) + split_dependency_specification(${depspec} dep version) + setup_enable_switch() + if(${enable_switch} STREQUAL AUTO) + find_package_with_ver(${dep} ${version}) + elseif(${${enable_switch}}) + find_package_with_ver(${dep} ${version} REQUIRED) + set(_required True) + endif() + + if(${depup}_FOUND) + list(APPEND SYSTEM_INCLUDES ${${depup}_INCLUDE_DIRS}) + list(APPEND DEPLIBS ${${depup}_LIBRARIES}) + list(APPEND DEP_DEFINES ${${depup}_DEFINITIONS}) + set(final_message ${final_message} "${depup} enabled: Yes") + export_dep() + elseif(_required) + message(FATAL_ERROR "${depup} enabled but not found") + else() + set(final_message ${final_message} "${depup} enabled: No") + endif() + endforeach() + set(final_message ${final_message} PARENT_SCOPE) +endfunction() + +function(core_file_read_filtered result filepattern) + # Reads STRINGS from text files + # with comments filtered out + # Result: [list: result] + # Input: [glob pattern: filepattern] + file(GLOB filenames ${filepattern}) + list(SORT filenames) + foreach(filename ${filenames}) + if(VERBOSE) + message(STATUS "core_file_read_filtered - filename: ${filename}") + endif() + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${filename}) + file(STRINGS ${filename} fstrings REGEX "^[^#//]") + foreach(fstring ${fstrings}) + string(REGEX REPLACE "^(.*)#(.*)" "\\1" fstring ${fstring}) + string(REGEX REPLACE "[ \n\r\t]//.*" "" fstring ${fstring}) + string(STRIP ${fstring} fstring) + list(APPEND filename_strings ${fstring}) + endforeach() + endforeach() + set(${result} ${filename_strings} PARENT_SCOPE) +endfunction() + +function(core_add_subdirs_from_filelist files) + # Adds subdirectories from a sorted list of files + # Input: [list: filenames] [bool: sort] + foreach(arg ${ARGN}) + list(APPEND files ${arg}) + endforeach() + list(SORT files) + if(VERBOSE) + message(STATUS "core_add_subdirs_from_filelist - got pattern: ${files}") + endif() + foreach(filename ${files}) + string(STRIP ${filename} filename) + core_file_read_filtered(fstrings ${filename}) + foreach(subdir ${fstrings}) + string(REPLACE " " ";" subdir ${subdir}) + list(GET subdir 0 subdir_src) + list(GET subdir -1 subdir_dest) + if(VERBOSE) + message(STATUS " core_add_subdirs_from_filelist - adding subdir: ${CMAKE_SOURCE_DIR}/${subdir_src} -> ${CORE_BUILD_DIR}/${subdir_dest}") + endif() + add_subdirectory(${CMAKE_SOURCE_DIR}/${subdir_src} ${CORE_BUILD_DIR}/${subdir_dest}) + endforeach() + endforeach() +endfunction() + +macro(core_add_optional_subdirs_from_filelist pattern) + # Adds subdirectories from text files + # if the option(s) in the 3rd field are enabled + # Input: [glob pattern: filepattern] + foreach(arg ${ARGN}) + list(APPEND pattern ${arg}) + endforeach() + foreach(elem ${pattern}) + string(STRIP ${elem} elem) + list(APPEND filepattern ${elem}) + endforeach() + + file(GLOB filenames ${filepattern}) + list(SORT filenames) + if(VERBOSE) + message(STATUS "core_add_optional_subdirs_from_filelist - got pattern: ${filenames}") + endif() + + foreach(filename ${filenames}) + if(VERBOSE) + message(STATUS "core_add_optional_subdirs_from_filelist - reading file: ${filename}") + endif() + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${filename}) + file(STRINGS ${filename} fstrings REGEX "^[^#//]") + foreach(line ${fstrings}) + string(REPLACE " " ";" line "${line}") + list(GET line 0 subdir_src) + list(GET line 1 subdir_dest) + list(GET line 3 opts) + foreach(opt ${opts}) + if(ENABLE_${opt}) + if(VERBOSE) + message(STATUS " core_add_optional_subdirs_from_filelist - adding subdir: ${CMAKE_SOURCE_DIR}/${subdir_src} -> ${CORE_BUILD_DIR}/${subdir_dest}") + endif() + add_subdirectory(${CMAKE_SOURCE_DIR}/${subdir_src} ${CORE_BUILD_DIR}/${subdir_dest}) + else() + if(VERBOSE) + message(STATUS " core_add_optional_subdirs_from_filelist: OPTION ${opt} not enabled for ${subdir_src}, skipping subdir") + endif() + endif() + endforeach() + endforeach() + endforeach() +endmacro() + +# Generates an RFC2822 timestamp +# +# The following variable is set: +# RFC2822_TIMESTAMP +function(rfc2822stamp) + execute_process(COMMAND date -R + OUTPUT_VARIABLE RESULT) + set(RFC2822_TIMESTAMP ${RESULT} PARENT_SCOPE) +endfunction() + +# Generates an user stamp from git config info +# +# The following variable is set: +# PACKAGE_MAINTAINER - user stamp in the form of "username " +# if no git tree is found, value is set to "nobody " +function(userstamp) + find_package(Git) + if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) + execute_process(COMMAND ${GIT_EXECUTABLE} config user.name + OUTPUT_VARIABLE username + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT_EXECUTABLE} config user.email + OUTPUT_VARIABLE useremail + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PACKAGE_MAINTAINER "${username} <${useremail}>" PARENT_SCOPE) + else() + set(PACKAGE_MAINTAINER "nobody " PARENT_SCOPE) + endif() +endfunction() + +# Parses git info and sets variables used to identify the build +# Arguments: +# stamp variable name to return +# Optional Arguments: +# FULL: generate git HEAD commit in the form of 'YYYYMMDD-hash' +# if git tree is dirty, value is set in the form of 'YYYYMMDD-hash-dirty' +# if no git tree is found, value is set in the form of 'YYYYMMDD-nogitfound' +# if FULL is not given, stamp is generated following the same process as above +# but without 'YYYYMMDD' +# On return: +# Variable is set with generated stamp to PARENT_SCOPE +function(core_find_git_rev stamp) + # allow manual setting GIT_VERSION + if(GIT_VERSION) + set(${stamp} ${GIT_VERSION} PARENT_SCOPE) + string(TIMESTAMP APP_BUILD_DATE "%Y%m%d" UTC) + set(APP_BUILD_DATE ${APP_BUILD_DATE} PARENT_SCOPE) + else() + find_package(Git) + if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) + # get tree status i.e. clean working tree vs dirty (uncommitted or unstashed changes, etc.) + execute_process(COMMAND ${GIT_EXECUTABLE} update-index --ignore-submodules -q --refresh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND ${GIT_EXECUTABLE} diff-files --ignore-submodules --quiet -- + RESULT_VARIABLE status_code + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + if(NOT status_code) + execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --ignore-submodules --quiet HEAD -- + RESULT_VARIABLE status_code + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + endif() + # get HEAD commit SHA-1 + execute_process(COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=format:"%h" HEAD + OUTPUT_VARIABLE HASH + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(REPLACE "\"" "" HASH ${HASH}) + + if(status_code) + string(CONCAT HASH ${HASH} "-dirty") + endif() + + # get HEAD commit date + execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:"%cd" --date=short HEAD + OUTPUT_VARIABLE DATE + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(REPLACE "\"" "" DATE ${DATE}) + string(REPLACE "-" "" DATE ${DATE}) + + # build date + string(TIMESTAMP APP_BUILD_DATE "%Y%m%d" UTC) + set(APP_BUILD_DATE ${APP_BUILD_DATE} PARENT_SCOPE) + else() + if(EXISTS ${CMAKE_SOURCE_DIR}/BUILDDATE) + file(STRINGS ${CMAKE_SOURCE_DIR}/BUILDDATE DATE LIMIT_INPUT 8) + else() + string(TIMESTAMP DATE "%Y%m%d" UTC) + endif() + set(APP_BUILD_DATE ${DATE} PARENT_SCOPE) + + if(EXISTS ${CMAKE_SOURCE_DIR}/VERSION) + file(STRINGS ${CMAKE_SOURCE_DIR}/VERSION HASH LIMIT_INPUT 16) + else() + set(HASH "nogitfound") + endif() + endif() + cmake_parse_arguments(arg "FULL" "" "" ${ARGN}) + if(arg_FULL) + set(${stamp} ${DATE}-${HASH} PARENT_SCOPE) + else() + set(${stamp} ${HASH} PARENT_SCOPE) + endif() + endif() +endfunction() + +# Parses version.txt and versions.h and sets variables +# used to construct dirs structure, file naming, API version, etc. +# +# The following variables are set from version.txt: +# APP_NAME - app name +# APP_NAME_LC - lowercased app name +# APP_NAME_UC - uppercased app name +# APP_PACKAGE - Android full package name +# COMPANY_NAME - company name +# APP_WEBSITE - site url +# APP_VERSION_MAJOR - the app version major +# APP_VERSION_MINOR - the app version minor +# APP_VERSION_TAG - the app version tag +# APP_VERSION_TAG_LC - lowercased app version tag +# APP_VERSION - the app version (${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}-${APP_VERSION_TAG}) +# APP_ADDON_API - the addon API version in the form of 16.9.702 +# ADDON_REPOS - official addon repositories and their origin path delimited by pipe +# - e.g. repository.xbmc.org|https://mirrors.kodi.tv - +# (multiple repo/path-sets are delimited by comma) +# FILE_VERSION - file version in the form of 16,9,702,0 - Windows only +# JSONRPC_VERSION - the json api version in the form of 8.3.0 +# +# Set various variables defined in "versions.h" +macro(core_find_versions) + # kodi-addons project also calls this macro and uses CORE_SOURCE_DIR + # to point to core base dir + # Set CORE_SOURCE_DIR here, otherwise kodi main project fails + # TODO: drop this code block and refactor the rest to use CMAKE_SOURCE_DIR + # if we ever integrate kodi-addons into kodi project + if(NOT CORE_SOURCE_DIR) + set(CORE_SOURCE_DIR ${CMAKE_SOURCE_DIR}) + endif() + + include(CMakeParseArguments) + core_file_read_filtered(version_list ${CORE_SOURCE_DIR}/version.txt) + core_file_read_filtered(json_version ${CORE_SOURCE_DIR}/xbmc/interfaces/json-rpc/schema/version.txt) + string(REGEX REPLACE "([^ ;]*) ([^;]*)" "\\1;\\2" version_list "${version_list};${json_version}") + set(version_props + ADDON_API + ADDON_REPOS + APP_NAME + APP_PACKAGE + COMPANY_NAME + COPYRIGHT_YEARS + JSONRPC_VERSION + PACKAGE_DESCRIPTION + PACKAGE_IDENTITY + PACKAGE_PUBLISHER + VERSION_MAJOR + VERSION_MINOR + VERSION_TAG + VERSION_CODE + WEBSITE + ) + cmake_parse_arguments(APP "" "${version_props}" "" ${version_list}) + + if(NOT ${APP_VERSION_CODE} MATCHES "^[0-9]+\\.[0-9][0-9]?\\.[0-9][0-9]?[0-9]?$") + message(FATAL_ERROR "VERSION_CODE was set to ${APP_VERSION_CODE} in version.txt, but it has to match '^\\d+\\.\\d{1,2}\\.\\d{1,3}$'") + endif() + set(APP_NAME ${APP_APP_NAME}) # inconsistency but APP_APP_NAME looks weird + string(TOLOWER ${APP_APP_NAME} APP_NAME_LC) + string(TOUPPER ${APP_APP_NAME} APP_NAME_UC) + set(COMPANY_NAME ${APP_COMPANY_NAME}) + set(APP_VERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}) + # Let Flatpak builders etc override APP_PACKAGE + # NOTE: We cannot declare an option() in top-level CMakeLists.txt + # because of CMP0077. + if(NOT APP_PACKAGE) + set(APP_PACKAGE ${APP_APP_PACKAGE}) + endif() + list(APPEND final_message "App package: ${APP_PACKAGE}") + if(APP_VERSION_TAG) + set(APP_VERSION ${APP_VERSION}-${APP_VERSION_TAG}) + string(TOLOWER ${APP_VERSION_TAG} APP_VERSION_TAG_LC) + endif() + string(REPLACE "." "," FILE_VERSION ${APP_ADDON_API}.0) + set(ADDON_REPOS ${APP_ADDON_REPOS}) + set(JSONRPC_VERSION ${APP_JSONRPC_VERSION}) + + # Set defines used in addon.xml.in and read from versions.h to set add-on + # version parts automatically + # This part is nearly identical to "AddonHelpers.cmake", except location of versions.h + file(STRINGS ${CORE_SOURCE_DIR}/xbmc/addons/kodi-dev-kit/include/kodi/versions.h BIN_ADDON_PARTS) + foreach(loop_var ${BIN_ADDON_PARTS}) + string(FIND "${loop_var}" "#define ADDON_" matchres) + if("${matchres}" EQUAL 0) + string(REGEX MATCHALL "[A-Z0-9._]+|[A-Z0-9._]+$" loop_var "${loop_var}") + list(GET loop_var 0 include_name) + list(GET loop_var 1 include_version) + string(REGEX REPLACE ".*\"(.*)\"" "\\1" ${include_name} ${include_version}) + endif() + endforeach(loop_var) + + # unset variables not used anywhere else + unset(version_list) + unset(APP_APP_NAME) + unset(APP_COMPANY_NAME) + unset(APP_APP_PACKAGE) + unset(APP_JSONRPC_VERSION) + unset(BIN_ADDON_PARTS) + + # bail if we can't parse version.txt + if(NOT DEFINED APP_VERSION_MAJOR OR NOT DEFINED APP_VERSION_MINOR) + message(FATAL_ERROR "Could not determine app version! Make sure that ${CORE_SOURCE_DIR}/version.txt exists") + endif() + if(NOT DEFINED JSONRPC_VERSION) + message(FATAL_ERROR "Could not determine json-rpc version! Make sure that ${CORE_SOURCE_DIR}/xbmc/interfaces/json-rpc/schema/version.txt exists") + endif() +endmacro() + +# add-on xml's +# find all folders containing addon.xml.in and used to define +# ADDON_XML_OUTPUTS, ADDON_XML_DEPENDS and ADDON_INSTALL_DATA +macro(find_addon_xml_in_files) + set(filter ${ARGV0}) + + if(filter AND VERBOSE) + message(STATUS "find_addon_xml_in_files: filtering ${filter}") + endif() + + file(GLOB ADDON_XML_IN_FILE ${CMAKE_SOURCE_DIR}/addons/*/addon.xml.in) + foreach(loop_var ${ADDON_XML_IN_FILE}) + list(GET loop_var 0 xml_name) + + string(REPLACE "/addon.xml.in" "" xml_name ${xml_name}) + string(REPLACE "${CORE_SOURCE_DIR}/" "" xml_name ${xml_name}) + + list(APPEND ADDON_XML_DEPENDS "${CORE_SOURCE_DIR}/${xml_name}/addon.xml.in") + if(filter AND NOT xml_name MATCHES ${filter}) + list(APPEND ADDON_XML_OUTPUTS "${CMAKE_BINARY_DIR}/${xml_name}/addon.xml") + endif() + + # Read content of add-on folder to have on install + file(GLOB ADDON_FILES "${CORE_SOURCE_DIR}/${xml_name}/*") + foreach(loop_var ${ADDON_FILES}) + if(loop_var MATCHES "addon.xml.in") + string(REPLACE "addon.xml.in" "addon.xml" loop_var ${loop_var}) + + list(GET loop_var 0 file_name) + string(REPLACE "${CORE_SOURCE_DIR}/" "" file_name ${file_name}) + list(APPEND ADDON_INSTALL_DATA "${file_name}") + + unset(file_name) + endif() + endforeach() + unset(xml_name) + endforeach() + + # Append also versions.h to depends + list(APPEND ADDON_XML_DEPENDS "${CORE_SOURCE_DIR}/xbmc/addons/kodi-dev-kit/include/kodi/versions.h") +endmacro() diff --git a/cmake/scripts/common/ModuleHelpers.cmake b/cmake/scripts/common/ModuleHelpers.cmake new file mode 100644 index 0000000..97a3901 --- /dev/null +++ b/cmake/scripts/common/ModuleHelpers.cmake @@ -0,0 +1,424 @@ +# 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() diff --git a/cmake/scripts/common/PathSetup.cmake b/cmake/scripts/common/PathSetup.cmake new file mode 100644 index 0000000..4fc888e --- /dev/null +++ b/cmake/scripts/common/PathSetup.cmake @@ -0,0 +1,13 @@ +# Platform path setup +include(cmake/scripts/${CORE_SYSTEM_NAME}/PathSetup.cmake) + +# Fallback install location for dependencies built +if(NOT DEPENDS_PATH) + set(DEPENDS_PATH "${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}") +endif() + +# If a platform sets a Depends_path for libs, prepend to cmake prefix path +# for when searching for libs (eg find_package) +if(DEPENDS_PATH) + list(PREPEND CMAKE_PREFIX_PATH ${DEPENDS_PATH}) +endif() diff --git a/cmake/scripts/common/Platform.cmake b/cmake/scripts/common/Platform.cmake new file mode 100644 index 0000000..397d8d4 --- /dev/null +++ b/cmake/scripts/common/Platform.cmake @@ -0,0 +1,57 @@ +if(NOT CORE_SYSTEM_NAME) + string(TOLOWER ${CMAKE_SYSTEM_NAME} CORE_SYSTEM_NAME) +endif() + +if(CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) + # Set default CORE_PLATFORM_NAME to X11 WAYLAND GBM + # This is overridden by user setting -DCORE_PLATFORM_NAME= + set(_DEFAULT_PLATFORM X11 WAYLAND GBM) + + if(NOT APP_RENDER_SYSTEM) + message(SEND_ERROR "You need to decide whether you want to use GL- or GLES-based rendering. Please set APP_RENDER_SYSTEM to either \"gl\" or \"gles\". For normal desktop systems, you will usually want to use \"gl\".") + endif() +else() + string(TOLOWER ${CORE_SYSTEM_NAME} _DEFAULT_PLATFORM) +endif() + +set(APP_BINARY_SUFFIX ".bin") + +# +# Note: please do not use CORE_PLATFORM_NAME in any checks, +# use the normalized to lower case CORE_PLATFORM_NAME_LC (see below) instead +# +if(NOT CORE_PLATFORM_NAME) + set(CORE_PLATFORM_NAME ${_DEFAULT_PLATFORM}) +endif() +set(CORE_PLATFORM_NAME ${CORE_PLATFORM_NAME} CACHE STRING "Platform port to build" FORCE) +unset(_DEFAULT_PLATFORM) + +string(REPLACE " " ";" CORE_PLATFORM_NAME "${CORE_PLATFORM_NAME}") +foreach(platform IN LISTS CORE_PLATFORM_NAME) + string(TOLOWER ${platform} platform) + list(APPEND CORE_PLATFORM_NAME_LC ${platform}) +endforeach() + +string(REPLACE ";" " " CORE_PLATFORM_STRING "${CORE_PLATFORM_NAME_LC}") +list(APPEND final_message "Platforms: ${CORE_PLATFORM_STRING}") + +if(CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) + list(LENGTH CORE_PLATFORM_NAME_LC PLATFORM_COUNT) + if(PLATFORM_COUNT EQUAL 1) + option(ENABLE_APP_AUTONAME "Enable renaming the binary according to windowing?" ON) + if(ENABLE_APP_AUTONAME) + set(APP_BINARY_SUFFIX "-${CORE_PLATFORM_NAME_LC}") + endif() + endif() +endif() + +foreach(CORE_PLATFORM_LC IN LISTS CORE_PLATFORM_NAME_LC) + if(EXISTS ${CMAKE_SOURCE_DIR}/cmake/platform/${CORE_SYSTEM_NAME}/${CORE_PLATFORM_LC}.cmake) + include(${CMAKE_SOURCE_DIR}/cmake/platform/${CORE_SYSTEM_NAME}/${CORE_PLATFORM_LC}.cmake) + else() + file(GLOB _platformnames RELATIVE ${CMAKE_SOURCE_DIR}/cmake/platform/${CORE_SYSTEM_NAME}/ + ${CMAKE_SOURCE_DIR}/cmake/platform/${CORE_SYSTEM_NAME}/*.cmake) + string(REPLACE ".cmake" " " _platformnames ${_platformnames}) + message(FATAL_ERROR "invalid CORE_PLATFORM_NAME: ${CORE_PLATFORM_NAME_LC}\nValid platforms: ${_platformnames}") + endif() +endforeach() diff --git a/cmake/scripts/common/PrepareEnv.cmake b/cmake/scripts/common/PrepareEnv.cmake new file mode 100644 index 0000000..559788f --- /dev/null +++ b/cmake/scripts/common/PrepareEnv.cmake @@ -0,0 +1,107 @@ +# parse version.txt and versions.h to get the version and API info +include(${CORE_SOURCE_DIR}/cmake/scripts/common/Macros.cmake) +core_find_versions() + +# in case we need to download something, set KODI_MIRROR to the default if not already set +if(NOT DEFINED KODI_MIRROR) + set(KODI_MIRROR "http://mirrors.kodi.tv") +endif() + +### copy all the addon binding header files to include/kodi +# make sure include/kodi exists and is empty +set(APP_LIB_DIR ${ADDON_DEPENDS_PATH}/lib/${APP_NAME_LC}) +if(NOT EXISTS "${APP_LIB_DIR}/") + file(MAKE_DIRECTORY ${APP_LIB_DIR}) +endif() + +set(APP_DATA_DIR ${ADDON_DEPENDS_PATH}/share/${APP_NAME_LC}) +if(NOT EXISTS "${APP_DATA_DIR}/") + file(MAKE_DIRECTORY ${APP_DATA_DIR}) +endif() + +set(APP_INCLUDE_DIR ${ADDON_DEPENDS_PATH}/include/${APP_NAME_LC}) +if(NOT EXISTS "${APP_INCLUDE_DIR}/") + file(MAKE_DIRECTORY ${APP_INCLUDE_DIR}) +endif() + +if(NOT CORE_SYSTEM_NAME) + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CORE_SYSTEM_NAME "osx") + else() + string(TOLOWER ${CMAKE_SYSTEM_NAME} CORE_SYSTEM_NAME) + endif() +endif() + +set(PLATFORM_TAG ${CORE_SYSTEM_NAME}) + +# The CPU variable is given either by ./tools/depends or by the +# ./cmake/scripts/common/ArchSetup.cmake (which refers to the Kodi building +# itself). However, this file is only used by addons, so CPU can not always +# be defined, so in this case, if empty, the base CPU will be used. +if(NOT CPU) + set(CPU ${CMAKE_SYSTEM_PROCESSOR}) +endif() + +if(CORE_SYSTEM_NAME STREQUAL android) + if (CPU MATCHES "v7a") + set(PLATFORM_TAG ${PLATFORM_TAG}-armv7) + elseif (CPU MATCHES "arm64") + set(PLATFORM_TAG ${PLATFORM_TAG}-aarch64) + elseif (CPU MATCHES "i686") + set(PLATFORM_TAG ${PLATFORM_TAG}-i686) + elseif (CPU MATCHES "x86_64") + set(PLATFORM_TAG ${PLATFORM_TAG}-x86_64) + else() + message(FATAL_ERROR "Unsupported architecture") + endif() +elseif(CORE_SYSTEM_NAME STREQUAL darwin_embedded) + set(PLATFORM_TAG ${CORE_PLATFORM_NAME}) + if (CPU MATCHES arm64) + set(PLATFORM_TAG ${PLATFORM_TAG}-aarch64) + else() + message(FATAL_ERROR "Unsupported architecture") + endif() +elseif(CORE_SYSTEM_NAME STREQUAL osx) + set(PLATFORM_TAG ${PLATFORM_TAG}-${CPU}) +elseif(CORE_SYSTEM_NAME STREQUAL windows) + include(CheckSymbolExists) + check_symbol_exists(_X86_ "Windows.h" _X86_) + check_symbol_exists(_AMD64_ "Windows.h" _AMD64_) + + if(_X86_) + set(PLATFORM_TAG ${PLATFORM_TAG}-i686) + elseif(_AMD64_) + set(PLATFORM_TAG ${PLATFORM_TAG}-x86_64) + else() + message(FATAL_ERROR "Unsupported architecture") + endif() + + unset(_X86_) + unset(_AMD64_) +endif() + +# generate the proper KodiConfig.cmake file +configure_file(${CORE_SOURCE_DIR}/cmake/KodiConfig.cmake.in ${APP_LIB_DIR}/KodiConfig.cmake @ONLY) + +# copy cmake helpers to lib/kodi +file(COPY ${CORE_SOURCE_DIR}/cmake/scripts/common/AddonHelpers.cmake + ${CORE_SOURCE_DIR}/cmake/scripts/common/AddOptions.cmake + DESTINATION ${APP_LIB_DIR}) + +### copy all the addon binding header files to include/kodi +include(${CORE_SOURCE_DIR}/xbmc/addons/AddonBindings.cmake) +file(COPY ${CORE_ADDON_BINDINGS_FILES} ${CORE_ADDON_BINDINGS_DIRS}/ + DESTINATION ${APP_INCLUDE_DIR} + REGEX ".txt" EXCLUDE) + +### processing additional tools required by the platform +if(EXISTS ${CORE_SOURCE_DIR}/cmake/scripts/${CORE_SYSTEM_NAME}/tools/) + file(GLOB platform_tools ${CORE_SOURCE_DIR}/cmake/scripts/${CORE_SYSTEM_NAME}/tools/*.cmake) + foreach(platform_tool ${platform_tools}) + get_filename_component(platform_tool_name ${platform_tool} NAME_WE) + message(STATUS "Processing ${CORE_SYSTEM_NAME} specific tool: ${platform_tool_name}") + + # include the file + include(${platform_tool}) + endforeach() +endif() diff --git a/cmake/scripts/common/ProjectMacros.cmake b/cmake/scripts/common/ProjectMacros.cmake new file mode 100644 index 0000000..3a1910c --- /dev/null +++ b/cmake/scripts/common/ProjectMacros.cmake @@ -0,0 +1,89 @@ +# This script holds macros which are project specific + +# Pack a skin xbt file +# Arguments: +# input input directory to pack +# output output xbt file +# On return: +# xbt is added to ${XBT_FILES} +function(pack_xbt input output) + file(GLOB_RECURSE MEDIA_FILES ${input}/*) + get_filename_component(dir ${output} DIRECTORY) + add_custom_command(OUTPUT ${output} + COMMAND ${CMAKE_COMMAND} -E make_directory ${dir} + COMMAND TexturePacker::TexturePacker::Executable + ARGS -input ${input} + -output ${output} + -dupecheck + DEPENDS ${MEDIA_FILES}) + list(APPEND XBT_FILES ${output}) + set(XBT_FILES ${XBT_FILES} PARENT_SCOPE) +endfunction() + +# Add a skin to installation list, mirroring it in build tree, packing textures +# Arguments: +# skin skin directory +# On return: +# xbt is added to ${XBT_FILES}, data added to ${install_data}, mirror in build tree +function(copy_skin_to_buildtree skin) + file(GLOB_RECURSE FILES ${skin}/*) + file(GLOB_RECURSE MEDIA_FILES ${skin}/media/*) + list(REMOVE_ITEM FILES ${MEDIA_FILES}) + foreach(file ${FILES}) + copy_file_to_buildtree(${file}) + endforeach() + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${dest}/media) + string(REPLACE "${CMAKE_SOURCE_DIR}/" "" dest ${skin}) + pack_xbt(${skin}/media ${CMAKE_BINARY_DIR}/${dest}/media/Textures.xbt) + + file(GLOB THEMES RELATIVE ${skin}/themes ${skin}/themes/*) + foreach(theme ${THEMES}) + pack_xbt(${skin}/themes/${theme} ${CMAKE_BINARY_DIR}/${dest}/media/${theme}.xbt) + endforeach() + + set(XBT_FILES ${XBT_FILES} PARENT_SCOPE) + set(install_data ${install_data} PARENT_SCOPE) +endfunction() + +# Get GTest tests as CMake tests. +# Copied from FindGTest.cmake +# Thanks to Daniel Blezek for the GTEST_ADD_TESTS code +function(GTEST_ADD_TESTS executable extra_args) + if(NOT ARGN) + message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS") + endif() + foreach(source ${ARGN}) + # This assumes that every source file passed in exists. Consider using + # SUPPORT_SOURCES for source files which do not contain tests and might + # have to be generated. + file(READ "${source}" contents) + string(REGEX MATCHALL "TEST_?[F]?\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents}) + foreach(hit ${found_tests}) + string(REGEX REPLACE ".*\\( *([A-Za-z_0-9]+), *([A-Za-z_0-9]+) *\\).*" "\\1.\\2" test_name ${hit}) + add_test(${test_name} ${executable} --gtest_filter=${test_name} ${extra_args}) + endforeach() + # Groups parametrized tests under a single ctest entry + string(REGEX MATCHALL "INSTANTIATE_TEST_CASE_P\\(([^,]+), *([^,]+)" found_tests2 ${contents}) + foreach(hit ${found_tests2}) + string(SUBSTRING ${hit} 24 -1 test_name) + string(REPLACE "," ";" test_name "${test_name}") + list(GET test_name 0 filter_name) + list(GET test_name 1 test_prefix) + string(STRIP ${test_prefix} test_prefix) + add_test(${test_prefix}.${filter_name} ${executable} --gtest_filter=${filter_name}* ${extra_args}) + endforeach() + endforeach() +endfunction() + +function(whole_archive output) + if(CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) + set(${output} -Wl,--whole-archive ${ARGN} -Wl,--no-whole-archive PARENT_SCOPE) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL AppleClang) + foreach(library ${ARGN}) + list(APPEND ${output} -Wl,-force_load ${library}) + set(${output} ${${output}} PARENT_SCOPE) + endforeach() + else() + set(${output} ${ARGN} PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/scripts/common/StaticAnalysis.cmake b/cmake/scripts/common/StaticAnalysis.cmake new file mode 100644 index 0000000..f7ffa39 --- /dev/null +++ b/cmake/scripts/common/StaticAnalysis.cmake @@ -0,0 +1,39 @@ +include(ProcessorCount) +ProcessorCount(CPU_CORES) + +find_program(CPPCHECK_EXECUTABLE cppcheck) + +if(CPPCHECK_EXECUTABLE) + add_custom_target(analyze-cppcheck + DEPENDS ${APP_NAME_LC} ${APP_NAME_LC}-test + COMMAND ${CPPCHECK_EXECUTABLE} + -j${CPU_CORES} + --project=${CMAKE_BINARY_DIR}/compile_commands.json + --std=c++${CMAKE_CXX_STANDARD} + --enable=all + --xml + --xml-version=2 + --language=c++ + --relative-paths=${CMAKE_SOURCE_DIR} + --rule-file=${CMAKE_SOURCE_DIR}/tools/static-analysis/cppcheck/cppcheck-rules.xml + --suppress-xml=${CMAKE_SOURCE_DIR}/tools/static-analysis/cppcheck/cppcheck-suppressions.xml + --output-file=${CMAKE_BINARY_DIR}/cppcheck-result.xml + COMMENT "Static code analysis using cppcheck") +endif() + +find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy) +find_program(RUN_CLANG_TIDY NAMES run-clang-tidy.py run-clang-tidy + PATHS /usr/share/clang /usr/bin) + +if(RUN_CLANG_TIDY AND CLANG_TIDY_EXECUTABLE) + add_custom_target(analyze-clang-tidy + DEPENDS ${APP_NAME_LC} ${APP_NAME_LC}-test + COMMAND ${RUN_CLANG_TIDY} + -j${CPU_CORES} + -clang-tidy-binary=${CLANG_TIDY_EXECUTABLE} + -p=${CMAKE_BINARY_DIR} + -header-filter='${CMAKE_BINARY_DIR}/.*/include/.*' + | tee ${CMAKE_BINARY_DIR}/clangtidy-result.xml + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Static code analysis using clang-tidy") +endif() diff --git a/cmake/scripts/common/Uninstall.cmake b/cmake/scripts/common/Uninstall.cmake new file mode 100644 index 0000000..5753857 --- /dev/null +++ b/cmake/scripts/common/Uninstall.cmake @@ -0,0 +1,58 @@ +macro(remove_empty_dirs) + list(REMOVE_DUPLICATES DIRECTORIES) + unset(PDIRECTORIES) + foreach(dir IN LISTS DIRECTORIES) + if(EXISTS $ENV{DESTDIR}${dir}) + file(GLOB _res $ENV{DESTDIR}${dir}/*) + list(LENGTH _res _len) + if(_len EQUAL 0 AND EXISTS $ENV{DESTDIR}${dir}) + message(STATUS "Removing empty dir: ${dir}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E remove_directory $ENV{DESTDIR}${dir} + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Failed to remove directory: $ENV{DESTDIR}${dir}") + endif() + get_filename_component(_pdir $ENV{DESTDIR}${dir} DIRECTORY) + list(APPEND PDIRECTORIES ${_pdir}) + endif() + endif() + endforeach() + list(LENGTH PDIRECTORIES _plen) + if(_plen GREATER 0) + set(DIRECTORIES ${PDIRECTORIES}) + remove_empty_dirs() + endif() +endmacro() + +# Uninstall target +set(MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt) +if(EXISTS ${MANIFEST}) + file(STRINGS ${MANIFEST} files) + foreach(file IN LISTS files) + if(EXISTS $ENV{DESTDIR}${file}) + get_filename_component(_dir $ENV{DESTDIR}${file} DIRECTORY) + list(APPEND DIRECTORIES $ENV{DESTDIR}${_dir}) + message(STATUS "Uninstalling: ${file}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E remove $ENV{DESTDIR}${file} + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Failed to remove file: $ENV{DESTDIR}${file}") + endif() + else() + message(STATUS "File does not exist: $ENV{DESTDIR}${file}") + endif() + endforeach(file) + + # delete empty dirs + if(DIRECTORIES) + remove_empty_dirs() + endif() +else() + message(STATUS "Cannot find install manifest: '${MANIFEST}'") +endif() -- cgit v1.2.3