# Copyright (c) 2015-2019, Ivailo Monev, # Redistribution and use is allowed according to the terms of the BSD license. set(KATIE_UIC "uic") set(KATIE_RCC "rcc") set(KATIE_MOC "bootstrap_moc") set(KATIE_LRELEASE "lrelease") set(KATIE_QDBUSXML2CPP "qdbusxml2cpp") # a macro to print a dev warning but only when the build type is Debug macro(KATIE_WARNING MESSAGESTR) if(CMAKE_BUILD_TYPE STREQUAL "Debug") message(AUTHOR_WARNING "${MESSAGESTR} ${ARGN}") endif() endmacro() # a macro to create camel-case headers pointing to their lower-case alternative # with a dev warning for possibly non-existent headers macro(KATIE_GENERATE_PUBLIC PUBLIC_INCLUDES SUBDIR) set(metaout ${CMAKE_BINARY_DIR}/include/${SUBDIR}/${SUBDIR}) set(metadata "#ifndef Qt${SUBDIR}_META_H\n#define Qt${SUBDIR}_META_H\n\n") foreach(pubheader ${PUBLIC_INCLUDES}) string(TOLOWER ${pubheader} pubname) get_filename_component(pubname ${pubname} NAME_WE) set(pubout ${CMAKE_BINARY_DIR}/include/${SUBDIR}/${pubheader}) set(pubcheck ${CMAKE_BINARY_DIR}/include/${SUBDIR}/${pubname}.h) if(NOT EXISTS ${pubout}) file(WRITE ${pubout} "#include <${pubname}.h>\n") endif() if(NOT EXISTS ${pubcheck}) katie_warning("Bogus public header: ${pubheader} (${pubcheck})") # qttestgui is exception because it requires explicit gui component linkage elseif(NOT "${pubname}" STREQUAL "qttestgui") set(metadata "${metadata}#include <${SUBDIR}/${pubname}.h>\n") endif() endforeach(pubheader) set(metadata "${metadata}\n#endif\n") file(WRITE ${metaout} "${metadata}") endmacro() # a macro to copy headers into specific directory based on their base names # ultimately suitable for copy operation of their destination macro(KATIE_GENERATE_MISC MISC_INCLUDES SUBDIR) foreach(mischeader ${MISC_INCLUDES}) get_filename_component(headername ${mischeader} NAME) if("${headername}" MATCHES "(_p.h)") set(headout ${CMAKE_BINARY_DIR}/privateinclude/${SUBDIR}/${headername}) else() set(headout ${CMAKE_BINARY_DIR}/include/${SUBDIR}/${headername}) endif() configure_file(${mischeader} ${headout} COPYONLY) endforeach(mischeader) endmacro() macro(KATIE_GENERATE_OBSOLETE OBSOLETE_INCLUDE SUBDIR REDIRECT) set(pubout ${CMAKE_BINARY_DIR}/include/${SUBDIR}/${OBSOLETE_INCLUDE}) if(NOT EXISTS "${pubout}") file(WRITE "${pubout}" "#include <${SUBDIR}/${REDIRECT}>\n") endif() endmacro() # a macro for creating pkgconfig files for major components macro(KATIE_GENERATE_PACKAGE FORTARGET REQUIRES) string(REPLACE "Kt" "Qt" PACKAGE_FAKE "${FORTARGET}") set(PACKAGE_NAME ${FORTARGET}) set(PACKAGE_REQUIRES ${REQUIRES}) string(REPLACE "Kt" "" compname "${FORTARGET}") string(TOUPPER ${compname} compname) set(PACKAGE_FLAGS "-DQT_${compname}_LIB") # adding the definitions to other components is simply redundant since # all components require the core component if("${FORTARGET}" STREQUAL "KtCore") katie_string_wrap("${KATIE_DEFINITIONS}" KATIE_DEFINITIONS) set(PACKAGE_FLAGS "${KATIE_DEFINITIONS}") endif() configure_file( ${CMAKE_SOURCE_DIR}/cmake/pkgconfig.cmake ${CMAKE_BINARY_DIR}/pkgconfig/${FORTARGET}.pc ) katie_setup_paths() install( FILES ${CMAKE_BINARY_DIR}/pkgconfig/${FORTARGET}.pc DESTINATION ${KATIE_PKGCONFIG_RELATIVE} COMPONENT Devel ) endmacro() # a function to ensure that (1) the output string is not null so that when it # is passed to another function/macro it does not complain about inproper # number of arguments and (2) it joins the input which if quoted has semicolons # in it (if it is a list) that the sub-command (e.g. gcc) can not handle function(KATIE_STRING_WRAP INVAR OUTSTR) string(STRIP "${INVAR}" instrtrimmed) if("${instrtrimmed}" STREQUAL "") set(${OUTSTR} " " PARENT_SCOPE) else() string(REPLACE ";" " " modstring "${INVAR}") set(${OUTSTR} "${modstring}" PARENT_SCOPE) endif() endfunction() # a function to convert string to list, opposite of katie_string_wrap() function(KATIE_STRING_UNWRAP INSTR OUTLST) string(STRIP "${INSTR}" instrtrimmed) if("${instrtrimmed}" STREQUAL "") set(${OUTLST} " " PARENT_SCOPE) else() string(REPLACE " " ";${ARGN}" modstring "${INSTR}") set(${OUTLST} ${modstring} PARENT_SCOPE) endif() endfunction() # a function to get the Git checkout hash and store it in a variable function(KATIE_GIT_CHECKOUT GITEXE OUTSTR) if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git") set(${OUTSTR} "unknown" PARENT_SCOPE) elseif(NOT GITEXE) message(WARNING "Git was not found, unable to obtain checkout.\n") set(${OUTSTR} "unknown" PARENT_SCOPE) else() execute_process( COMMAND ${GITEXE} rev-parse HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE git_result ERROR_VARIABLE git_output OUTPUT_VARIABLE git_output OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT git_result STREQUAL 0) message(WARNING "Git command failed, unable to obtain checkout:\n${git_output}") set(${OUTSTR} "unknown" PARENT_SCOPE) else() set(${OUTSTR} "${git_output}" PARENT_SCOPE) endif() endif() endfunction() # a macro to instruct katie_setup_target() which sources to exclude from the # all-in-one source file macro(KATIE_ALLINONE_EXCLUDE ARG1) set_source_files_properties(${ARG1} ${ARGN} PROPERTIES ALLINONE_EXCLUDE TRUE) endmacro() # a function to create an array of source files for a target while taking into # account all-in-one target build setting up proper dependency for the # moc/uic/rcc generated resources function(KATIE_SETUP_TARGET FORTARGET) # this can be simpler if continue() was supported by old CMake versions set(resourcesdep "${CMAKE_CURRENT_BINARY_DIR}/${FORTARGET}_resources.cpp") if(NOT EXISTS "${resourcesdep}") file(WRITE "${resourcesdep}" "enum { CompilersWorkaroundAlaAutomoc = 1 };\n") endif() set(targetresources) foreach(tmpres ${ARGN}) get_filename_component(resource ${tmpres} ABSOLUTE) get_filename_component(rscext ${resource} EXT) get_filename_component(rscname ${resource} NAME_WE) get_filename_component(rscpath ${resource} PATH) string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" rscpath "${rscpath}") if("${rscext}" STREQUAL ".ui") set(rscout "${rscpath}/ui_${rscname}.h") set(targetresources ${targetresources} ${rscout}) make_directory(${rscpath}) add_custom_command( COMMAND "${KATIE_UIC}" "${resource}" -o "${rscout}" OUTPUT "${rscout}" ) elseif("${rscext}" STREQUAL ".qrc") set(rscout "${rscpath}/qrc_${rscname}.cpp") set(targetresources ${targetresources} ${rscout}) make_directory(${rscpath}) add_custom_command( COMMAND "${KATIE_RCC}" "${resource}" -o "${rscout}" -name "${rscname}" OUTPUT "${rscout}" ) elseif("${rscext}" MATCHES "(.h|.hpp|.cc|.cpp)") file(READ "${resource}" rsccontent) if("${rsccontent}" MATCHES "(Q_OBJECT|Q_OBJECT_FAKE|Q_GADGET)") set(rscout "${rscpath}/moc_${rscname}${rscext}") set(targetresources ${targetresources} ${rscout}) get_directory_property(dirdefs COMPILE_DEFINITIONS) get_directory_property(dirincs INCLUDE_DIRECTORIES) set(mocargs) foreach(ddef ${dirdefs}) # TODO: filter non -D, support -U too set(mocargs ${mocargs} -D${ddef}) endforeach() foreach(incdir ${dirincs}) set(mocargs ${mocargs} -I${incdir}) endforeach() make_directory(${rscpath}) add_custom_command( COMMAND "${KATIE_MOC}" -nw "${resource}" -o "${rscout}" ${mocargs} OUTPUT "${rscout}" ) endif() endif() endforeach() set_source_files_properties(${resourcesdep} PROPERTIES OBJECT_DEPENDS "${targetresources}") if(NOT KATIE_ALLINONE) set(filteredsources) foreach(srcstring ${ARGN}) get_filename_component(srcext ${srcstring} EXT) if(NOT "${srcext}" MATCHES "(.qrc|.ui)") set(filteredsources ${filteredsources} ${srcstring}) endif() endforeach() set(${FORTARGET}_SOURCES ${resourcesdep} ${filteredsources} PARENT_SCOPE) else() # use temporary file, then configure_file() to avoid all-in-one target rebuilds when the # project is reconfigured set(allinonetemp "${CMAKE_CURRENT_BINARY_DIR}/allinone_temporary.cpp") set(allinonesource "${CMAKE_CURRENT_BINARY_DIR}/${FORTARGET}_allinone.cpp") set(allinonedata) set(excludesources) foreach(srcstring ${ARGN}) get_filename_component(srcext ${srcstring} EXT) get_source_file_property(skip ${srcstring} ALLINONE_EXCLUDE) if(skip OR "${srcext}" STREQUAL ".c") katie_warning("Source is excluded: ${srcstring}") set(excludesources ${excludesources} ${srcstring}) elseif(NOT "${srcext}" MATCHES "(.h|.qrc|.ui)") set(allinonedata "${allinonedata}#include \"${srcstring}\"\n") endif() endforeach() file(WRITE "${allinonetemp}" "${allinonedata}") configure_file("${allinonetemp}" "${allinonesource}") set(${FORTARGET}_SOURCES ${resourcesdep} ${allinonesource} ${excludesources} PARENT_SCOPE) endif() endfunction() # a macro to ensure that object targets are build with PIC if the target they # are going to be used in (like $) is build with PIC or # PIC has been enabled for all module/library/executable targets macro(KATIE_SETUP_OBJECT FORTARGET) get_target_property(targets_pic ${FORTARGET} POSITION_INDEPENDENT_CODE) if(CMAKE_POSITION_INDEPENDENT_CODE OR targets_pic) foreach(objtarget ${ARGN}) set_target_properties(${objtarget} PROPERTIES POSITION_INDEPENDENT_CODE TRUE ) endforeach() endif() endmacro() # a function to change full installation paths to relative so that CPack # generators do not choke, still paths must contain a string of some sort that # is not just CMAKE_INSTALL_PREFIX - if they are null after they have been made # relative even quoting them will not help and CMake will complain that not # enought arguments have been passed to install() for DESTINATION function(KATIE_SETUP_PATHS) set(instpaths _PREFIX _HEADERS _LIBRARIES _BINARIES _PLUGINS _IMPORTS _DATA _TRANSLATIONS _SETTINGS _CMAKE _LDCONF _PROFILE _MAN _APPLICATIONS _PIXMAPS _PKGCONFIG _PYTHON ) foreach(instpath ${instpaths}) string(REGEX REPLACE ".*${CMAKE_INSTALL_PREFIX}/" "" modpath "${KATIE${instpath}_FULL}") string(REGEX REPLACE ".*${CMAKE_INSTALL_PREFIX}" "" modpath "${modpath}") set(KATIE${instpath}_RELATIVE "${modpath}" PARENT_SCOPE) # message(STATUS "KATIE${instpath}_RELATIVE: ${modpath}") endforeach() endfunction() # a macro to remove conditional code from headers which is only relevant to the # process of building Katie itself macro(KATIE_OPTIMIZE_HEADERS DIR) find_program(unifdef NAMES unifdef) if(unifdef) install( CODE "set(UNIFDEF_EXECUTABLE \"${unifdef}\")" CODE "set(HEADERS_DIRECTORY \"${DIR}\")" CODE "set(HEADERS_DEFINITIONS \"${ARGN}\")" SCRIPT "${CMAKE_SOURCE_DIR}/cmake/modules/OptimizeHeaders.cmake" ) else() get_filename_component(basename ${DIR} NAME) katie_warning("unifdef not installed, cannot optimize headers for: ${basename}") endif() endmacro() # a macro to add tests easily by setting them up with the assumptions they make macro(KATIE_TEST TESTNAME TESTSOURCES) katie_resources(${TESTSOURCES} ${ARGN}) add_executable(${TESTNAME} ${TESTSOURCES} ${ARGN}) target_link_libraries(${TESTNAME} KtCore KtTest) target_compile_definitions( ${TESTNAME} PRIVATE -DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}/" ) set_target_properties( ${TESTNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) add_test( NAME ${TESTNAME} COMMAND ${CMAKE_BINARY_DIR}/runtest.sh ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} ) endmacro() # a macro to add tests easily by setting them up with the assumptions they make macro(KATIE_GUI_TEST TESTNAME TESTSOURCES) katie_resources(${TESTSOURCES} ${ARGN}) add_executable(${TESTNAME} ${TESTSOURCES} ${ARGN}) target_link_libraries(${TESTNAME} KtCore KtGui KtTest) target_compile_definitions( ${TESTNAME} PRIVATE -DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}/" -DQT_GUI_LIB ) set_target_properties( ${TESTNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) add_test( NAME ${TESTNAME} COMMAND ${CMAKE_BINARY_DIR}/runtest.sh ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME} ) endmacro() # a macro to generate Python bindings sources macro(KATIE_GENERATE_BINDINGS FORTARGET OUTPUTFILES) set(outputfiles ${OUTPUTFILES} ${ARGN}) set(includedirs) get_directory_property(dirincs INCLUDE_DIRECTORIES) foreach(incdir ${SHIBOKEN_INCLUDES} ${dirincs}) if(NOT includedirs) set(includedirs "${incdir}") else() set(includedirs "${includedirs}:${incdir}") endif() endforeach() get_target_property(targetsources ${FORTARGET} SOURCES) add_custom_command( COMMENT "Generating Python bindings for: ${FORTARGET}" COMMAND ${SHIBOKEN_BINARY} --use-isnull-as-nb_nonzero --enable-return-value-heuristic --enable-parent-ctor-heuristic --avoid-protected-hack --include-paths="${includedirs}" --output-directory="${CMAKE_SOURCE_DIR}/bindings" --license-file="${CMAKE_SOURCE_DIR}/header.BSD" "${CMAKE_SOURCE_DIR}/bindings/${FORTARGET}_global.hpp" "${CMAKE_SOURCE_DIR}/bindings/typesystem_${FORTARGET}.xml" DEPENDS ${FORTARGET} ${targetsources} "${CMAKE_SOURCE_DIR}/bindings/${FORTARGET}_global.hpp" "${CMAKE_SOURCE_DIR}/bindings/typesystem_${FORTARGET}.xml" "${CMAKE_SOURCE_DIR}/header.BSD" OUTPUT ${outputfiles} ) set_source_files_properties(${outputfiles} PROPERTIES GENERATED TRUE) set_source_files_properties(${outputfiles} PROPERTIES SKIP_AUTOGEN ON) endmacro()