1 cmake_minimum_required(VERSION 3.12)
4 list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
6 option(ADDON_TARBALL_CACHING "Cache downloaded addon source tarballs?" ON)
7 if(ADDON_TARBALL_CACHING)
8 message(STATUS "Addon source tarball caching is enabled")
10 message(STATUS "Addon source tarball caching is disabled")
13 if(NOT CMAKE_BUILD_TYPE)
14 set(CMAKE_BUILD_TYPE Release)
17 if(NOT CORE_SYSTEM_NAME)
18 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
19 set(CORE_SYSTEM_NAME "osx")
21 string(TOLOWER ${CMAKE_SYSTEM_NAME} CORE_SYSTEM_NAME)
25 include(ExternalProject)
27 ### setup all the necessary paths
29 set(CORE_SOURCE_DIR ${APP_ROOT})
31 message(WARNING "APP_ROOT is deprecated. Please use CORE_SOURCE_DIR instead.")
33 if(NOT CORE_SOURCE_DIR)
34 set(CORE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../..)
36 file(TO_CMAKE_PATH "${CORE_SOURCE_DIR}" CORE_SOURCE_DIR)
38 get_filename_component(CORE_SOURCE_DIR "${CORE_SOURCE_DIR}" ABSOLUTE)
41 set(BUILD_DIR "${CMAKE_BINARY_DIR}/build")
43 file(TO_CMAKE_PATH "${BUILD_DIR}" BUILD_DIR)
45 get_filename_component(BUILD_DIR "${BUILD_DIR}" ABSOLUTE)
47 if(NOT ADDON_DEPENDS_PATH)
48 set(ADDON_DEPENDS_PATH "${BUILD_DIR}/depends")
50 file(TO_CMAKE_PATH "${ADDON_DEPENDS_PATH}" ADDON_DEPENDS_PATH)
52 get_filename_component(ADDON_DEPENDS_PATH "${ADDON_DEPENDS_PATH}" ABSOLUTE)
55 set(PLATFORM_DIR ${CORE_SOURCE_DIR}/cmake/platform/${CORE_SYSTEM_NAME})
56 file(TO_CMAKE_PATH "${PLATFORM_DIR}" PLATFORM_DIR)
59 # make sure CMAKE_PREFIX_PATH is set
60 if(NOT CMAKE_PREFIX_PATH)
61 set(CMAKE_PREFIX_PATH "${ADDON_DEPENDS_PATH}")
63 file(TO_CMAKE_PATH "${CMAKE_PREFIX_PATH}" CMAKE_PREFIX_PATH)
64 list(APPEND CMAKE_PREFIX_PATH "${ADDON_DEPENDS_PATH}")
67 # check for autoconf stuff to pass on
69 string(REPLACE " " ";" AUTOCONF_FILES ${AUTOCONF_FILES})
70 set(CROSS_AUTOCONF "yes")
73 if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR NOT CMAKE_INSTALL_PREFIX)
74 set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/output/addons")
76 list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX})
78 if (CMAKE_SYSTEM_NAME STREQUAL WindowsStore)
79 set(BUILD_ARGS_ext -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
80 -DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION})
83 include(${CORE_SOURCE_DIR}/cmake/scripts/common/CompilerSettings.cmake)
85 set(BUILD_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
86 -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
87 -DPACKAGE_CONFIG_PATH=${ADDON_DEPENDS_PATH}/lib/pkgconfig
88 -DADDON_DEPENDS_PATH=${ADDON_DEPENDS_PATH}
89 -DOVERRIDE_PATHS=${OVERRIDE_PATHS}
90 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
91 -DCMAKE_USER_MAKE_RULES_OVERRIDE=${CMAKE_USER_MAKE_RULES_OVERRIDE}
92 -DCMAKE_USER_MAKE_RULES_OVERRIDE_CXX=${CMAKE_USER_MAKE_RULES_OVERRIDE_CXX}
93 -DCORE_SYSTEM_NAME=${CORE_SYSTEM_NAME}
95 -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
96 -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
100 # move cmake specific targets to a CMakePredefinedTargets folder in Visual Studio
101 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
104 option(PACKAGE_ZIP "Prepare built addons for packaging" OFF)
106 # needed for project installing
107 list(APPEND BUILD_ARGS -DPACKAGE_ZIP=ON)
109 # figure out where to store the packaged ZIP archives
111 set(PACKAGE_DIR "${BUILD_DIR}/zips")
113 file(TO_CMAKE_PATH "${PACKAGE_DIR}" PACKAGE_DIR)
115 list(APPEND BUILD_ARGS -DPACKAGE_DIR=${PACKAGE_DIR})
117 message(STATUS "ZIP packaging enabled (destination: ${PACKAGE_DIR})")
120 if(CMAKE_TOOLCHAIN_FILE)
121 list(APPEND BUILD_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE})
122 message(STATUS "Toolchain specified")
123 message(STATUS ${BUILD_ARGS})
126 # used for addons where need special folders to store there content (if
127 # not set the addon define it byself).
128 # e.g. Google Chromium addon where his git bring:
129 # - "unable to create file" ... "Filename too long"
130 # see also WARNING by Windows on: https://bitbucket.org/chromiumembedded/cef/wiki/MasterBuildQuickStart
132 message(STATUS "Third party lib path specified")
133 message(STATUS ${THIRD_PARTY_PATH})
134 list(APPEND BUILD_ARGS -DTHIRD_PARTY_PATH=${THIRD_PARTY_PATH})
137 if(NOT ADDONS_TO_BUILD)
138 set(ADDONS_TO_BUILD "all")
140 string(STRIP "${ADDONS_TO_BUILD}" ADDONS_TO_BUILD)
141 message(STATUS "Building following addons: ${ADDONS_TO_BUILD}")
142 string(REPLACE " " ";" ADDONS_TO_BUILD ${ADDONS_TO_BUILD})
145 if(NOT ADDONS_DEFINITION_DIR)
146 set(ADDONS_DEFINITION_DIR ${PROJECT_SOURCE_DIR}/addons)
148 file(TO_CMAKE_PATH "${ADDONS_DEFINITION_DIR}" ADDONS_DEFINITION_DIR)
150 get_filename_component(ADDONS_DEFINITION_DIR "${ADDONS_DEFINITION_DIR}" ABSOLUTE)
153 if(NOT IS_ABSOLUTE ${ADDON_SRC_PREFIX})
154 get_filename_component(ADDON_SRC_PREFIX "${CMAKE_BINARY_DIR}/${ADDON_SRC_PREFIX}" ABSOLUTE)
156 message(STATUS "Overriding addon source directory prefix: ${ADDON_SRC_PREFIX}")
160 set(APP_LIB_DIR "${ADDON_DEPENDS_PATH}/lib/kodi")
162 file(TO_CMAKE_PATH "${APP_LIB_DIR}" APP_LIB_DIR)
165 set(APP_PREFIX "${CMAKE_INSTALL_PREFIX}")
167 # check for platform specific stuff
168 if(EXISTS ${PLATFORM_DIR}/defines.txt)
169 file(STRINGS ${PLATFORM_DIR}/defines.txt platformdefines)
171 if(NOT ARCH_DEFINES AND platformdefines)
172 set(ARCH_DEFINES ${platformdefines})
176 # include check_target_platform() function
177 include(${CORE_SOURCE_DIR}/cmake/scripts/common/CheckTargetPlatform.cmake)
179 set(ADDON_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
181 # check install permissions
182 check_install_permissions(${CMAKE_INSTALL_PREFIX} can_write)
183 if(NOT ${can_write} AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
185 set(ADDON_INSTALL_DIR ${CMAKE_BINARY_DIR}/.install)
186 list(APPEND BUILD_ARGS -DOVERRIDE_PATHS=ON)
187 message(STATUS "NEED_SUDO: ${NEED_SUDO} (no write permission for ${CMAKE_INSTALL_PREFIX})")
191 ### prepare the build environment for the binary addons
192 # copy the PrepareEnv.cmake script to the depends path so that we can include it
193 file(COPY ${CORE_SOURCE_DIR}/cmake/scripts/common/PrepareEnv.cmake DESTINATION ${APP_LIB_DIR})
195 # add the location of PrepareEnv.cmake to CMAKE_MODULE_PATH so that it is found
196 list(APPEND CMAKE_MODULE_PATH ${APP_LIB_DIR})
198 # include PrepareEnv.cmake which contains the logic to install the addon header bindings etc
201 ### add the depends subdirectory for any general dependencies
202 message(STATUS "\n-- ---- Preparing general dependencies ----")
203 add_subdirectory(depends)
205 # add a custom target "package-addons" which will package and install all addons
206 add_custom_target(package-addons)
208 ### get and build all the binary addons
209 # look for all the addons to be built
210 file(GLOB_RECURSE addons ${ADDONS_DEFINITION_DIR}/*.txt)
212 #if there are no addons assume that bootstrapping hasn't happened yet
214 message(STATUS "Bootstrapping all default repositories as no addons were found...")
215 set(BOOTSTRAP_BUILD_DIR "${BUILD_DIR}/bootstrap")
217 # make sure that the bootstraps build addon exists
218 if(NOT EXISTS ${BOOTSTRAP_BUILD_DIR})
219 file(MAKE_DIRECTORY ${BOOTSTRAP_BUILD_DIR})
222 string(REPLACE ";" " " ADDONS_TO_BUILD_STR "${ADDONS_TO_BUILD}")
223 # generate the bootstrap buildsystem
224 execute_process(COMMAND ${CMAKE_COMMAND} ${PROJECT_SOURCE_DIR}/bootstrap
225 -DCMAKE_INSTALL_PREFIX:PATH=${ADDONS_DEFINITION_DIR}
226 -DBUILD_DIR:PATH=${BOOTSTRAP_BUILD_DIR}
227 -DADDONS_TO_BUILD:STRING=${ADDONS_TO_BUILD_STR}
228 -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
229 WORKING_DIRECTORY ${BOOTSTRAP_BUILD_DIR})
231 # execute the generated bootstrap buildsystem
232 execute_process(COMMAND ${CMAKE_COMMAND} --build ${BOOTSTRAP_BUILD_DIR}
233 WORKING_DIRECTORY ${BOOTSTRAP_BUILD_DIR})
235 # now look for all the addons to be built again
236 file(GLOB_RECURSE addons ${ADDONS_DEFINITION_DIR}/*.txt)
239 message(FATAL_ERROR "No addons available to be built")
243 # Track if at least one addon has been found. Everything else is likely an
244 # error either in ADDONS_TO_BUILD or in the directory configuration.
245 set(SUPPORTED_ADDON_FOUND FALSE)
247 if(NOT ADDONS_TO_BUILD)
248 set(ADDONS_TO_BUILD "all")
251 if(NOT ADDONS_TO_BUILD STREQUAL "all")
252 # Exact addon match list
253 set(REGEX_ADDONS_TO_BUILD ${ADDONS_TO_BUILD})
254 set(EXACT_MATCH_ADDON_LIST "")
255 set(EXCLUDE_ADDONS "")
257 foreach(addon ${ADDONS_TO_BUILD})
258 set(FOUND_EXCLUSION "")
259 string(REGEX MATCH "^[-](.*)" FOUND_EXCLUSION "${addon}")
260 if(NOT FOUND_EXCLUSION STREQUAL "")
261 list(APPEND EXCLUDE_ADDONS ${CMAKE_MATCH_1})
262 list(REMOVE_ITEM REGEX_ADDONS_TO_BUILD "-${CMAKE_MATCH_1}")
264 foreach(addonrepoitem ${addons})
265 if(NOT (addonrepoitem MATCHES platforms.txt))
266 # need to strip regex chars, or the filter regex will use
267 string(REPLACE "*" "" strippedregex ${addon})
268 if(${addonrepoitem} MATCHES "^.*\/(${strippedregex}).txt")
269 list(APPEND EXACT_MATCH_ADDON_LIST ${addon})
270 # remove exact matches from addons_to_build
271 list(REMOVE_ITEM REGEX_ADDONS_TO_BUILD "${addon}")
278 message(STATUS "Exclusion list: ${EXCLUDE_ADDONS}")
279 message(STATUS "Exact Match list: ${EXACT_MATCH_ADDON_LIST}")
280 message(STATUS "Regex list: ${REGEX_ADDONS_TO_BUILD}")
283 foreach(addon ${addons})
284 if(NOT (addon MATCHES platforms.txt))
285 file(STRINGS ${addon} def)
286 string(REPLACE " " ";" def ${def})
289 if("${ADDONS_TO_BUILD}" STREQUAL "all")
290 set(ADDON_FOUND TRUE)
292 set(ADDON_EXCLUDE FALSE)
293 set(ADDON_FOUND FALSE)
294 foreach(exclusion ${EXCLUDE_ADDONS})
295 if(id MATCHES "${exclusion}")
296 set(ADDON_EXCLUDE TRUE)
297 message(STATUS "Addon ${id} matches exclusion rule -${exclusion}")
306 list(FIND EXACT_MATCH_ADDON_LIST ${id} idx)
308 # exact match, so build
309 message(STATUS "Exact match ${id}, building addon")
310 set(ADDON_FOUND TRUE)
313 foreach(ADDONLISTITEM ${REGEX_ADDONS_TO_BUILD})
314 if(id MATCHES "${ADDONLISTITEM}")
315 message(STATUS "Pattern ${ADDONLISTITEM} matches ${id}, building addon")
316 set(ADDON_FOUND TRUE)
324 message(STATUS "\n-- ---- Configuring addon ${addon} ----")
325 set(SUPPORTED_ADDON_FOUND TRUE)
327 get_filename_component(dir ${addon} DIRECTORY)
329 # check if the addon has a platforms.txt
330 set(platform_found FALSE)
331 check_target_platform(${dir} ${CORE_SYSTEM_NAME} platform_found)
333 if(${platform_found})
334 # make sure the output directory is clean
335 file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}/${id}/")
337 # get the URL and revision of the addon
338 list(LENGTH def deflength)
341 set(archive_name ${id})
343 set(SOURCE_DIR ${ADDON_SRC_PREFIX}/${id})
349 # if there is a 3rd parameter in the file, we consider it a git revision
350 if(deflength GREATER 2 AND "${SOURCE_DIR}" STREQUAL "")
351 list(GET def 2 revision)
353 # we need access to a git executable
354 find_package(Git REQUIRED)
356 # resolve revision to git hash
357 execute_process(COMMAND ${GIT_EXECUTABLE} ls-remote ${url} ${revision} OUTPUT_VARIABLE revision_hash)
358 # git ls-remote only works on branches and tag names but not on revisions
359 if(NOT "${revision_hash}" STREQUAL "")
360 string(REPLACE "\t" ";" revision_list ${revision_hash})
361 list(GET revision_list 0 revision_hash)
362 message(STATUS "${id}: git branch/tag ${revision} resolved to hash: ${revision_hash}")
363 set(revision ${revision_hash})
366 # Note: downloading specific revisions via http in the format below is probably github specific
367 # if we ever use other repositories, this might need adapting
368 set(url ${url}/archive/${revision}.tar.gz)
369 set(archive_name ${archive_name}-${revision})
370 elseif("${SOURCE_DIR}" STREQUAL "")
371 # check if the URL starts with file://
372 string(REGEX MATCH "^file://.*$" local_url "${url}")
374 #if not we assume this to be a local directory
376 # this is not an archive
379 # remove the file:// protocol from the URL
380 string(REPLACE "file://" "" SOURCE_DIR "${url}")
382 # on win32 we may have to remove another leading /
384 # check if the path is a local path
385 string(REGEX MATCH "^/.*$" local_path "${SOURCE_DIR}")
387 string(SUBSTRING "${SOURCE_DIR}" 1 -1 SOURCE_DIR)
393 # download the addon if necessary
394 if(NOT "${archive_name}" STREQUAL "")
395 # download and extract the addon
396 if(NOT ADDON_TARBALL_CACHING OR NOT EXISTS ${BUILD_DIR}/download/${archive_name}.tar.gz)
397 # cleanup any of the previously downloaded archives of this addon
398 file(GLOB archives "${BUILD_DIR}/download/${id}*.tar.gz")
400 message(STATUS "Removing old archives of ${id}: ${archives}")
401 file(REMOVE ${archives})
405 file(DOWNLOAD "${url}" "${BUILD_DIR}/download/${archive_name}.tar.gz" STATUS dlstatus LOG dllog SHOW_PROGRESS)
406 list(GET dlstatus 0 retcode)
407 if(NOT ${retcode} EQUAL 0)
408 file(REMOVE ${BUILD_DIR}/download/${archive_name}.tar.gz)
409 message(STATUS "ERROR downloading ${url} - status: ${dlstatus} log: ${dllog}")
410 # add a dummy target for addons to get it in addons failure file
411 list(APPEND ALL_ADDONS_BUILDING ${id})
412 add_custom_target(${id} COMMAND ${CMAKE_COMMAND} -E echo "IGNORED ${id} - download failed" COMMAND exit 1)
417 # remove any previously extracted version of the addon
418 file(REMOVE_RECURSE "${BUILD_DIR}/${id}")
420 # extract the addon from the archive
421 execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzvf ${BUILD_DIR}/download/${archive_name}.tar.gz
422 WORKING_DIRECTORY ${BUILD_DIR})
423 file(GLOB extract_dir "${BUILD_DIR}/${archive_name}*")
424 if(extract_dir STREQUAL "")
425 message(FATAL_ERROR "${id}: error extracting ${BUILD_DIR}/download/${archive_name}.tar.gz")
427 file(RENAME "${extract_dir}" "${BUILD_DIR}/${id}")
430 set(SOURCE_DIR ${BUILD_DIR}/${id})
433 if(NOT "${SOURCE_DIR}" STREQUAL "" AND EXISTS ${SOURCE_DIR})
434 # create a list of addons we are building
435 list(APPEND ALL_ADDONS_BUILDING ${id})
437 # setup the buildsystem for the addon
438 externalproject_add(${id}
439 SOURCE_DIR ${SOURCE_DIR}
440 INSTALL_DIR ${ADDON_INSTALL_DIR}
441 CMAKE_ARGS ${BUILD_ARGS})
443 # add a custom step to the external project between the configure and the build step which will always
444 # be executed and therefore forces a re-build of all changed files
445 externalproject_add_step(${id} forcebuild
446 COMMAND ${CMAKE_COMMAND} -E echo "Force build of ${id}"
451 set(${id}_DEPENDS_DIR ${SOURCE_DIR}/depends)
453 if(EXISTS ${${id}_DEPENDS_DIR})
454 include(${CORE_SOURCE_DIR}/cmake/scripts/common/HandleDepends.cmake)
455 add_addon_depends(${id} ${${id}_DEPENDS_DIR})
456 if(${id}_DEPS AND NOT "${${id}_DEPS}" STREQUAL "")
457 message(STATUS "${id} DEPENDENCIES: ${${id}_DEPS}")
458 add_dependencies(${id} ${${id}_DEPS})
462 if(CROSS_AUTOCONF AND AUTOCONF_FILES)
463 if(EXISTS ${SOURCE_DIR}/bootstrap/autoreconf.txt)
464 file(STRINGS ${SOURCE_DIR}/bootstrap/autoreconf.txt conf_dirs)
465 foreach(conf_dir ${conf_dirs})
466 foreach(afile ${AUTOCONF_FILES})
467 message(STATUS "copying ${afile} to ${SOURCE_DIR}/${conf_dir}")
468 file(COPY ${afile} DESTINATION ${SOURCE_DIR}/${conf_dir})
474 # create a forwarding target to the addon-package target
475 get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
477 set(config --config $<CONFIG>)
479 add_custom_target(package-${id}
480 COMMAND ${CMAKE_COMMAND} --build ${id}-prefix/src/${id}-build ${config} --target addon-package
482 add_dependencies(package-addons package-${id})
485 message(FATAL_ERROR "${id}: invalid or missing addon source directory at ${SOURCE_DIR}")
488 # add a dummy target for addons that are unsupported on this platform
489 add_custom_target(${id} COMMAND ${CMAKE_COMMAND} -E echo "IGNORED ${id} - not supported on ${CORE_SYSTEM_NAME}\n")
497 add_custom_target(sudo-install
498 COMMAND ${CMAKE_COMMAND} -E echo "sudo rights needed to install to ${CMAKE_INSTALL_PREFIX}\n"
499 COMMAND sudo ${CMAKE_COMMAND} -E copy_directory ${ADDON_INSTALL_DIR}/ ${CMAKE_INSTALL_PREFIX}/
502 foreach(_id ${ALL_ADDONS_BUILDING})
503 add_dependencies(sudo-install ${_id})
505 message(WARNING "sudo rights needed to install to ${CMAKE_INSTALL_PREFIX}")
506 message(STATUS "\nplease type \"make sudo-install\"\n\n")
509 if(NOT SUPPORTED_ADDON_FOUND)
510 message(FATAL_ERROR "${ADDONS_TO_BUILD} did not match any of the supported addons. \
511 A list of supported addons can be viewed by building the 'supported_addons' target. \
512 Addon definitions are loaded from ADDONS_DEFINITION_DIR (${ADDONS_DEFINITION_DIR}).")
515 # add custom target "supported_addons" that returns all addons that are supported on this platform
516 string(REPLACE ";" " " ALL_ADDONS_BUILDING "${ALL_ADDONS_BUILDING}")
517 add_custom_target(supported_addons COMMAND ${CMAKE_COMMAND} -E echo "ALL_ADDONS_BUILDING: ${ALL_ADDONS_BUILDING}" VERBATIM)
518 add_custom_target(need-sudo COMMAND ${CMAKE_COMMAND} -E echo ${NEED_SUDO} VERBATIM)