From f44ecaa4f27e7538ddcad66d40e543bffa2d2d86 Mon Sep 17 00:00:00 2001 From: manuel Date: Sun, 4 Jun 2017 16:57:49 +0200 Subject: sync with upstream --- cmake/scripts/common/AddOptions.cmake | 78 +++ cmake/scripts/common/AddonHelpers.cmake | 378 +++++++++++ cmake/scripts/common/ArchSetup.cmake | 159 +++++ cmake/scripts/common/CMakeHelpers.cmake | 54 ++ cmake/scripts/common/CheckCommits.cmake | 75 +++ cmake/scripts/common/CheckTargetPlatform.cmake | 67 ++ cmake/scripts/common/GenerateVersionedFiles.cmake | 38 ++ cmake/scripts/common/GeneratorSetup.cmake | 49 ++ cmake/scripts/common/HandleDepends.cmake | 252 ++++++++ cmake/scripts/common/Macros.cmake | 722 ++++++++++++++++++++++ cmake/scripts/common/PrepareEnv.cmake | 65 ++ cmake/scripts/common/ProjectMacros.cmake | 89 +++ cmake/scripts/common/Uninstall.cmake | 22 + 13 files changed, 2048 insertions(+) create mode 100644 cmake/scripts/common/AddOptions.cmake create mode 100644 cmake/scripts/common/AddonHelpers.cmake 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/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/PrepareEnv.cmake create mode 100644 cmake/scripts/common/ProjectMacros.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..8772057 --- /dev/null +++ b/cmake/scripts/common/AddonHelpers.cmake @@ -0,0 +1,378 @@ +# 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. +add_custom_target(addon-package + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target package) + +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 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PACKAGE_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_PACKAGE_DIRECTORY}/addon-${target}-${version}.${ext} ${PACKAGE_DIR}/${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 + if(${prefix}_HEADERS MATCHES ${PROJECT_SOURCE_DIR}) + # 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 + if(${prefix}_SOURCES MATCHES ${PROJECT_SOURCE_DIR}) + # 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() + + # 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" "" depends_name ${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(FIND "${loop_var}" "#include" matchres) + if("${matchres}" EQUAL 0) + string(REPLACE " " ";" loop_var "${loop_var}") + list(GET loop_var 1 include_name) + string(REGEX REPLACE "[<>\"]|kodi/" "" include_name "${include_name}") + if(include_name MATCHES ${depend_header}) + set(ADDON_DEPENDS "${ADDON_DEPENDS}\n") + # Inform with them the addon header about used type + add_definitions(-D${used_type_name}) + message(STATUS "Added usage definition: ${used_type_name}") + 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}) + target_link_libraries(${target} ${${libs}}) + set_target_properties(${target} PROPERTIES VERSION ${${prefix}_VERSION} + SOVERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR} + PREFIX "") + 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) + set(PLATFORM ${CORE_SYSTEM_NAME}) + + 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() + + string(CONFIGURE "${addon_file}" addon_file_conf @ONLY) + file(GENERATE OUTPUT ${PROJECT_SOURCE_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) + set(PLATFORM ${CORE_SYSTEM_NAME}) + + file(READ ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in settings_file) + string(CONFIGURE "${settings_file}" settings_file_conf @ONLY) + file(GENERATE OUTPUT ${PROJECT_SOURCE_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}) + # Pack files together to create an archive + install(DIRECTORY ${target} DESTINATION ./ COMPONENT ${target}-${${prefix}_VERSION} PATTERN "xml.in" 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() + + # in case of a VC++ project the installation location contains a $(Configuration) VS variable + # we replace it with ${CMAKE_BUILD_TYPE} (which doesn't cover the case when the build configuration + # is changed within Visual Studio) + string(REPLACE "$(Configuration)" "${CMAKE_BUILD_TYPE}" LIBRARY_LOCATION "${LIBRARY_LOCATION}") + + if(${prefix}_SOURCES) + # install the generated DLL file + install(PROGRAMS ${LIBRARY_LOCATION} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}) + + if(CMAKE_BUILD_TYPE MATCHES Debug) + # for debug builds also install the PDB file + get_filename_component(LIBRARY_DIR ${LIBRARY_LOCATION} DIRECTORY) + install(FILES $ DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}) + endif() + endif() + if(${prefix}_CUSTOM_BINARY) + install(FILES ${LIBRARY_LOCATION} DESTINATION ${target} RENAME ${LIBRARY_FILENAME}) + endif() + if(${prefix}_CUSTOM_DATA) + install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${target}/resources) + 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}) + endif() + if(${prefix}_CUSTOM_BINARY) + install(FILES ${LIBRARY_LOCATION} DESTINATION ${target} RENAME ${LIBRARY_FILENAME} + COMPONENT ${target}-${${prefix}_VERSION}) + endif() + if(${prefix}_CUSTOM_DATA) + install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${target}/resources) + endif() + endif() + add_cpack_workaround(${target} ${${prefix}_VERSION} ${ext}) + else() + if(CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL rbpi 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} DESTINATION ${CMAKE_INSTALL_DATADIR}/addons PATTERN "xml.in" EXCLUDE) + if(${prefix}_CUSTOM_DATA) + install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${CMAKE_INSTALL_DATADIR}/addons/${target}/resources) + endif() + endif() + if(${APP_NAME_UC}_BUILD_DIR) + file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/${target}/*) + if(${prefix}_CUSTOM_DATA) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${${prefix}_CUSTOM_DATA} + ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/resources) + 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}) + 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/ArchSetup.cmake b/cmake/scripts/common/ArchSetup.cmake new file mode 100644 index 0000000..8d5dba8 --- /dev/null +++ b/cmake/scripts/common/ArchSetup.cmake @@ -0,0 +1,159 @@ +# 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. + +include(CheckCXXSourceCompiles) +include(CheckSymbolExists) +include(CheckFunctionExists) +include(CheckIncludeFile) + +# Macro to check if a given type exists in a given header +# Arguments: +# header the header to check +# type the type to check for existence +# var the compiler definition to set if type exists +# On return: +# If type was found, the definition is added to SYSTEM_DEFINES +macro(check_type header type var) + check_cxx_source_compiles("#include <${header}> + int main() + { + ${type} s; + }" ${var}) + if(${var}) + list(APPEND SYSTEM_DEFINES -D${var}=1) + endif() +endmacro() + +# 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(NOT CORE_SYSTEM_NAME) + string(TOLOWER ${CMAKE_SYSTEM_NAME} CORE_SYSTEM_NAME) +endif() + +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) + set(CORE_HOST_IS_TARGET TRUE) +else() + set(CORE_HOST_IS_TARGET FALSE) +endif() + +# Main cpp +set(CORE_MAIN_SOURCE ${CMAKE_SOURCE_DIR}/xbmc/platform/posix/main.cpp) + +# 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) + +message(STATUS "Core system type: ${CORE_SYSTEM_NAME}") +message(STATUS "Platform: ${PLATFORM}") +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_type(string std::u16string HAVE_STD__U16_STRING) +check_type(string std::u32string HAVE_STD__U32_STRING) +check_type(string char16_t HAVE_CHAR16_T) +check_type(string char32_t HAVE_CHAR32_T) +check_type(stdint.h uint_least16_t HAVE_STDINT_H) +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() +if(HAVE_INTTYPES_H) + list(APPEND SYSTEM_DEFINES -DHAVE_INTTYPES_H=1) +endif() + +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(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_options (ALL_LANGUAGES DEBUG "-g" "-D_DEBUG" "-Wall") +endif() + 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..82ee668 --- /dev/null +++ b/cmake/scripts/common/CheckTargetPlatform.cmake @@ -0,0 +1,67 @@ +# 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(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 wether 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/GenerateVersionedFiles.cmake b/cmake/scripts/common/GenerateVersionedFiles.cmake new file mode 100644 index 0000000..90b2173 --- /dev/null +++ b/cmake/scripts/common/GenerateVersionedFiles.cmake @@ -0,0 +1,38 @@ +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) +foreach(loop_var ${ADDON_XML_IN_FILE}) + # prevent 'xbmc.json'; will be obtained from 'xbmc/interfaces/json-rpc/schema/CMakeLists.txt'. + if(loop_var MATCHES "xbmc.json") + continue() + endif() + + 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}) + + # copy everything except addon.xml.in to build folder + file(COPY "${source_dir}" DESTINATION "${CMAKE_BINARY_DIR}/addons" REGEX ".xml.in" EXCLUDE) + + 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..85d2cf4 --- /dev/null +++ b/cmake/scripts/common/HandleDepends.cmake @@ -0,0 +1,252 @@ +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) + 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) + + # replace some custom placeholders + string(REPLACE "@MINGW_TOOLCHAIN_FILE@" "${OUTPUT_DIR}/Toolchain_mingw32.cmake" extraflags "${extraflags}") + string(REPLACE " " ";" extraflags ${extraflags}) + + message(STATUS "${id} extraflags: ${extraflags}") + 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) + # 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}") + list(APPEND BUILD_ARGS -DCMAKE_C_FLAGS=${TMP_C_FLAGS} + -DCMAKE_CXX_FLAGS=${TMP_CXX_FLAGS}) + endif() + + if(CMAKE_TOOLCHAIN_FILE) + list(APPEND BUILD_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) + message("toolchain specified") + message(${BUILD_ARGS}) + endif() + + # prepare patchfile. ensure we have a clean file after reconfiguring + set(PATCH_FILE ${BUILD_DIR}/${id}/tmp/patch.cmake) + file(REMOVE ${PATCH_FILE}) + + # 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) + file(APPEND ${PATCH_FILE} + "file(COPY ${dir}/CMakeLists.txt + DESTINATION ${BUILD_DIR}/${id}/src/${id})\n") + 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 + find_program(PATCH_EXECUTABLE NAMES patch) + + 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") + set(PATCH_PROGRAM "\"${PATCH_PROGRAM}\" --binary") + endif() + endif() + endif() + + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${patch}) + file(APPEND ${PATCH_FILE} + "execute_process(COMMAND ${PATCH_PROGRAM} -p1 -i \"${patch}\")\n") + 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}") + 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}) + file(APPEND ${PATCH_FILE} + "message(STATUS \"AUTOCONF: copying ${afile} to ${BUILD_DIR}/${id}/src/${id}\")\n + file(COPY ${afile} DESTINATION ${BUILD_DIR}/${id}/src/${id})\n") + endforeach() + endif() + + # if the patch file exists we need to set the PATCH_COMMAND + set(PATCH_COMMAND "") + if(EXISTS ${PATCH_FILE}) + set(PATCH_COMMAND ${CMAKE_COMMAND} -P ${PATCH_FILE}) + 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_EXE_LINKER_FLAGS=-L${OUTPUT_DIR}/lib + -DCMAKE_INCLUDE_PATH=${OUTPUT_DIR}/include) + endif() + + externalproject_add(${id} + URL ${url} + DOWNLOAD_DIR ${BUILD_DIR}/download + 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..4b6f890 --- /dev/null +++ b/cmake/scripts/common/Macros.cmake @@ -0,0 +1,722 @@ +# 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} libcpluff ffmpeg dvdnav crossguid) + set(CORE_LIBRARY ${name} PARENT_SCOPE) + + # Add precompiled headers to Kodi main libraries + if(CORE_SYSTEM_NAME STREQUAL 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} libcpluff ffmpeg dvdnav crossguid) + set(test_archives ${test_archives} ${name} CACHE STRING "" FORCE) + 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 an addon callback library +# Arguments: +# name name of the library to add +# 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_addon_library name) + get_filename_component(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} NAME) + list(APPEND SOURCES lib${name}.cpp) + core_add_shared_library(${name} OUTPUT_DIRECTORY addons/${DIRECTORY}) + set_target_properties(${name} PROPERTIES FOLDER addons) + target_include_directories(${name} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/xbmc/addons/kodi-addon-dev-kit/include/kodi + ${CMAKE_SOURCE_DIR}/xbmc) +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}) + 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(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") + 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() + 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() + +# add a required dependency of main application +# Arguments: +# dep_list name of find rule for dependency, used uppercased for variable prefix +# also accepts a list of multiple dependencies +# On return: +# dependency added to ${SYSTEM_INCLUDES}, ${DEPLIBS} and ${DEP_DEFINES} +function(core_require_dep) + foreach(dep ${ARGN}) + find_package(${dep} 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() + +# add a required dyloaded dependency of main application +# Arguments: +# dep_list name of find rule for dependency, used uppercased for variable prefix +# also accepts a list of multiple dependencies +# On return: +# dependency added to ${SYSTEM_INCLUDES}, ${dep}_SONAME is set up +function(core_require_dyload_dep) + foreach(dep ${ARGN}) + find_package(${dep} REQUIRED) + string(TOUPPER ${dep} depup) + list(APPEND SYSTEM_INCLUDES ${${depup}_INCLUDE_DIRS}) + list(APPEND DEP_DEFINES ${${depup}_DEFINITIONS}) + find_soname(${depup} REQUIRED) + export_dep() + set(${depup}_SONAME ${${depup}_SONAME} PARENT_SCOPE) + 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 an optional dependency of main application +# Arguments: +# dep_list name of find rule for dependency, used uppercased for variable prefix +# also accepts a list of multiple dependencies +# On return: +# dependency optionally added to ${SYSTEM_INCLUDES}, ${DEPLIBS} and ${DEP_DEFINES} +function(core_optional_dep) + foreach(dep ${ARGN}) + set(_required False) + setup_enable_switch() + if(${enable_switch} STREQUAL AUTO) + find_package(${dep}) + elseif(${${enable_switch}}) + find_package(${dep} 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() + +# add an optional dyloaded dependency of main application +# Arguments: +# dep_list name of find rule for dependency, used uppercased for variable prefix +# also accepts a list of multiple dependencies +# On return: +# dependency optionally added to ${SYSTEM_INCLUDES}, ${DEP_DEFINES}, ${dep}_SONAME is set up +function(core_optional_dyload_dep) + foreach(dep ${ARGN}) + set(_required False) + setup_enable_switch() + if(${enable_switch} STREQUAL AUTO) + find_package(${dep}) + elseif(${${enable_switch}}) + find_package(${dep} REQUIRED) + set(_required True) + endif() + + if(${depup}_FOUND) + list(APPEND SYSTEM_INCLUDES ${${depup}_INCLUDE_DIRS}) + find_soname(${depup} REQUIRED) + list(APPEND DEP_DEFINES ${${depup}_DEFINITIONS}) + set(final_message ${final_message} "${depup} enabled: Yes" PARENT_SCOPE) + export_dep() + set(${depup}_SONAME ${${depup}_SONAME} PARENT_SCOPE) + elseif(_required) + message(FATAL_ERROR "${depup} enabled but not found") + else() + set(final_message ${final_message} "${depup} enabled: No" PARENT_SCOPE) + endif() + endforeach() +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) + else() + find_package(Git) + if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) + execute_process(COMMAND ${GIT_EXECUTABLE} update-index --ignore-submodules --refresh -q) + 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() + if(status_code) + execute_process(COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=format:"%h-dirty" HEAD + OUTPUT_VARIABLE HASH + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(SUBSTRING ${HASH} 1 13 HASH) + else() + execute_process(COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=format:"%h" HEAD + OUTPUT_VARIABLE HASH + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(SUBSTRING ${HASH} 1 7 HASH) + endif() + execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:"%cd" --date=short HEAD + OUTPUT_VARIABLE DATE + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(SUBSTRING ${DATE} 1 10 DATE) + string(REPLACE "-" "" DATE ${DATE}) + else() + string(TIMESTAMP DATE "%Y%m%d" UTC) + set(HASH "nogitfound") + 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_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 +# FILE_VERSION - file version in the form of 16,9,702,0 - Windows only +# +# 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) + string(REPLACE " " ";" version_list "${version_list}") + cmake_parse_arguments(APP "" "APP_NAME;COMPANY_NAME;WEBSITE;VERSION_MAJOR;VERSION_MINOR;VERSION_TAG;VERSION_CODE;ADDON_API;APP_PACKAGE" "" ${version_list}) + + 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}) + set(APP_PACKAGE ${APP_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 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-addon-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(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() +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) + 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") + list(APPEND ADDON_XML_OUTPUTS "${CMAKE_BINARY_DIR}/${xml_name}/addon.xml") + + # 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}) + endif() + + 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) + endforeach() + unset(xml_name) + endforeach() + + # Append also versions.h to depends + list(APPEND ADDON_XML_DEPENDS "${CORE_SOURCE_DIR}/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h") +endmacro() diff --git a/cmake/scripts/common/PrepareEnv.cmake b/cmake/scripts/common/PrepareEnv.cmake new file mode 100644 index 0000000..4dc8698 --- /dev/null +++ b/cmake/scripts/common/PrepareEnv.cmake @@ -0,0 +1,65 @@ +# 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() + +# make sure C++11 is always set +if(NOT WIN32) + string(REGEX MATCH "-std=(gnu|c)\\+\\+11" cxx11flag "${CMAKE_CXX_FLAGS}") + if(NOT cxx11flag) + set(CXX11_SWITCH "-std=c++11") + endif() +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 standard add-on include files +file(COPY ${CORE_SOURCE_DIR}/xbmc/addons/kodi-addon-dev-kit/include/kodi/ + DESTINATION ${APP_INCLUDE_DIR}) + +### copy all the addon binding header files to include/kodi +# parse addon-bindings.mk to get the list of header files to copy +core_file_read_filtered(bindings ${CORE_SOURCE_DIR}/xbmc/addons/addon-bindings.mk) +foreach(header ${bindings}) + # copy the header file to include/kodi + configure_file(${CORE_SOURCE_DIR}/${header} ${APP_INCLUDE_DIR} COPYONLY) +endforeach() + +### 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..89ecca4 --- /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 ouput 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 + 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/Uninstall.cmake b/cmake/scripts/common/Uninstall.cmake new file mode 100644 index 0000000..5660e19 --- /dev/null +++ b/cmake/scripts/common/Uninstall.cmake @@ -0,0 +1,22 @@ +# 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}) + 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) +else() + message(STATUS "Cannot find install manifest: '${MANIFEST}'") +endif() -- cgit v1.2.3