OSDN Git Service

645404d05b6788a864df62c3438877ac2fc4c141
[kde/Katie.git] / cmake / modules / KatieBuildMacros.cmake
1 # Copyright (c) 2015-2020, Ivailo Monev, <xakepa10@gmail.com>
2 # Redistribution and use is allowed according to the terms of the BSD license.
3
4 set(KATIE_UIC "uic")
5 set(KATIE_RCC "rcc")
6 set(KATIE_MOC "bootstrap_moc")
7 set(KATIE_LRELEASE "lrelease")
8
9 include(CMakePushCheckState)
10 include(CheckSymbolExists)
11 include(CheckFunctionExists)
12 include(CheckStructHasMember)
13
14 # a macro to print a dev warning but only when the build type is Debug
15 macro(KATIE_WARNING MESSAGESTR)
16     if(CMAKE_BUILD_TYPE STREQUAL "Debug")
17         message(AUTHOR_WARNING "${MESSAGESTR} ${ARGN}")
18     endif()
19 endmacro()
20
21 # a function to check for C function/definition, works for external functions.
22 function(KATIE_CHECK_DEFINED FORDEFINITION FROMHEADER)
23     set(compileout "${CMAKE_BINARY_DIR}/${FORDEFINITION}.cpp")
24     configure_file(
25         "${CMAKE_SOURCE_DIR}/cmake/modules/katie_check_defined.cpp.cmake"
26         "${compileout}"
27         @ONLY
28     )
29     try_compile(${FORDEFINITION}_test
30         "${CMAKE_BINARY_DIR}"
31         "${compileout}"
32         COMPILE_DEFINITIONS ${ARGN}
33         OUTPUT_VARIABLE ${FORDEFINITION}_test_output
34     )
35     if(${FORDEFINITION}_test)
36         message(STATUS "Found ${FORDEFINITION} in: <${FROMHEADER}>")
37         set(HAVE_${FORDEFINITION} TRUE PARENT_SCOPE)
38     else()
39         message(STATUS "Could not find ${FORDEFINITION} in: <${FROMHEADER}>")
40         set(HAVE_${FORDEFINITION} FALSE PARENT_SCOPE)
41     endif()
42 endfunction()
43
44 # a macro to check for C function presence in header, if function is found a
45 # definition is added.
46 macro(KATIE_CHECK_FUNCTION FORFUNCTION FROMHEADER)
47     katie_check_defined("${FORFUNCTION}" "${FROMHEADER}" ${ARGN})
48
49     if(HAVE_${FORFUNCTION})
50         string(TOUPPER "${FORFUNCTION}" upperfunction)
51         add_definitions(-DQT_HAVE_${upperfunction})
52     endif()
53 endmacro()
54
55 # a function to check for C function with 64-bit offset alternative, sets
56 # QT_LARGEFILE_SUPPORT to FALSE if not available and does not perform
57 # additional checks if one fails
58 function(KATIE_CHECK_FUNCTION64 FORFUNCTION FROMHEADER)
59     if(QT_LARGEFILE_SUPPORT)
60         katie_check_defined("${FORFUNCTION}" "${FROMHEADER}" -D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE ${ARGN})
61
62         if(NOT HAVE_${FORFUNCTION})
63             set(QT_LARGEFILE_SUPPORT FALSE PARENT_SCOPE)
64         endif()
65     endif()
66 endfunction()
67
68 # a macro to check for C struct member presence in header, if member is found a
69 # definition is added.
70 function(KATIE_CHECK_STRUCT FORSTRUCT FORMEMBER FROMHEADER)
71     cmake_reset_check_state()
72     check_struct_has_member("struct ${FORSTRUCT}" "${FORMEMBER}" "${FROMHEADER}" HAVE_${FORSTRUCT}_${FORMEMBER})
73     cmake_pop_check_state()
74
75     if(HAVE_${FORSTRUCT}_${FORMEMBER})
76         string(TOUPPER "${FORSTRUCT}_${FORMEMBER}" upperstructmember)
77         add_definitions(-DQT_HAVE_${upperstructmember})
78     endif()
79 endfunction()
80
81 # a macro to write data to file, does nothing if the file exists and its
82 # content is the same as the data to be written
83 macro(KATIE_WRITE_FILE OUTFILE DATA)
84     if(NOT EXISTS "${OUTFILE}")
85         file(WRITE "${OUTFILE}" "${DATA}")
86     else()
87         file(READ "${OUTFILE}" OUTDATA)
88         if(NOT "${OUTDATA}" STREQUAL "${DATA}")
89             file(WRITE "${OUTFILE}" "${DATA}")
90         endif()
91     endif()
92 endmacro()
93
94 # a macro to create camel-case headers pointing to their lower-case alternative
95 # as well as meta header that includes all component headers
96 macro(KATIE_GENERATE_PUBLIC PUBLIC_INCLUDES SUBDIR)
97     foreach(pubheader ${PUBLIC_INCLUDES})
98         string(TOLOWER ${pubheader} pubname)
99         set(pubout "${CMAKE_BINARY_DIR}/include/${SUBDIR}/${pubheader}")
100         katie_write_file("${pubout}" "#include <${pubname}.h>\n")
101     endforeach()
102
103     file(GLOB PUBLIC_LIST "${CMAKE_BINARY_DIR}/include/${SUBDIR}/*.h")
104     set(metaout "${CMAKE_BINARY_DIR}/include/${SUBDIR}/${SUBDIR}")
105     set(metadata "#ifndef ${SUBDIR}_META_H\n#define ${SUBDIR}_META_H\n\n")
106     foreach(pubheader ${PUBLIC_LIST})
107         get_filename_component(pubname ${pubheader} NAME)
108         # qtest_gui.h is exception because it requires explicit gui component linkage
109         if(NOT "${pubname}" STREQUAL "qtest_gui.h")
110             set(metadata "${metadata}#include <${SUBDIR}/${pubname}>\n")
111         endif()
112     endforeach()
113     set(metadata "${metadata}\n#endif\n")
114     katie_write_file("${metaout}" "${metadata}")
115 endmacro()
116
117 # a macro to copy headers into specific directory based on their base names
118 # ultimately suitable for copy operation of their destination
119 macro(KATIE_GENERATE_MISC MISC_INCLUDES SUBDIR)
120     foreach(mischeader ${MISC_INCLUDES})
121         get_filename_component(headername ${mischeader} NAME)
122         if("${headername}" MATCHES "(_p.h)")
123             set(headout "${CMAKE_BINARY_DIR}/privateinclude/${SUBDIR}/${headername}")
124         else()
125             set(headout "${CMAKE_BINARY_DIR}/include/${SUBDIR}/${headername}")
126         endif()
127         configure_file("${mischeader}" "${headout}" COPYONLY)
128     endforeach(mischeader)
129 endmacro()
130
131 macro(KATIE_GENERATE_OBSOLETE OBSOLETE_INCLUDE SUBDIR REDIRECT)
132     set(pubout "${CMAKE_BINARY_DIR}/include/${SUBDIR}/${OBSOLETE_INCLUDE}")
133     katie_write_file("${pubout}" "#include <${SUBDIR}/${REDIRECT}>\n")
134 endmacro()
135
136 # a macro for creating pkgconfig files for major components
137 macro(KATIE_GENERATE_PACKAGE FORTARGET REQUIRES)
138     string(REPLACE "Kt" "Qt" PACKAGE_FAKE "${FORTARGET}")
139     set(PACKAGE_NAME ${FORTARGET})
140     set(PACKAGE_REQUIRES ${REQUIRES})
141     string(REPLACE "Kt" "" compname "${FORTARGET}")
142     string(TOUPPER ${compname} compname)
143     set(PACKAGE_FLAGS "-DQT_${compname}_LIB")
144     # adding the definitions to other components is simply redundant since
145     # all components require the core component
146     if("${FORTARGET}" STREQUAL "KtCore")
147         katie_string_wrap("${KATIE_DEFINITIONS}" KATIE_DEFINITIONS)
148         set(PACKAGE_FLAGS "${PACKAGE_FLAGS} ${KATIE_DEFINITIONS}")
149     endif()
150     configure_file(
151         "${CMAKE_SOURCE_DIR}/cmake/pkgconfig.cmake"
152         "${CMAKE_BINARY_DIR}/pkgconfig/${FORTARGET}.pc"
153     )
154     install(
155         FILES "${CMAKE_BINARY_DIR}/pkgconfig/${FORTARGET}.pc"
156         DESTINATION "${KATIE_PKGCONFIG_PATH}"
157         COMPONENT Devel
158     )
159 endmacro()
160
161 # a function to ensure that (1) the output string is not null so that when it
162 # is passed to another function/macro it does not complain about inproper
163 # number of arguments and (2) it joins the input which if quoted has semicolons
164 # in it (if it is a list) that the sub-command (e.g. gcc) can not handle
165 function(KATIE_STRING_WRAP INVAR OUTSTR)
166     string(STRIP "${INVAR}" instrtrimmed)
167     if("${instrtrimmed}" STREQUAL "")
168         set(${OUTSTR} " " PARENT_SCOPE)
169     else()
170         string(REPLACE ";" " " modstring "${INVAR}")
171         set(${OUTSTR} "${modstring}" PARENT_SCOPE)
172     endif()
173 endfunction()
174
175 # a function to convert string to list, opposite of katie_string_wrap()
176 function(KATIE_STRING_UNWRAP INSTR OUTLST)
177     string(STRIP "${INSTR}" instrtrimmed)
178     if("${instrtrimmed}" STREQUAL "")
179         set(${OUTLST} " " PARENT_SCOPE)
180     else()
181         string(REPLACE " " ";${ARGN}" modstring "${ARGN}${INSTR}")
182         set(${OUTLST} ${modstring} PARENT_SCOPE)
183     endif()
184 endfunction()
185
186 # a function to get the Git checkout hash and store it in a variable
187 function(KATIE_GIT_CHECKOUT OUTSTR)
188     find_program(git NAMES git)
189     if(EXISTS "${CMAKE_SOURCE_DIR}/.git" AND NOT git)
190         message(WARNING "Git was not found, unable to obtain checkout.\n")
191     else(EXISTS "${CMAKE_SOURCE_DIR}/.git")
192         execute_process(
193             COMMAND "${git}" rev-parse HEAD
194             WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
195             RESULT_VARIABLE git_result
196             ERROR_VARIABLE git_output
197             OUTPUT_VARIABLE git_output
198             OUTPUT_STRIP_TRAILING_WHITESPACE
199         )
200
201         if(NOT git_result STREQUAL 0)
202             message(WARNING "Git command failed, unable to obtain checkout:\n${git_output}")
203         else()
204             set(${OUTSTR} "${git_output}" PARENT_SCOPE)
205         endif()
206     endif()
207 endfunction()
208
209 # a macro to instruct katie_setup_target() which sources to exclude from the
210 # all-in-one source file
211 macro(KATIE_ALLINONE_EXCLUDE ARG1)
212     set_source_files_properties(${ARG1} ${ARGN} PROPERTIES ALLINONE_EXCLUDE TRUE)
213 endmacro()
214
215 # a function to create an array of source files for a target while taking into
216 # account all-in-one target build setting up proper dependency for the
217 # moc/uic/rcc generated resources
218 function(KATIE_SETUP_TARGET FORTARGET)
219     # this can be simpler if continue() was supported by old CMake versions
220     set(resourcesdep "${CMAKE_CURRENT_BINARY_DIR}/${FORTARGET}_resources.cpp")
221     katie_write_file("${resourcesdep}" "enum { CompilersWorkaroundAlaAutomoc = 1 };\n")
222     set(targetresources)
223     set(rscpath "${CMAKE_CURRENT_BINARY_DIR}/${FORTARGET}_resources")
224     include_directories("${rscpath}")
225     foreach(tmpres ${ARGN})
226         get_filename_component(resource "${tmpres}" ABSOLUTE)
227         get_filename_component(rscext "${resource}" EXT)
228         get_filename_component(rscname "${resource}" NAME_WE)
229         if("${rscext}" STREQUAL ".ui")
230             set(rscout "${rscpath}/ui_${rscname}.h")
231             set(targetresources ${targetresources} "${rscout}")
232             make_directory("${rscpath}")
233             add_custom_command(
234                 COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_BINARY_DIR}/bin/${KATIE_UIC}${KATIE_TOOLS_SUFFIX}" "${resource}" -o "${rscout}"
235                 DEPENDS "${KATIE_UIC}"
236                 OUTPUT "${rscout}"
237             )
238         elseif("${rscext}" STREQUAL ".qrc")
239             set(rscout "${rscpath}/qrc_${rscname}.cpp")
240             set(targetresources ${targetresources} "${rscout}")
241             make_directory("${rscpath}")
242             add_custom_command(
243                 COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_BINARY_DIR}/bin/${KATIE_RCC}${KATIE_TOOLS_SUFFIX}" "${resource}" -o "${rscout}" -name "${rscname}"
244                 DEPENDS "${KATIE_RCC}"
245                 OUTPUT "${rscout}"
246             )
247         elseif("${rscext}" MATCHES "(.h|.hpp|.cc|.cpp)")
248             file(READ "${resource}" rsccontent)
249             if("${rsccontent}" MATCHES "(Q_OBJECT|Q_OBJECT_FAKE|Q_GADGET)")
250                 set(rscout "${rscpath}/moc_${rscname}${rscext}")
251                 set(targetresources ${targetresources} "${rscout}")
252                 get_directory_property(dirdefs COMPILE_DEFINITIONS)
253                 get_directory_property(dirincs INCLUDE_DIRECTORIES)
254                 set(mocargs)
255                 # COMPILE_DEFINITIONS does not include undefine definitions
256                 foreach(ddef ${dirdefs})
257                     set(mocargs ${mocargs} -D${ddef})
258                 endforeach()
259                 foreach(incdir ${dirincs})
260                     set(mocargs ${mocargs} -I${incdir})
261                 endforeach()
262                 make_directory("${rscpath}")
263                 add_custom_command(
264                     COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_BINARY_DIR}/bin/${KATIE_MOC}" -nw "${resource}" -o "${rscout}" ${mocargs}
265                     DEPENDS "${KATIE_MOC}"
266                     OUTPUT "${rscout}"
267                 )
268             endif()
269         elseif("${rscext}" MATCHES ".ts")
270             make_directory("${CMAKE_CURRENT_BINARY_DIR}")
271             set(rscout "${CMAKE_CURRENT_BINARY_DIR}/${rscname}.qm")
272             add_custom_target(
273                 ${FORTARGET}_${rscname} ALL
274                 COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_BINARY_DIR}/bin/${KATIE_LRELEASE}${KATIE_TOOLS_SUFFIX}" "${resource}" -qm "${rscout}"
275                 DEPENDS "${KATIE_LRELEASE}"
276             )
277             set_source_files_properties("${rscout}" PROPERTIES GENERATED TRUE)
278             install(
279                 FILES "${rscout}"
280                 DESTINATION "${KATIE_TRANSLATIONS_PATH}"
281                 COMPONENT Runtime
282             )
283         endif()
284     endforeach()
285     set_property(SOURCE "${resourcesdep}" APPEND PROPERTY OBJECT_DEPENDS "${targetresources}")
286
287     if(NOT KATIE_ALLINONE)
288         set(filteredsources)
289         foreach(srcstring ${ARGN})
290             get_filename_component(srcext "${srcstring}" EXT)
291             if(NOT "${srcext}" MATCHES "(.qrc|.ui)")
292                 set(filteredsources ${filteredsources} "${srcstring}")
293             endif()
294         endforeach()
295         set(${FORTARGET}_SOURCES "${resourcesdep}" ${filteredsources} PARENT_SCOPE)
296     else()
297         set(allinonesource "${CMAKE_CURRENT_BINARY_DIR}/${FORTARGET}_allinone.cpp")
298         set(allinonedata)
299         set(excludesources)
300         foreach(srcstring ${ARGN})
301             get_filename_component(srcext "${srcstring}" EXT)
302             get_source_file_property(skip "${srcstring}" ALLINONE_EXCLUDE)
303             if(skip OR "${srcext}" STREQUAL ".c")
304                 katie_warning("Source is excluded: ${srcstring}")
305                 set(excludesources ${excludesources} "${srcstring}")
306             elseif(NOT "${srcext}" MATCHES "(.h|.qrc|.ui)")
307                 set(allinonedata "${allinonedata}#include \"${srcstring}\"\n")
308             endif()
309         endforeach()
310         katie_write_file("${allinonesource}" "${allinonedata}")
311         set(${FORTARGET}_SOURCES ${resourcesdep} "${allinonesource}" ${excludesources} PARENT_SCOPE)
312     endif()
313 endfunction()
314
315 # a macro to ensure that object targets are build with PIC if the target they
316 # are going to be used in (like $<TARGET_OBJECTS:foo>) is build with PIC or
317 # PIC has been enabled for all module/library/executable targets. in addition
318 # the macro will add the object include directories and definitions to the
319 # target properties
320 macro(KATIE_SETUP_OBJECT FORTARGET)
321     get_target_property(target_pic ${FORTARGET} POSITION_INDEPENDENT_CODE)
322     if(CMAKE_POSITION_INDEPENDENT_CODE OR target_pic)
323         foreach(objtarget ${ARGN})
324             set_target_properties(${objtarget} PROPERTIES
325                 POSITION_INDEPENDENT_CODE TRUE
326             )
327         endforeach()
328     endif()
329
330     foreach(objtarget ${ARGN})
331         get_target_property(object_definitions ${objtarget} COMPILE_DEFINITIONS)
332         get_target_property(object_includes ${objtarget} INCLUDE_DIRECTORIES)
333         if(object_definitions)
334             target_compile_definitions(${FORTARGET} PRIVATE ${object_definitions})
335         endif()
336         target_include_directories(${FORTARGET} PRIVATE ${object_includes})
337     endforeach()
338 endmacro()
339
340 # a macro to remove conditional code from headers which is only relevant to the
341 # process of building Katie itself
342 macro(KATIE_OPTIMIZE_HEADERS DIR)
343     find_program(unifdef NAMES unifdef)
344     if(unifdef)
345         install(
346             CODE "set(UNIFDEF_EXECUTABLE \"${unifdef}\")"
347             CODE "set(HEADERS_DIRECTORY \"${DIR}\")"
348             CODE "set(HEADERS_DEFINITIONS \"${ARGN}\")"
349             SCRIPT "${CMAKE_SOURCE_DIR}/cmake/modules/OptimizeHeaders.cmake"
350         )
351     else()
352         get_filename_component(basename "${DIR}" NAME)
353         message(WARNING "unifdef not installed, cannot optimize headers for: ${basename}")
354     endif()
355 endmacro()
356
357 # a macro to add tests easily by setting them up with the assumptions they make
358 macro(KATIE_TEST TESTNAME TESTSOURCES)
359     katie_setup_target(${TESTNAME} ${TESTSOURCES} ${ARGN})
360
361     add_executable(${TESTNAME} ${${TESTNAME}_SOURCES})
362
363     target_link_libraries(${TESTNAME} KtCore KtTest)
364     target_compile_definitions(
365         ${TESTNAME} PRIVATE
366         -DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}/"
367     )
368     set_target_properties(
369         ${TESTNAME} PROPERTIES
370         RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
371     )
372
373     add_test(
374         NAME ${TESTNAME}
375         COMMAND "${CMAKE_BINARY_DIR}/exec.sh" "${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}" -tickcounter
376     )
377 endmacro()
378
379 # a macro to add tests that require GUI easily by setting them up with the assumptions they make
380 macro(KATIE_GUI_TEST TESTNAME TESTSOURCES)
381     katie_test(${TESTNAME} ${TESTSOURCES} ${ARGN})
382
383     target_link_libraries(${TESTNAME} KtGui)
384     target_compile_definitions(
385         ${TESTNAME} PRIVATE
386         -DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}/"
387         -DQT_GUI_LIB
388     )
389 endmacro()