summaryrefslogtreecommitdiffstats
path: root/cmake/scripts/common/AddonHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/scripts/common/AddonHelpers.cmake')
-rw-r--r--cmake/scripts/common/AddonHelpers.cmake378
1 files changed, 378 insertions, 0 deletions
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 @@
1# Workaround for the fact that cpack's filenames are not customizable.
2# Each add-on is added as a separate component to facilitate zip/tgz packaging.
3# The filenames are always of the form basename-component, which is
4# incompatible with the addonid-version scheme we want. This hack renames
5# the files from the file names generated by the 'package' target.
6# Sadly we cannot extend the 'package' target, as it is a builtin target, see
7# http://public.kitware.com/Bug/view.php?id=8438
8# Thus, we have to add an 'addon-package' target.
9add_custom_target(addon-package
10 COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target package)
11
12macro(add_cpack_workaround target version ext)
13 if(NOT PACKAGE_DIR)
14 set(PACKAGE_DIR "${CMAKE_INSTALL_PREFIX}/zips")
15 endif()
16
17 add_custom_command(TARGET addon-package PRE_BUILD
18 COMMAND ${CMAKE_COMMAND} -E make_directory ${PACKAGE_DIR}
19 COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_PACKAGE_DIRECTORY}/addon-${target}-${version}.${ext} ${PACKAGE_DIR}/${target}-${version}.${ext})
20endmacro()
21
22# Grab the version from a given add-on's addon.xml
23macro (addon_version dir prefix)
24 if(EXISTS ${PROJECT_SOURCE_DIR}/${dir}/addon.xml.in)
25 file(READ ${PROJECT_SOURCE_DIR}/${dir}/addon.xml.in ADDONXML)
26 else()
27 file(READ ${dir}/addon.xml ADDONXML)
28 endif()
29
30 string(REGEX MATCH "<addon[^>]*version.?=.?.[0-9\\.]+" VERSION_STRING ${ADDONXML})
31 string(REGEX REPLACE ".*version=.([0-9\\.]+).*" "\\1" ${prefix}_VERSION ${VERSION_STRING})
32 message(STATUS ${prefix}_VERSION=${${prefix}_VERSION})
33endmacro()
34
35# Build, link and optionally package an add-on
36macro (build_addon target prefix libs)
37 addon_version(${target} ${prefix})
38
39 # Below comes the generation of a list with used sources where the includes to
40 # kodi's headers becomes checked.
41 # This goes the following steps to identify them:
42 # 1. Check headers are at own depended on addon
43 # - If so, it is checked whether the whole folder is already inserted, if
44 # not, it is added.
45 # 2. If headers are not defined independently and there is more as one source
46 # file.
47 # - If yes, it is checked whether the headers with the sources together
48 # - In case no headers are inserted and more than one source file exists,
49 # the whole addon folder is searched for headers.
50 # 3. As a last step, the actual source files are checked.
51 if(${prefix}_SOURCES)
52 # Read used headers from addon, needed to identitfy used kodi addon interface headers
53 if(${prefix}_HEADERS)
54 # Add the used header files defined with CMakeLists.txt from addon itself
55 if(${prefix}_HEADERS MATCHES ${PROJECT_SOURCE_DIR})
56 # include path name already complete
57 list(APPEND USED_SOURCES ${${prefix}_HEADERS})
58 else()
59 # add the complete include path to begin
60 foreach(hdr_file ${${prefix}_HEADERS})
61 list(APPEND USED_SOURCES ${PROJECT_SOURCE_DIR}/${hdr_file})
62 endforeach()
63 endif()
64 else()
65 list(LENGTH ${prefix}_SOURCES _length)
66 if(${_length} GREATER 1)
67 string(REGEX MATCHALL "[.](h)" _length ${${prefix}_SOURCES}})
68 if(NOT _length)
69 file(GLOB_RECURSE USED_SOURCES ${PROJECT_SOURCE_DIR}/*.h*)
70 if(USED_SOURCES)
71 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.")
72 endif()
73 endif()
74 endif()
75 endif()
76
77 # Add the used source files defined with CMakeLists.txt from addon itself
78 if(${prefix}_SOURCES MATCHES ${PROJECT_SOURCE_DIR})
79 # include path name already complete
80 list(APPEND USED_SOURCES ${${prefix}_SOURCES})
81 else()
82 # add the complete include path to begin
83 foreach(src_file ${${prefix}_SOURCES})
84 list(APPEND USED_SOURCES ${PROJECT_SOURCE_DIR}/${src_file})
85 endforeach()
86 endif()
87
88 # Set defines used in addon.xml.in and read from versions.h to set add-on
89 # version parts automatically
90 file(STRINGS ${KODI_INCLUDE_DIR}/versions.h BIN_ADDON_PARTS)
91 foreach(loop_var ${BIN_ADDON_PARTS})
92 # Only pass strings with "#define ADDON_" from versions.h
93 if(loop_var MATCHES "#define ADDON_")
94 string(REGEX REPLACE "\\\n" " " loop_var ${loop_var}) # remove header line breaks
95 string(REGEX REPLACE "#define " "" loop_var ${loop_var}) # remove the #define name from string
96 string(REGEX MATCHALL "[//a-zA-Z0-9._-]+" loop_var "${loop_var}") # separate the define values to a list
97
98 # Get the definition name
99 list(GET loop_var 0 include_name)
100 # Check definition are depends who is a bigger list
101 if("${include_name}" MATCHES "_DEPENDS")
102 # Use start definition name as base for other value type
103 list(GET loop_var 0 list_name)
104 string(REPLACE "_DEPENDS" "" depends_name ${list_name})
105 string(REPLACE "_DEPENDS" "_XML_ID" xml_entry_name ${list_name})
106 string(REPLACE "_DEPENDS" "_USED" used_type_name ${list_name})
107
108 # remove the first value, not needed and wrong on "for" loop
109 list(REMOVE_AT loop_var 0)
110
111 foreach(depend_header ${loop_var})
112 string(STRIP ${depend_header} depend_header)
113 foreach(src_file ${USED_SOURCES})
114 file(STRINGS ${src_file} BIN_ADDON_SRC_PARTS)
115 foreach(loop_var ${BIN_ADDON_SRC_PARTS})
116 string(FIND "${loop_var}" "#include" matchres)
117 if("${matchres}" EQUAL 0)
118 string(REPLACE " " ";" loop_var "${loop_var}")
119 list(GET loop_var 1 include_name)
120 string(REGEX REPLACE "[<>\"]|kodi/" "" include_name "${include_name}")
121 if(include_name MATCHES ${depend_header})
122 set(ADDON_DEPENDS "${ADDON_DEPENDS}\n<import addon=\"${${xml_entry_name}}\" version=\"${${depends_name}}\"/>")
123 # Inform with them the addon header about used type
124 add_definitions(-D${used_type_name})
125 message(STATUS "Added usage definition: ${used_type_name}")
126 set(FOUND_HEADER_USAGE 1)
127 endif()
128 endif()
129 endforeach()
130 if(FOUND_HEADER_USAGE EQUAL 1) # break this loop if found but not unset, needed in parts where includes muddled up on addon
131 break()
132 endif()
133 endforeach()
134 # type is found and round becomes broken for next round with other type
135 if(FOUND_HEADER_USAGE EQUAL 1)
136 unset(FOUND_HEADER_USAGE)
137 break()
138 endif()
139 endforeach()
140 else()
141 # read the definition values and make it by the on version.h defined names public
142 list(GET loop_var 1 include_variable)
143 string(REGEX REPLACE ".*\"(.*)\"" "\\1" ${include_name} ${include_variable})
144 set(${include_name} ${${include_name}})
145 endif()
146 endif()
147 endforeach()
148
149 add_library(${target} ${${prefix}_SOURCES})
150 target_link_libraries(${target} ${${libs}})
151 set_target_properties(${target} PROPERTIES VERSION ${${prefix}_VERSION}
152 SOVERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}
153 PREFIX "")
154 if(OS STREQUAL "android")
155 set_target_properties(${target} PROPERTIES PREFIX "lib")
156 endif()
157 elseif(${prefix}_CUSTOM_BINARY)
158 add_custom_target(${target} ALL)
159 endif()
160
161 # get the library's location
162 if(${prefix}_CUSTOM_BINARY)
163 list(GET ${prefix}_CUSTOM_BINARY 0 LIBRARY_LOCATION)
164 list(GET ${prefix}_CUSTOM_BINARY 1 LIBRARY_FILENAME)
165 if(CORE_SYSTEM_NAME STREQUAL android)
166 set(LIBRARY_FILENAME "lib${LIBRARY_FILENAME}")
167 endif()
168 else()
169 set(LIBRARY_LOCATION $<TARGET_FILE:${target}>)
170 # get the library's filename
171 if(CORE_SYSTEM_NAME STREQUAL android)
172 # for android we need the filename without any version numbers
173 set(LIBRARY_FILENAME $<TARGET_LINKER_FILE_NAME:${target}>)
174 else()
175 set(LIBRARY_FILENAME $<TARGET_FILE_NAME:${target}>)
176 endif()
177 endif()
178
179 # if there's an addon.xml.in we need to generate the addon.xml
180 if(EXISTS ${PROJECT_SOURCE_DIR}/${target}/addon.xml.in)
181 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/${target}/addon.xml.in)
182 set(PLATFORM ${CORE_SYSTEM_NAME})
183
184 file(READ ${PROJECT_SOURCE_DIR}/${target}/addon.xml.in addon_file)
185
186 # If sources are present must be the depends set
187 if(${prefix}_SOURCES)
188 string(FIND "${addon_file}" "\@ADDON_DEPENDS\@" matchres)
189 if("${matchres}" EQUAL -1)
190 message(FATAL_ERROR "\"\@ADDON_DEPENDS\@\" not found in addon.xml.in.")
191 endif()
192 endif()
193
194 string(CONFIGURE "${addon_file}" addon_file_conf @ONLY)
195 file(GENERATE OUTPUT ${PROJECT_SOURCE_DIR}/${target}/addon.xml CONTENT "${addon_file_conf}")
196 if(${APP_NAME_UC}_BUILD_DIR)
197 file(GENERATE OUTPUT ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/addon.xml CONTENT "${addon_file_conf}")
198 endif()
199 endif()
200
201 # if there's an settings.xml.in we need to generate the settings.xml
202 if(EXISTS ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in)
203 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in)
204 set(PLATFORM ${CORE_SYSTEM_NAME})
205
206 file(READ ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml.in settings_file)
207 string(CONFIGURE "${settings_file}" settings_file_conf @ONLY)
208 file(GENERATE OUTPUT ${PROJECT_SOURCE_DIR}/${target}/resources/settings.xml CONTENT "${settings_file_conf}")
209 if(${APP_NAME_UC}_BUILD_DIR)
210 file(GENERATE OUTPUT ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/resources/settings.xml CONTENT "${settings_file_conf}")
211 endif()
212 endif()
213
214 # set zip as default if addon-package is called without PACKAGE_XXX
215 set(CPACK_GENERATOR "ZIP")
216 set(ext "zip")
217 if(PACKAGE_ZIP OR PACKAGE_TGZ)
218 if(PACKAGE_TGZ)
219 set(CPACK_GENERATOR "TGZ")
220 set(ext "tar.gz")
221 endif()
222 set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
223 set(CPACK_PACKAGE_FILE_NAME addon)
224 if(CMAKE_BUILD_TYPE STREQUAL "Release")
225 set(CPACK_STRIP_FILES TRUE)
226 endif()
227 set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
228 set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
229 list(APPEND CPACK_COMPONENTS_ALL ${target}-${${prefix}_VERSION})
230 # Pack files together to create an archive
231 install(DIRECTORY ${target} DESTINATION ./ COMPONENT ${target}-${${prefix}_VERSION} PATTERN "xml.in" EXCLUDE)
232 if(WIN32)
233 if(NOT CPACK_PACKAGE_DIRECTORY)
234 # determine the temporary path
235 file(TO_CMAKE_PATH "$ENV{TEMP}" WIN32_TEMP_PATH)
236 string(LENGTH "${WIN32_TEMP_PATH}" WIN32_TEMP_PATH_LENGTH)
237 string(LENGTH "${PROJECT_BINARY_DIR}" PROJECT_BINARY_DIR_LENGTH)
238
239 # check if the temporary path is shorter than the default packaging directory path
240 if(WIN32_TEMP_PATH_LENGTH GREATER 0 AND WIN32_TEMP_PATH_LENGTH LESS PROJECT_BINARY_DIR_LENGTH)
241 # set the directory used by CPack for packaging to the temp directory
242 set(CPACK_PACKAGE_DIRECTORY ${WIN32_TEMP_PATH})
243 endif()
244 endif()
245
246 # in case of a VC++ project the installation location contains a $(Configuration) VS variable
247 # we replace it with ${CMAKE_BUILD_TYPE} (which doesn't cover the case when the build configuration
248 # is changed within Visual Studio)
249 string(REPLACE "$(Configuration)" "${CMAKE_BUILD_TYPE}" LIBRARY_LOCATION "${LIBRARY_LOCATION}")
250
251 if(${prefix}_SOURCES)
252 # install the generated DLL file
253 install(PROGRAMS ${LIBRARY_LOCATION} DESTINATION ${target}
254 COMPONENT ${target}-${${prefix}_VERSION})
255
256 if(CMAKE_BUILD_TYPE MATCHES Debug)
257 # for debug builds also install the PDB file
258 get_filename_component(LIBRARY_DIR ${LIBRARY_LOCATION} DIRECTORY)
259 install(FILES $<TARGET_PDB_FILE:${target}> DESTINATION ${target}
260 COMPONENT ${target}-${${prefix}_VERSION})
261 endif()
262 endif()
263 if(${prefix}_CUSTOM_BINARY)
264 install(FILES ${LIBRARY_LOCATION} DESTINATION ${target} RENAME ${LIBRARY_FILENAME})
265 endif()
266 if(${prefix}_CUSTOM_DATA)
267 install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${target}/resources)
268 endif()
269 else() # NOT WIN32
270 if(NOT CPACK_PACKAGE_DIRECTORY)
271 set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR})
272 endif()
273 if(${prefix}_SOURCES)
274 install(TARGETS ${target} DESTINATION ${target}
275 COMPONENT ${target}-${${prefix}_VERSION})
276 endif()
277 if(${prefix}_CUSTOM_BINARY)
278 install(FILES ${LIBRARY_LOCATION} DESTINATION ${target} RENAME ${LIBRARY_FILENAME}
279 COMPONENT ${target}-${${prefix}_VERSION})
280 endif()
281 if(${prefix}_CUSTOM_DATA)
282 install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${target}/resources)
283 endif()
284 endif()
285 add_cpack_workaround(${target} ${${prefix}_VERSION} ${ext})
286 else()
287 if(CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL rbpi OR CORE_SYSTEM_NAME STREQUAL freebsd)
288 if(NOT OVERRIDE_PATHS)
289 if(CMAKE_INSTALL_PREFIX AND NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND NOT CMAKE_INSTALL_PREFIX STREQUAL "${${APP_NAME_UC}_PREFIX}")
290 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")
291 endif()
292 if(CMAKE_INSTALL_LIBDIR AND NOT CMAKE_INSTALL_LIBDIR STREQUAL "${${APP_NAME_UC}_LIB_DIR}")
293 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")
294 endif()
295 if(CMAKE_INSTALL_DATADIR AND NOT CMAKE_INSTALL_DATADIR STREQUAL "${${APP_NAME_UC}_DATA_DIR}")
296 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")
297 endif()
298 set(CMAKE_INSTALL_PREFIX "${${APP_NAME_UC}_PREFIX}" CACHE PATH "${APP_NAME} install prefix" FORCE)
299 set(CMAKE_INSTALL_LIBDIR "${${APP_NAME_UC}_LIB_DIR}" CACHE PATH "${APP_NAME} install libdir" FORCE)
300 set(CMAKE_INSTALL_DATADIR "${${APP_NAME_UC}_DATA_DIR}" CACHE PATH "${APP_NAME} install datadir" FORCE)
301 else()
302 if(NOT CMAKE_INSTALL_LIBDIR)
303 set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib/${APP_NAME_LC}")
304 endif()
305 if(NOT CMAKE_INSTALL_DATADIR)
306 set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share/${APP_NAME_LC}")
307 endif()
308 endif()
309 else()
310 set(CMAKE_INSTALL_LIBDIR "lib/${APP_NAME_LC}")
311 set(CMAKE_INSTALL_DATADIR "share/${APP_NAME_LC}")
312 endif()
313 if(${prefix}_SOURCES)
314 install(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target})
315 endif()
316 if (${prefix}_CUSTOM_BINARY)
317 install(FILES ${LIBRARY_LOCATION} DESTINATION ${CMAKE_INSTALL_LIBDIR}/addons/${target} RENAME ${LIBRARY_FILENAME})
318 endif()
319 install(DIRECTORY ${target} DESTINATION ${CMAKE_INSTALL_DATADIR}/addons PATTERN "xml.in" EXCLUDE)
320 if(${prefix}_CUSTOM_DATA)
321 install(DIRECTORY ${${prefix}_CUSTOM_DATA} DESTINATION ${CMAKE_INSTALL_DATADIR}/addons/${target}/resources)
322 endif()
323 endif()
324 if(${APP_NAME_UC}_BUILD_DIR)
325 file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/${target}/*)
326 if(${prefix}_CUSTOM_DATA)
327 add_custom_command(TARGET ${target} POST_BUILD
328 COMMAND ${CMAKE_COMMAND} -E copy_directory
329 ${${prefix}_CUSTOM_DATA}
330 ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/resources)
331 endif()
332 foreach(file ${files})
333 string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/${target}/" "" name "${file}")
334 # A good way to deal with () in filenames
335 if(NOT ${file} MATCHES xml.in)
336 configure_file(${file} ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/${name} COPYONLY)
337 endif()
338 endforeach()
339 add_custom_command(TARGET ${target} POST_BUILD
340 COMMAND ${CMAKE_COMMAND} -E copy
341 ${LIBRARY_LOCATION}
342 ${${APP_NAME_UC}_BUILD_DIR}/addons/${target}/${LIBRARY_FILENAME})
343 endif()
344endmacro()
345
346# finds a path to a given file (recursive)
347function (kodi_find_path var_name filename search_path strip_file)
348 file(GLOB_RECURSE PATH_TO_FILE ${search_path} ${filename})
349 if(strip_file)
350 string(REPLACE ${filename} "" PATH_TO_FILE ${PATH_TO_FILE})
351 endif()
352 set (${var_name} ${PATH_TO_FILE} PARENT_SCOPE)
353endfunction()
354
355# Cmake build options
356include(AddOptions)
357include(TestCXXAcceptsFlag)
358option(PACKAGE_ZIP "Package Zip file?" OFF)
359option(PACKAGE_TGZ "Package TGZ file?" OFF)
360option(BUILD_SHARED_LIBS "Build shared libs?" ON)
361
362# LTO support?
363CHECK_CXX_ACCEPTS_FLAG("-flto" HAVE_LTO)
364if(HAVE_LTO)
365 option(USE_LTO "use link time optimization" OFF)
366 if(USE_LTO)
367 add_options(ALL_LANGUAGES ALL_BUILDS "-flto")
368 endif()
369endif()
370
371# set this to try linking dependencies as static as possible
372if(ADDONS_PREFER_STATIC_LIBS)
373 set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
374endif()
375
376if(${APP_NAME_UC}_BUILD_DIR)
377 list(APPEND CMAKE_PREFIX_PATH ${${APP_NAME_UC}_BUILD_DIR}/build)
378endif()