Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# SPDX-FileCopyrightText: 2003-2018 University of Illinois at Urbana-Champaign.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
CheckAtomic
|
||||
-----------
|
||||
|
||||
Check if the compiler supports std:atomic out of the box or if libatomic is
|
||||
needed for atomic support. If it is needed libatomicis added to
|
||||
``CMAKE_REQUIRED_LIBRARIES``. So after running CheckAtomic you can use
|
||||
std:atomic.
|
||||
|
||||
Since 5.75.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckLibraryExists)
|
||||
|
||||
# Sometimes linking against libatomic is required for atomic ops, if
|
||||
# the platform doesn't support lock-free atomics.
|
||||
|
||||
function(check_working_cxx_atomics varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
std::atomic<int> x;
|
||||
std::atomic<short> y;
|
||||
std::atomic<char> z;
|
||||
int main() {
|
||||
++z;
|
||||
++y;
|
||||
return ++x;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction()
|
||||
|
||||
function(check_working_cxx_atomics64 varname)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
std::atomic<uint64_t> x (0);
|
||||
int main() {
|
||||
uint64_t i = x.load(std::memory_order_relaxed);
|
||||
return 0;
|
||||
}
|
||||
" ${varname})
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
|
||||
endfunction()
|
||||
|
||||
# Check for (non-64-bit) atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB True)
|
||||
elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE)
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
|
||||
if(HAVE_LIBATOMIC)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check for 64 bit atomic operations.
|
||||
if(MSVC)
|
||||
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
|
||||
elseif(LLVM_COMPILER_IS_GCC_COMPATIBLE)
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
|
||||
if(HAVE_CXX_LIBATOMICS64)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,183 @@
|
||||
# SPDX-FileCopyrightText: 2018-2023 Aleix Pol <aleixpol@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMAddAndroidApk
|
||||
----------------
|
||||
|
||||
Functions for creating Android APK packages using Qt6's ``androiddeployqt`` tool
|
||||
as well as the associated Fastlane metadata.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_android_apk(<target>
|
||||
[ANDROID_DIR <dir>]
|
||||
[PACKAGE_NAME <name>]
|
||||
# TODO extra args?
|
||||
)
|
||||
|
||||
Creates an Android APK for the given target.
|
||||
|
||||
If ``ANDROID_DIR`` is given, the Android manifest file as well as any potential
|
||||
Gradle build system files or Java/Kotlin source files are taken from that directory.
|
||||
If not set, the standard template shipped with Qt6 is used, which in usually not
|
||||
what you want for production applications.
|
||||
|
||||
If ``PACKAGE_NAME`` is given, it is used as name for the Android APK.
|
||||
If not set, the ``target`` name is used. Since 6.10.0
|
||||
|
||||
The use of this function creates a build target called ``create-apk-<target>``
|
||||
which will run ``androiddeployqt`` to produce an (unsigned) APK, as well
|
||||
as convert Appstream application metadata (if present) into the Fastlane format used
|
||||
by F-Droid and Play store automation.
|
||||
|
||||
There's also a ``create-apk`` convenience target being created that
|
||||
will build all APKs defined in a project.
|
||||
|
||||
When building for another platform than Android, this function does nothing.
|
||||
|
||||
The following variables impact the behavior:
|
||||
``ECM_ADDITIONAL_FIND_ROOT_PATH``
|
||||
See documentation in the Android toolchain file.
|
||||
|
||||
``ECM_APK_STAGING_ROOT_PATH``
|
||||
For use with Craft's image directory. If set this is used as the source
|
||||
for all content of the APK rather than the search paths used for building.
|
||||
This allows to separate e.g. development files from what ends up in the APK.
|
||||
|
||||
Since 6.0.0
|
||||
#]=======================================================================]
|
||||
|
||||
# make ExecuteCoreModules test pass on Qt5
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../modules/QtVersionOption.cmake)
|
||||
if (QT_MAJOR_VERSION EQUAL 5)
|
||||
message(WARNING "ECMAddAndroidApk is not compatible with Qt5 - skipping.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Qt6Core REQUIRED) # required for the following to work stand-alone
|
||||
find_package(Qt6CoreTools REQUIRED)
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
|
||||
set(_ECM_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}/../toolchain")
|
||||
|
||||
function (ecm_add_android_apk TARGET)
|
||||
set(oneValueArgs ANDROID_DIR PACKAGE_NAME)
|
||||
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN})
|
||||
if (NOT ANDROID)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(APK_NAME "${TARGET}")
|
||||
if (ARGS_PACKAGE_NAME)
|
||||
set(APK_NAME "${ARGS_PACKAGE_NAME}")
|
||||
endif()
|
||||
|
||||
set(APK_OUTPUT_DIR "${CMAKE_BINARY_DIR}/${TARGET}_build_apk/")
|
||||
set(APK_EXECUTABLE_PATH "${APK_OUTPUT_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/lib${APK_NAME}_${CMAKE_ANDROID_ARCH_ABI}.so")
|
||||
|
||||
set(QML_IMPORT_PATHS "")
|
||||
# add build directory to the search path as well, so this works without installation
|
||||
if (EXISTS ${CMAKE_BINARY_DIR}/lib)
|
||||
set(QML_IMPORT_PATHS ${CMAKE_BINARY_DIR}/lib)
|
||||
endif()
|
||||
if (ECM_APK_STAGING_ROOT_PATH)
|
||||
set(QML_IMPORT_PATHS "${ECM_APK_STAGING_ROOT_PATH}/lib/qml")
|
||||
set(ANDROID_QT6_INSTALL_PREFIX ${ECM_APK_STAGING_ROOT_PATH})
|
||||
else()
|
||||
foreach(prefix ${ECM_ADDITIONAL_FIND_ROOT_PATH})
|
||||
# qmlimportscanner chokes on symlinks, so we need to resolve those first
|
||||
get_filename_component(qml_path "${prefix}/lib/qml" REALPATH)
|
||||
if(EXISTS ${qml_path})
|
||||
if (QML_IMPORT_PATHS)
|
||||
set(QML_IMPORT_PATHS "${QML_IMPORT_PATHS},${qml_path}")
|
||||
else()
|
||||
set(QML_IMPORT_PATHS "${qml_path}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
set(ANDROID_QT6_INSTALL_PREFIX ${QT6_INSTALL_PREFIX})
|
||||
endif()
|
||||
if (QML_IMPORT_PATHS)
|
||||
set(DEFINE_QML_IMPORT_PATHS "\"qml-import-paths\": \"${QML_IMPORT_PATHS}\",")
|
||||
endif()
|
||||
|
||||
set(EXTRA_PREFIX_DIRS "\"${CMAKE_BINARY_DIR}\"")
|
||||
set(EXTRA_LIB_DIRS "\"${CMAKE_BINARY_DIR}/lib\"")
|
||||
if (ECM_APK_STAGING_ROOT_PATH)
|
||||
set(EXTRA_PREFIX_DIRS "${EXTRA_PREFIX_DIRS}, \"${ECM_APK_STAGING_ROOT_PATH}\"")
|
||||
set(EXTRA_LIB_DIRS "${EXTRA_LIB_DIRS}, \"${ECM_APK_STAGING_ROOT_PATH}/lib\"")
|
||||
else()
|
||||
foreach(prefix ${ECM_ADDITIONAL_FIND_ROOT_PATH})
|
||||
set(EXTRA_PREFIX_DIRS "${EXTRA_PREFIX_DIRS}, \"${prefix}\"")
|
||||
set(EXTRA_LIB_DIRS "${EXTRA_LIB_DIRS}, \"${prefix}/lib\"")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if (ARGS_ANDROID_DIR AND EXISTS ${ARGS_ANDROID_DIR}/AndroidManifest.xml)
|
||||
set(ANDROID_APK_DIR ${ARGS_ANDROID_DIR})
|
||||
else()
|
||||
message("Using default Qt APK template - this is often not intentional!")
|
||||
get_filename_component(_qtCore_install_prefix "${Qt6Core_DIR}/../../../" ABSOLUTE)
|
||||
set(ANDROID_APK_DIR "${_qtCore_install_prefix}/src/android/templates/")
|
||||
endif()
|
||||
|
||||
get_target_property(QT6_RCC_BINARY Qt6::rcc LOCATION)
|
||||
string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" _LOWER_CMAKE_HOST_SYSTEM_NAME)
|
||||
configure_file("${_ECM_TOOLCHAIN_DIR}/deployment-file-qt6.json.in" "${CMAKE_BINARY_DIR}/${APK_NAME}-deployment.json.in")
|
||||
|
||||
if (NOT TARGET create-apk)
|
||||
add_custom_target(create-apk)
|
||||
if (NOT DEFINED ANDROID_FASTLANE_METADATA_OUTPUT_DIR)
|
||||
set(ANDROID_FASTLANE_METADATA_OUTPUT_DIR ${CMAKE_BINARY_DIR}/fastlane)
|
||||
endif()
|
||||
add_custom_target(create-fastlane
|
||||
COMMAND Python3::Interpreter ${_ECM_TOOLCHAIN_DIR}/generate-fastlane-metadata.py --output ${ANDROID_FASTLANE_METADATA_OUTPUT_DIR} --source ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED ANDROID_APK_OUTPUT_DIR)
|
||||
set(ANDROID_APK_OUTPUT_DIR ${APK_OUTPUT_DIR})
|
||||
endif()
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
|
||||
set(arguments "\\$(ARGS)")
|
||||
endif()
|
||||
|
||||
if (NOT ECM_APK_STAGING_ROOT_PATH)
|
||||
set(ECM_APK_STAGING_ROOT_PATH "${CMAKE_INSTALL_PREFIX}")
|
||||
endif()
|
||||
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/ranlib "${CMAKE_RANLIB}")
|
||||
set(CREATEAPK_TARGET_NAME "create-apk-${APK_NAME}")
|
||||
set(APK_NAME_FULL "${APK_NAME}-${CMAKE_ANDROID_ARCH_ABI}.apk")
|
||||
add_custom_target(${CREATEAPK_TARGET_NAME}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Generating ${APK_NAME_FULL} with $<TARGET_FILE:Qt6::androiddeployqt>"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${APK_OUTPUT_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${TARGET}>" "${APK_EXECUTABLE_PATH}"
|
||||
COMMAND LANG=C ${CMAKE_COMMAND} "-DTARGET=$<TARGET_FILE:${TARGET}>" -P ${_ECM_TOOLCHAIN_DIR}/hasMainSymbol.cmake
|
||||
COMMAND LANG=C ${CMAKE_COMMAND}
|
||||
-DINPUT_FILE="${CMAKE_BINARY_DIR}/${APK_NAME}-deployment.json.in"
|
||||
-DOUTPUT_FILE="${CMAKE_BINARY_DIR}/${APK_NAME}-deployment.json"
|
||||
"-DTARGET=$<TARGET_FILE:${TARGET}>"
|
||||
"-DOUTPUT_DIR=$<TARGET_FILE_DIR:${TARGET}>"
|
||||
"-DEXPORT_DIR=${ECM_APK_STAGING_ROOT_PATH}"
|
||||
"-DECM_ADDITIONAL_FIND_ROOT_PATH=\"${ECM_ADDITIONAL_FIND_ROOT_PATH}\""
|
||||
-P ${_ECM_TOOLCHAIN_DIR}/specifydependencies.cmake
|
||||
COMMAND Qt6::androiddeployqt
|
||||
${ANDROIDDEPLOYQT_EXTRA_ARGS}
|
||||
--gradle
|
||||
--input "${CMAKE_BINARY_DIR}/${APK_NAME}-deployment.json"
|
||||
--apk "${ANDROID_APK_OUTPUT_DIR}/${APK_NAME_FULL}"
|
||||
--output "${APK_OUTPUT_DIR}"
|
||||
--android-platform android-${ANDROID_SDK_COMPILE_API}
|
||||
--deployment bundled
|
||||
--qml-importscanner-binary $<TARGET_FILE:Qt6::qmlimportscanner>
|
||||
${arguments}
|
||||
)
|
||||
|
||||
add_dependencies(create-apk ${CREATEAPK_TARGET_NAME})
|
||||
add_dependencies(${CREATEAPK_TARGET_NAME} create-fastlane)
|
||||
endfunction()
|
||||
@@ -0,0 +1,378 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
# SPDX-FileCopyrightText: 2006-2009 Alexander Neundorf <neundorf@kde.org>
|
||||
# SPDX-FileCopyrightText: 2006, 2007 Laurent Montel <montel@kde.org>
|
||||
# SPDX-FileCopyrightText: 2007 Matthias Kretz <kretz@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMAddAppIcon
|
||||
-------------
|
||||
|
||||
Add icons to executable files and packages.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_app_icon(<sources_var_name(|target (since 5.83))>
|
||||
ICONS <icon> [<icon> [...]]
|
||||
[SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49
|
||||
[OUTFILE_BASENAME <name>]) # Since 5.49
|
||||
)
|
||||
|
||||
The given icons, whose names must match the pattern::
|
||||
|
||||
<size>-<other_text>.png
|
||||
|
||||
will be added as platform-specific application icons
|
||||
to the variable named ``<sources_var_name>`` or, if the first argument
|
||||
is a target (since 5.83), to the ``SOURCES`` property of ``<target>``.
|
||||
Any target must be created with add_executable() and not be an alias.
|
||||
|
||||
Other icon files are ignored but on macOS SVG files can be supported and
|
||||
it is thus possible to mix those with png files in a single macro call.
|
||||
|
||||
The platforms currently supported are Windows and macOS, on all others
|
||||
the call has no effect and is ignored.
|
||||
|
||||
``<size>`` is a numeric pixel size (typically 16, 32, 48, 64, 128 or 256).
|
||||
``<other_text>`` can be any other text. See the platform notes below for any
|
||||
recommendations about icon sizes.
|
||||
|
||||
``SIDEBAR_ICONS`` can be used to add macOS sidebar
|
||||
icons to the generated iconset. They are used when a folder monitored by the
|
||||
application is dragged into Finder's sidebar. Since 5.49.
|
||||
|
||||
``OUTFILE_BASENAME`` will be used as the basename for the icon file. If
|
||||
you specify it, the icon file will be called ``<OUTFILE_BASENAME>.icns`` on macOS
|
||||
and ``<OUTFILE_BASENAME>.ico`` on Windows. If you don't specify it, it defaults
|
||||
to ``<sources_var_name>.<ext>``. Since 5.49.
|
||||
|
||||
|
||||
Windows notes
|
||||
* Icons are compiled into the executable using a resource file.
|
||||
* Icons may not show up in Windows Explorer if the executable
|
||||
target does not have the ``WIN32_EXECUTABLE`` property set.
|
||||
* Icotool (see :find-module:`FindIcoTool`) is required.
|
||||
* Supported sizes: 16, 24, 32, 48, 64, 128, 256, 512 and 1024.
|
||||
|
||||
macOS notes
|
||||
* The executable target must have the ``MACOSX_BUNDLE`` property set.
|
||||
* Icons are added to the bundle.
|
||||
* If the ksvg2icns tool from KIconThemes is available, .svg and .svgz
|
||||
files are accepted; the first that is converted successfully to .icns
|
||||
will provide the application icon. SVG files are ignored otherwise.
|
||||
* The tool iconutil (provided by Apple) is required for bitmap icons.
|
||||
* Supported sizes: 16, 32, 64, 128, 256 (and 512, 1024 after OS X 10.9).
|
||||
* At least a 128x128px (or an SVG) icon is required.
|
||||
* Larger sizes are automatically used to substitute for smaller sizes on
|
||||
"Retina" (high-resolution) displays. For example, a 32px icon, if
|
||||
provided, will be used as a 32px icon on standard-resolution displays,
|
||||
and as a 16px-equivalent icon (with an "@2x" tag) on high-resolution
|
||||
displays. That is why you should provide 64px and 1024px icons although
|
||||
they are not supported anymore directly. Instead they will be used as
|
||||
32px@2x and 512px@2x. If an SVG icon is provided, ksvg2icns will be
|
||||
used internally to automatically generate all appropriate sizes,
|
||||
including the high-resolution ones.
|
||||
* This function sets the ``MACOSX_BUNDLE_ICON_FILE`` variable to the name
|
||||
of the generated icns file, so that it will be used as the
|
||||
``MACOSX_BUNDLE_ICON_FILE`` target property when you call
|
||||
``add_executable``.
|
||||
* Sidebar icons should typically provided in 16, 32, 64, 128 and 256px.
|
||||
|
||||
Since 1.7.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ecm_add_app_icon appsources_or_target)
|
||||
set(options)
|
||||
set(oneValueArgs OUTFILE_BASENAME)
|
||||
set(multiValueArgs ICONS SIDEBAR_ICONS)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT ARG_ICONS)
|
||||
message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
|
||||
endif()
|
||||
if (TARGET ${appsources_or_target})
|
||||
get_target_property(target_type ${appsources_or_target} TYPE)
|
||||
if (NOT target_type STREQUAL "EXECUTABLE")
|
||||
message(FATAL_ERROR "Target argument passed to ecm_add_app_icon is not an executable: ${appsources_or_target}")
|
||||
endif()
|
||||
get_target_property(aliased_target ${appsources_or_target} ALIASED_TARGET)
|
||||
if(aliased_target)
|
||||
message(FATAL_ERROR "Target argument passed to ecm_add_app_icon must not be an alias: ${appsources_or_target}")
|
||||
endif()
|
||||
endif()
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_add_app_icon: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
find_program(KSVG2ICNS NAMES ksvg2icns)
|
||||
foreach(icon ${ARG_ICONS})
|
||||
get_filename_component(icon_full ${icon} ABSOLUTE)
|
||||
get_filename_component(icon_type ${icon_full} EXT)
|
||||
# do we have ksvg2icns in the path and did we receive an svg (or compressed svg) icon?
|
||||
if(KSVG2ICNS AND (${icon_type} STREQUAL ".svg" OR ${icon_type} STREQUAL ".svgz"))
|
||||
# convert the svg icon to an icon resource
|
||||
execute_process(COMMAND ${KSVG2ICNS} "${icon_full}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE KSVG2ICNS_ERROR)
|
||||
if(${KSVG2ICNS_ERROR})
|
||||
message(AUTHOR_WARNING "ksvg2icns could not generate an OS X application icon from ${icon}")
|
||||
else()
|
||||
# install the icns file we just created
|
||||
get_filename_component(icon_name ${icon_full} NAME_WE)
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${icon_name}.icns PARENT_SCOPE)
|
||||
if (TARGET ${appsources_or_target})
|
||||
target_sources(${appsources_or_target} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns")
|
||||
else()
|
||||
set(${appsources_or_target} "${${appsources_or_target}};${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns" PARENT_SCOPE)
|
||||
endif()
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
# we're done now
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024")
|
||||
if(ARG_SIDEBAR_ICONS)
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;32;64;128;256")
|
||||
endif()
|
||||
|
||||
set(mac_icons
|
||||
# Icons: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
|
||||
${icons_at_16px}
|
||||
${icons_at_32px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px}
|
||||
${icons_at_256px}
|
||||
${icons_at_512px}
|
||||
${icons_at_1024px})
|
||||
|
||||
set(mac_sidebar_icons
|
||||
# Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
|
||||
${sidebar_icons_at_16px}
|
||||
${sidebar_icons_at_32px}
|
||||
${sidebar_icons_at_64px}
|
||||
${sidebar_icons_at_128px}
|
||||
${sidebar_icons_at_256px})
|
||||
|
||||
if (NOT (mac_icons OR mac_sidebar_icons))
|
||||
message(AUTHOR_WARNING "No icons suitable for use on macOS provided")
|
||||
endif()
|
||||
|
||||
|
||||
set(windows_icons_classic ${icons_at_16px}
|
||||
${icons_at_24px}
|
||||
${icons_at_32px}
|
||||
${icons_at_48px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px})
|
||||
set(windows_icons_modern ${windows_icons_classic}
|
||||
${icons_at_256px}
|
||||
${icons_at_512px}
|
||||
${icons_at_1024px})
|
||||
|
||||
if (NOT (windows_icons_modern OR windows_icons_classic))
|
||||
message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
|
||||
endif()
|
||||
|
||||
if (ARG_OUTFILE_BASENAME)
|
||||
set (_outfilebasename "${ARG_OUTFILE_BASENAME}")
|
||||
else()
|
||||
set (_outfilebasename "${appsources_or_target}")
|
||||
endif()
|
||||
set (_outfilename "${CMAKE_CURRENT_BINARY_DIR}/${_outfilebasename}")
|
||||
|
||||
if (WIN32 AND (windows_icons_modern OR windows_icons_classic))
|
||||
set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
|
||||
find_package(IcoTool)
|
||||
set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
|
||||
|
||||
function(create_windows_icon_and_rc command args deps)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.ico"
|
||||
COMMAND ${command}
|
||||
ARGS ${args}
|
||||
DEPENDS ${deps}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
# this bit's a little hacky to make the dependency stuff work
|
||||
file(WRITE "${_outfilename}.rc.in" "IDI_ICON1 ICON DISCARDABLE \"${_outfilename}.ico\"\n")
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.rc"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_outfilename}.rc.in" "${_outfilename}.rc"
|
||||
DEPENDS "${_outfilename}.ico"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
if (IcoTool_FOUND)
|
||||
list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
|
||||
|
||||
# According to https://stackoverflow.com/a/40851713/2886832
|
||||
# Windows always chooses the first icon above 255px, all other ones will be ignored
|
||||
set(maxSize 0)
|
||||
foreach(size 256 512 1024)
|
||||
if(icons_at_${size}px)
|
||||
set(maxSize "${size}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(size 16 24 32 48 64 128 ${maxSize})
|
||||
if(NOT icons_at_${size}px)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(icotool_icon_arg "")
|
||||
if(size STREQUAL "${maxSize}")
|
||||
# maxSize icon needs to be included as raw png
|
||||
list(APPEND icotool_args "-r")
|
||||
endif()
|
||||
|
||||
foreach(icon ${icons_at_${size}px})
|
||||
list(APPEND icotool_args "${icons_at_${size}px}")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
create_windows_icon_and_rc(IcoTool::IcoTool "${icotool_args}" "${windows_icons_modern}")
|
||||
if (TARGET ${appsources_or_target})
|
||||
target_sources(${appsources_or_target} PRIVATE "${_outfilename}.rc")
|
||||
else()
|
||||
set(${appsources_or_target} "${${appsources_or_target}};${_outfilename}.rc" PARENT_SCOPE)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Unable to find the icotool utilities or icons in matching sizes - application will not have an application icon!")
|
||||
endif()
|
||||
elseif (APPLE AND (mac_icons OR mac_sidebar_icons))
|
||||
# first generate .iconset directory structure, then convert to .icns format using the macOS "iconutil" utility,
|
||||
# to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
|
||||
# 256x256, 512x512, 1024x1024
|
||||
find_program(ICONUTIL_EXECUTABLE NAMES iconutil)
|
||||
if (ICONUTIL_EXECUTABLE)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.iconset"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E make_directory "${_outfilename}.iconset"
|
||||
)
|
||||
set(iconset_icons)
|
||||
macro(copy_icon filename sizename type)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.iconset/${type}_${sizename}.png"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E copy
|
||||
"${filename}"
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png"
|
||||
DEPENDS
|
||||
"${_outfilename}.iconset"
|
||||
"${filename}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
list(APPEND iconset_icons
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png")
|
||||
endmacro()
|
||||
|
||||
# List of supported sizes and filenames taken from:
|
||||
# https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
|
||||
foreach(size 16 32 128 256 512)
|
||||
math(EXPR double_size "2 * ${size}")
|
||||
foreach(file ${icons_at_${size}px})
|
||||
copy_icon("${file}" "${size}x${size}" "icon")
|
||||
endforeach()
|
||||
foreach(file ${icons_at_${double_size}px})
|
||||
copy_icon("${file}" "${size}x${size}@2x" "icon")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# List of supported sizes and filenames taken from:
|
||||
# https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
|
||||
foreach(file ${sidebar_icons_at_16px})
|
||||
copy_icon("${file}" "16x16" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_32px})
|
||||
copy_icon("${file}" "16x16@2x" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_32px})
|
||||
copy_icon("${file}" "18x18" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_64px})
|
||||
copy_icon("${file}" "18x18@2x" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_128px})
|
||||
copy_icon("${file}" "32x32" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_256px})
|
||||
copy_icon("${file}" "32x32@2x" "sidebar")
|
||||
endforeach()
|
||||
|
||||
# generate .icns icon file
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.icns"
|
||||
COMMAND ${ICONUTIL_EXECUTABLE}
|
||||
ARGS
|
||||
--convert icns
|
||||
--output "${_outfilename}.icns"
|
||||
"${_outfilename}.iconset"
|
||||
DEPENDS "${iconset_icons}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
# This will register the icon into the bundle
|
||||
set(MACOSX_BUNDLE_ICON_FILE "${_outfilebasename}.icns" PARENT_SCOPE)
|
||||
|
||||
# Append the icns file to the sources list so it will be a dependency to the
|
||||
# main target
|
||||
if (TARGET ${appsources_or_target})
|
||||
target_sources(${appsources_or_target} PRIVATE "${_outfilename}.icns")
|
||||
else()
|
||||
set(${appsources_or_target} "${${appsources_or_target}};${_outfilename}.icns" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
# Install the icon into the Resources dir in the bundle
|
||||
set_source_files_properties("${_outfilename}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
else()
|
||||
message(STATUS "Unable to find the iconutil utility - application will not have an application icon!")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
macro(_ecm_add_app_icon_categorize_icons icons type known_sizes)
|
||||
set(_${type}_known_sizes)
|
||||
foreach(size ${known_sizes})
|
||||
set(${type}_at_${size}px)
|
||||
list(APPEND _${type}_known_sizes ${size})
|
||||
endforeach()
|
||||
|
||||
|
||||
foreach(icon ${icons})
|
||||
get_filename_component(icon_full ${icon} ABSOLUTE)
|
||||
if (NOT EXISTS "${icon_full}")
|
||||
message(AUTHOR_WARNING "${icon_full} does not exist, ignoring")
|
||||
else()
|
||||
get_filename_component(icon_name ${icon} NAME)
|
||||
string(REGEX MATCH "([0-9]+|sc)\\-[^/]+\\.([a-z]+)$"
|
||||
_dummy "${icon_name}")
|
||||
set(size "${CMAKE_MATCH_1}")
|
||||
set(ext "${CMAKE_MATCH_2}")
|
||||
|
||||
if (NOT (ext STREQUAL "svg" OR ext STREQUAL "svgz"))
|
||||
if (NOT size)
|
||||
message(AUTHOR_WARNING "${icon_full} is not named correctly for ecm_add_app_icon - ignoring")
|
||||
elseif (NOT ext STREQUAL "png")
|
||||
message(AUTHOR_WARNING "${icon_full} is not a png file - ignoring")
|
||||
else()
|
||||
list(FIND _${type}_known_sizes ${size} offset)
|
||||
|
||||
if (offset GREATER -1)
|
||||
list(APPEND ${type}_at_${size}px "${icon_full}")
|
||||
elseif()
|
||||
message(STATUS "not found ${type}_at_${size}px ${icon_full}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
@@ -0,0 +1,798 @@
|
||||
# SPDX-FileCopyrightText: 2016-2017 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMAddQch
|
||||
------------------
|
||||
|
||||
This module provides the ``ecm_add_qch`` function for generating API
|
||||
documentation files in the QCH format, and the ``ecm_install_qch_export``
|
||||
function for generating and installing exported CMake targets for such
|
||||
generated QCH files to enable builds of other software with generation of
|
||||
QCH files to create links into the given QCH files.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_qch(<target_name>
|
||||
NAME <name>
|
||||
VERSION <version>
|
||||
QCH_INSTALL_DESTINATION <qchfile_install_path>
|
||||
TAGFILE_INSTALL_DESTINATION <tagsfile_install_path>
|
||||
[COMPONENT <component>]
|
||||
[BASE_NAME <basename>]
|
||||
[SOURCE_DIRS <dir> [<dir2> [...]]]
|
||||
[SOURCES <file> [<file2> [...]]]
|
||||
|MD_MAINPAGE <md_file>]
|
||||
[INCLUDE_DIRS <incdir> [<incdir2> [...]]]
|
||||
[IMAGE_DIRS <idir> [<idir2> [...]]]
|
||||
[EXAMPLE_DIRS <edir> [<edir2> [...]]]
|
||||
[ORG_DOMAIN <domain>]
|
||||
[NAMESPACE <namespace>]
|
||||
[LINK_QCHS <qch> [<qch2> [...]]]
|
||||
[PREDEFINED_MACROS <macro[=content]> [<macro2[=content]> [...]]]
|
||||
[BLANK_MACROS <macro> [<macro2> [...]]]
|
||||
[CONFIG_TEMPLATE <configtemplate_file>]
|
||||
[VERBOSE]
|
||||
)
|
||||
|
||||
This macro adds a target called <target_name> for the creation of an API
|
||||
documentation manual in the QCH format from the given sources.
|
||||
It currently uses doxygen, future versions might optionally also allow other
|
||||
tools.
|
||||
Next to the QCH file the target will generate a corresponding doxygen tag
|
||||
file, which enables creating links from other documentation into the
|
||||
generated QCH file.
|
||||
|
||||
It is recommended to make the use of this macro optional, by depending
|
||||
the call to ``ecm_add_qch`` on a CMake option being set, with a name like
|
||||
``BUILD_QCH`` and being ``TRUE`` by default. This will allow the developers to
|
||||
saves resources on normal source development build cycles by setting this
|
||||
option to FALSE.
|
||||
|
||||
The macro will set the target properties ``DOXYGEN_TAGFILE``, ``QHP_NAMESPACE``,
|
||||
``QHP_NAMESPACE_VERSIONED``, ``QHP_VIRTUALFOLDER`` and ``LINK_QCHS`` to the respective
|
||||
values, to allow other code access to them, e.g. the macro
|
||||
``ecm_install_qch_export``.
|
||||
To enable the use of the target <target_name> as item for ``LINK_QCHS``
|
||||
in further ``ecm_add_qch`` calls in the current build,
|
||||
additionally a target property ``DOXYGEN_TAGFILE_BUILD`` is set, with the path
|
||||
of the created doxygen tag file in the build dir.
|
||||
If existing, ``ecm_add_qch`` will use this property instead of
|
||||
``DOXYGEN_TAGFILE`` for access to the tags file.
|
||||
|
||||
``NAME`` specifies the name for the generated documentation.
|
||||
|
||||
``VERSION`` specifies the version of the library for which the documentation is
|
||||
created.
|
||||
|
||||
``BASE_NAME`` specifies the base name for the generated files.
|
||||
The default basename is ``<name>``.
|
||||
|
||||
``SOURCE_DIRS`` specifies the dirs (incl. subdirs) with the source files for
|
||||
which the API documentation should be generated. Dirs can be relative to
|
||||
the current source dir. Dependencies to the files in the dirs are not
|
||||
tracked currently, other than with the ``SOURCES`` argument. So do not use for
|
||||
sources generated during the build.
|
||||
Needs to be used when ``SOURCES`` or ``CONFIG_TEMPLATE`` are not used.
|
||||
|
||||
``SOURCES`` specifies the source files for which the API documentation should be
|
||||
generated.
|
||||
Needs to be used when ``SOURCE_DIRS`` or ``CONFIG_TEMPLATE`` are not used.
|
||||
|
||||
``MD_MAINPAGE`` specifies a file in Markdown format that should be used as main
|
||||
page. This page will overrule any ``\mainpage`` command in the included
|
||||
sources.
|
||||
|
||||
``INCLUDE_DIRS`` specifies the dirs which should be searched for included
|
||||
headers. Dirs can be relative to the current source dir. Since 5.63.
|
||||
|
||||
``IMAGE_DIRS`` specifies the dirs which contain images that are included in the
|
||||
documentation. Dirs can be relative to the current source dir.
|
||||
|
||||
``EXAMPLE_DIRS`` specifies the dirs which contain examples that are included in
|
||||
the documentation. Dirs can be relative to the current source dir.
|
||||
|
||||
``QCH_INSTALL_DESTINATION`` specifies where the generated QCH file will be
|
||||
installed.
|
||||
|
||||
``TAGFILE_INSTALL_DESTINATION`` specifies where the generated tag file will be
|
||||
installed.
|
||||
|
||||
``COMPONENT`` specifies the installation component name with which the install
|
||||
rules for the generated QCH file and tag file are associated.
|
||||
|
||||
``NAMESPACE`` can be used to set a custom namespace <namespace> of the generated
|
||||
QCH file. The namepspace is used as the unique id by QHelpEngine (cmp.
|
||||
https://doc.qt.io/qt-5/qthelpproject.html#namespace).
|
||||
The default namespace is ``<domain>.<name>``.
|
||||
Needs to be used when ``ORG_DOMAIN`` is not used.
|
||||
|
||||
``ORG_DOMAIN`` can be used to define the organization domain prefix for the
|
||||
default namespace of the generated QCH file.
|
||||
Needs to be used when ``NAMESPACE`` is not used.
|
||||
|
||||
``LINK_QCHS`` specifies a list of other QCH targets which should be used for
|
||||
creating references to API documentation of code in external libraries.
|
||||
For each target <qch> in the list these target properties are expected to be
|
||||
defined: ``DOXYGEN_TAGFILE``, ``QHP_NAMESPACE`` and ``QHP_VIRTUALFOLDER``.
|
||||
If any of these is not existing, <qch> will be ignored.
|
||||
Use the macro ``ecm_install_qch_export`` for exporting a target with these
|
||||
properties with the CMake config of a library.
|
||||
Any target <qch> can also be one created before in the same buildsystem by
|
||||
another call of ``ecm_add_qch``.
|
||||
|
||||
``PREDEFINED_MACROS`` specifies a list of C/C++ macros which should be handled as
|
||||
given by the API dox generation tool.
|
||||
Examples are macros only defined in generated files, so whose
|
||||
definition might be not available to the tool.
|
||||
|
||||
``BLANK_MACROS`` specifies a list of C/C++ macro names which should be ignored by
|
||||
the API dox generation tool and handled as if they resolve to empty strings.
|
||||
Examples are export macros only defined in generated files, so whose
|
||||
definition might be not available to the tool.
|
||||
|
||||
``CONFIG_TEMPLATE`` specifies a custom cmake template file for the config file
|
||||
that is created to control the execution of the API dox generation tool.
|
||||
The following CMake variables need to be used:
|
||||
- ``ECM_QCH_DOXYGEN_QHELPGENERATOR_EXECUTABLE``
|
||||
- ``ECM_QCH_DOXYGEN_FILEPATH, ECM_QCH_DOXYGEN_TAGFILE``
|
||||
The following CMake variables can be used:
|
||||
- ``ECM_QCH_DOXYGEN_PROJECTNAME``
|
||||
- ``ECM_QCH_DOXYGEN_PROJECTVERSION``
|
||||
- ``ECM_QCH_DOXYGEN_VIRTUALFOLDER``
|
||||
- ``ECM_QCH_DOXYGEN_FULLNAMESPACE``
|
||||
- ``ECM_QCH_DOXYGEN_TAGFILES``
|
||||
- ``ECM_QCH_DOXYGEN_WARN_LOGFILE``
|
||||
- ``ECM_QCH_DOXYGEN_QUIET``
|
||||
There is no guarantue that the other CMake variables currently used in the
|
||||
default config file template will also be present with the same semantics
|
||||
in future versions of this macro.
|
||||
|
||||
``VERBOSE`` tells the API dox generation tool to be more verbose about its
|
||||
activity.
|
||||
|
||||
The default config file for the API dox generation tool, so the one when not
|
||||
using ``CONFIG_TEMPLATE``, allows code to handle the case of being processed by
|
||||
the tool by defining the C/C++ preprocessor macro ``K_DOXYGEN`` when run
|
||||
(since v5.67.0). For backward-compatibility also the definition
|
||||
``DOXYGEN_SHOULD_SKIP_THIS`` is set, but its usage is deprecated.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_add_qch(
|
||||
MyLib_QCH
|
||||
NAME MyLib
|
||||
VERSION "0.42.0"
|
||||
ORG_DOMAIN org.myorg
|
||||
SOURCE_DIRS
|
||||
src
|
||||
LINK_QCHS
|
||||
Qt5Core_QCH
|
||||
Qt5Xml_QCH
|
||||
Qt5Gui_QCH
|
||||
Qt5Widgets_QCH
|
||||
BLANK_MACROS
|
||||
MyLib_EXPORT
|
||||
MyLib_DEPRECATED
|
||||
TAGFILE_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/tags
|
||||
QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/qch
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
Example usage (with two QCH files, second linking first):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_add_qch(
|
||||
MyLib_QCH
|
||||
NAME MyLib
|
||||
VERSION ${MyLib_VERSION}
|
||||
ORG_DOMAIN org.myorg
|
||||
SOURCES ${MyLib_PUBLIC_HEADERS}
|
||||
MD_MAINPAGE src/mylib/README.md
|
||||
LINK_QCHS Qt5Core_QCH
|
||||
TAGFILE_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/tags
|
||||
QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/qch
|
||||
COMPONENT Devel
|
||||
)
|
||||
ecm_add_qch(
|
||||
MyOtherLib_QCH
|
||||
NAME MyOtherLib
|
||||
VERSION ${MyOtherLib_VERSION}
|
||||
ORG_DOMAIN org.myorg
|
||||
SOURCES ${MyOtherLib_PUBLIC_HEADERS}
|
||||
MD_MAINPAGE src/myotherlib/README.md
|
||||
LINK_QCHS Qt5Core_QCH MyLib_QCH
|
||||
TAGFILE_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/tags
|
||||
QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/qch
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
::
|
||||
|
||||
ecm_install_qch_export(
|
||||
TARGETS [<name> [<name2> [...]]]
|
||||
FILE <file>
|
||||
DESTINATION <dest>
|
||||
[COMPONENT <component>]
|
||||
)
|
||||
|
||||
This macro creates and installs a CMake file <file> which exports the given
|
||||
QCH targets <name> etc., so they can be picked up by CMake-based builds of
|
||||
other software that also generate QCH files (using ``ecm_add_qch``) and
|
||||
which should include links to the QCH files created by the given targets.
|
||||
The installed CMake file <file> is expected to be included by the CMake
|
||||
config file created for the software the related QCH files are documenting.
|
||||
|
||||
``TARGETS`` specifies the QCH targets which should be exported. If a target does
|
||||
not exist or does not have all needed properties, a warning will be
|
||||
generated and the target skipped.
|
||||
This behaviour might change in future versions to result in a fail instead.
|
||||
|
||||
``FILE`` specifies the name of the created CMake file, typically with a .cmake
|
||||
extension.
|
||||
|
||||
``DESTINATION`` specifies the directory on disk to which the file will be
|
||||
installed. It usually is the same as the one where the CMake config files
|
||||
for this software are installed.
|
||||
|
||||
``COMPONENT`` specifies the installation component name with which the
|
||||
install rule is associated.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_install_qch_export(
|
||||
TARGETS MyLib_QCH
|
||||
FILE MyLibQCHTargets.cmake
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/MyLib"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
Since 5.36.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../modules/QtVersionOption.cmake)
|
||||
include(ECMQueryQt)
|
||||
|
||||
|
||||
# Helper method: adding the LINK_QCHS property to a Qt QCH targets, from module base names ("Core" etc.)
|
||||
# if target does not exist (e.g. because no tagsfile was found), this is a no-op
|
||||
macro(_ecm_setup_qt_qch_links _module)
|
||||
set(_target "Qt${QT_MAJOR_VERSION}${_module}_QCH")
|
||||
if(TARGET ${_target})
|
||||
set(_linkqchs)
|
||||
foreach(_linkqch ${ARGN})
|
||||
list(APPEND _linkqchs "Qt${QT_MAJOR_VERSION}${_linkqch}_QCH")
|
||||
endforeach()
|
||||
set_property(TARGET ${_target} PROPERTY LINK_QCHS ${_linkqchs})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Helper method: ensure Qt QCH targets are created
|
||||
function(_ecm_ensure_qt_qch_targets)
|
||||
# create QCH targets for Qt
|
||||
# Ideally one day Qt CMake Config files provide these
|
||||
if(NOT TARGET Qt${QT_MAJOR_VERSION}Core_QCH)
|
||||
# get Qt version, if any
|
||||
find_package(Qt${QT_MAJOR_VERSION}Core CONFIG QUIET)
|
||||
# lookup tag files
|
||||
ecm_query_qt(qt_docs_dir QT_INSTALL_DOCS TRY)
|
||||
find_path(_qtcoreTagsPath qtcore/qtcore.tags
|
||||
PATHS
|
||||
${qt_docs_dir}
|
||||
)
|
||||
|
||||
if(Qt${QT_MAJOR_VERSION}Core_FOUND AND _qtcoreTagsPath)
|
||||
string(REPLACE "." "" _version ${Qt${QT_MAJOR_VERSION}Core_VERSION})
|
||||
# TODO: properly find each tag file
|
||||
# TODO: complete list of Qt modules
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
set(_module_list
|
||||
3D Bluetooth Concurrent Core DBus Gui Location
|
||||
Network Nfc Pdf Positioning PrintSupport Qml Quick
|
||||
Sensors SerialBus SerialPort Sql StateMachine Svg
|
||||
Test TextToSpeech WebChannel WebEngine WebSockets Widgets Xml)
|
||||
else()
|
||||
set(_module_list
|
||||
3D Bluetooth Concurrent Core DBus Gui Location
|
||||
Network Positioning PrintSupport Qml Quick
|
||||
Sensors SerialPort Sql Svg
|
||||
WebChannel WebEngine WebSockets Widgets Xml XmlPatterns)
|
||||
endif()
|
||||
foreach(_module ${_module_list})
|
||||
string(TOLOWER ${_module} _lowermodule)
|
||||
|
||||
set(_tagfile "${_qtcoreTagsPath}/qt${_lowermodule}/qt${_lowermodule}.tags")
|
||||
if(EXISTS "${_tagfile}")
|
||||
add_custom_target(Qt${QT_MAJOR_VERSION}${_module}_QCH)
|
||||
set_target_properties(Qt${QT_MAJOR_VERSION}${_module}_QCH PROPERTIES
|
||||
DOXYGEN_TAGFILE "${_tagfile}"
|
||||
QHP_NAMESPACE "org.qt-project.qt${_lowermodule}"
|
||||
QHP_NAMESPACE_VERSIONED "org.qt-project.qt${_lowermodule}.${_version}"
|
||||
QHP_VIRTUALFOLDER "qt${_lowermodule}"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
_ecm_setup_qt_qch_links(3D Gui Core)
|
||||
_ecm_setup_qt_qch_links(Bluetooth DBus Core)
|
||||
_ecm_setup_qt_qch_links(Concurrent Gui Core)
|
||||
_ecm_setup_qt_qch_links(DBus Core)
|
||||
_ecm_setup_qt_qch_links(Gui Core)
|
||||
_ecm_setup_qt_qch_links(Location Positioning Gui Core)
|
||||
_ecm_setup_qt_qch_links(Network Core)
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
_ecm_setup_qt_qch_links(Nfc Core)
|
||||
_ecm_setup_qt_qch_links(Pdf Gui Core)
|
||||
endif()
|
||||
_ecm_setup_qt_qch_links(Positioning Core)
|
||||
_ecm_setup_qt_qch_links(PrintSupport Widgets Gui Core)
|
||||
_ecm_setup_qt_qch_links(Qml Network Core)
|
||||
_ecm_setup_qt_qch_links(Quick Qml Network Gui Core)
|
||||
_ecm_setup_qt_qch_links(Sensors Core)
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
_ecm_setup_qt_qch_links(SerialBus Core)
|
||||
endif()
|
||||
_ecm_setup_qt_qch_links(SerialPort Core)
|
||||
_ecm_setup_qt_qch_links(Sql Core)
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
_ecm_setup_qt_qch_links(StateMachine Core)
|
||||
endif()
|
||||
_ecm_setup_qt_qch_links(Svg Widgets Gui Core)
|
||||
_ecm_setup_qt_qch_links(Test Core)
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
_ecm_setup_qt_qch_links(TextToSpeech Core)
|
||||
endif()
|
||||
_ecm_setup_qt_qch_links(WebChannel Qml Core)
|
||||
_ecm_setup_qt_qch_links(WebEngine Quick Qml Gui Core)
|
||||
_ecm_setup_qt_qch_links(WebSockets Network Core)
|
||||
_ecm_setup_qt_qch_links(Widgets Gui Core)
|
||||
_ecm_setup_qt_qch_links(Xml Core)
|
||||
if (QT_MAJOR_VERSION EQUAL "5")
|
||||
_ecm_setup_qt_qch_links(XmlPatterns Network Core)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper method: collect all qch targets from the LINK_QCHS dependency tree and set result to <var>
|
||||
function(_ecm_collect_linkable_qch_targets name)
|
||||
set(_candidate_qchs ${ARGN})
|
||||
set(_handled_qchs)
|
||||
set(_good_qchs)
|
||||
# while unhandled qch targets
|
||||
while(_candidate_qchs)
|
||||
# get another unhandled qch target
|
||||
list(GET _candidate_qchs 0 _qch)
|
||||
list(REMOVE_AT _candidate_qchs 0)
|
||||
list(FIND _handled_qchs ${_qch} _index)
|
||||
# if not already handled
|
||||
if(_index EQUAL -1)
|
||||
list(APPEND _handled_qchs ${_qch})
|
||||
if(TARGET ${_qch})
|
||||
# always look at other linked qch targets, also for incomplete targets
|
||||
get_property(_link_qchs TARGET ${_qch} PROPERTY LINK_QCHS)
|
||||
if(_link_qchs)
|
||||
list(APPEND _candidate_qchs ${_link_qchs})
|
||||
endif()
|
||||
# check if this target has all needed properties
|
||||
set(_target_usable TRUE)
|
||||
foreach(_propertyname
|
||||
DOXYGEN_TAGFILE
|
||||
QHP_NAMESPACE
|
||||
QHP_VIRTUALFOLDER
|
||||
)
|
||||
get_target_property(_property ${_qch} ${_propertyname})
|
||||
if(NOT _property)
|
||||
message(STATUS "No property ${_propertyname} set on ${_qch} when calling ecm_add_qch(). <<${_property}>>")
|
||||
set(_target_usable FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
get_target_property(_tagfile_build ${_qch} DOXYGEN_TAGFILE_BUILD)
|
||||
if (NOT _tagfile_build)
|
||||
get_target_property(_tagfile ${_qch} DOXYGEN_TAGFILE)
|
||||
if(NOT EXISTS ${_tagfile})
|
||||
message(STATUS "No such tag file \"${_tagfile}\" found for ${_qch} when calling ecm_add_qch().")
|
||||
set(_target_usable FALSE)
|
||||
endif()
|
||||
endif()
|
||||
if(_target_usable)
|
||||
list(APPEND _good_qchs ${_qch})
|
||||
else()
|
||||
message(WARNING "No linking to API dox of ${_qch}.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "No such target ${_qch} defined when calling ecm_add_qch(), ignored.")
|
||||
endif()
|
||||
endif()
|
||||
endwhile()
|
||||
set(${name} ${_good_qchs} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_add_qch target_name)
|
||||
# Parse arguments
|
||||
set(options VERBOSE)
|
||||
set(oneValueArgs NAME BASE_NAME QCH_INSTALL_DESTINATION TAGFILE_INSTALL_DESTINATION COMPONENT VERSION NAMESPACE MD_MAINPAGE ORG_DOMAIN CONFIG_TEMPLATE)
|
||||
set(multiValueArgs SOURCE_DIRS SOURCES INCLUDE_DIRS IMAGE_DIRS EXAMPLE_DIRS PREDEFINED_MACROS BLANK_MACROS LINK_QCHS)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# check required args
|
||||
foreach(_arg_name NAME QCH_INSTALL_DESTINATION TAGFILE_INSTALL_DESTINATION VERSION)
|
||||
if(NOT DEFINED ARGS_${_arg_name})
|
||||
message(FATAL_ERROR "${_arg_name} needs to be defined when calling ecm_add_qch")
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT DEFINED ARGS_SOURCE_DIRS AND NOT DEFINED ARGS_SOURCES AND NOT DEFINED ARGS_CONFIG_TEMPLATE)
|
||||
message(FATAL_ERROR "SOURCE_DIRS or SOURCES needs to be defined when calling ecm_add_qch")
|
||||
endif()
|
||||
if(DEFINED ARGS_SOURCE_DIRS AND DEFINED ARGS_SOURCES)
|
||||
message(FATAL_ERROR "Either SOURCE_DIRS or SOURCES, not both, needs to be defined when calling ecm_add_qch")
|
||||
endif()
|
||||
if(NOT DEFINED ARGS_ORG_DOMAIN AND NOT DEFINED ARGS_NAMESPACE)
|
||||
message(FATAL_ERROR "ORG_DOMAIN or NAMESPACE needs to be defined when calling ecm_add_qch")
|
||||
endif()
|
||||
|
||||
# find required tools
|
||||
if (NOT DOXYGEN_PATCHED_JSFILESADDED)
|
||||
set(REQUIRED_DOXYGEN_VERSION 1.8.13)
|
||||
endif()
|
||||
find_package(Doxygen ${REQUIRED_DOXYGEN_VERSION} REQUIRED)
|
||||
if (NOT DOXYGEN_FOUND AND NOT DOXYGEN_PATCHED_JSFILESADDED)
|
||||
set(doxygen_description_addition " (Or older version patched with https://github.com/doxygen/doxygen/commit/bf9415698e53d79b, pass -DDOXYGEN_PATCHED_JSFILESADDED=ON to cmake if patched)")
|
||||
endif()
|
||||
set_package_properties(Doxygen PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Needed for API dox QCH file generation${doxygen_description_addition}"
|
||||
)
|
||||
|
||||
if (QT_MAJOR_VERSION EQUAL "5")
|
||||
find_package(QHelpGenerator REQUIRED)
|
||||
set_package_properties(QHelpGenerator PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Needed for API dox QCH file generation"
|
||||
DESCRIPTION "Part of Qt5 tools"
|
||||
)
|
||||
else()
|
||||
find_package(Qt6 COMPONENTS ToolsTools CONFIG REQUIRED)
|
||||
set_package_properties(Qt6ToolsTools PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Needed for API dox QCH file generation"
|
||||
DESCRIPTION "qhelpgenerator from Qt6 tools"
|
||||
)
|
||||
if(TARGET Qt6::qhelpgenerator)
|
||||
get_target_property(QHelpGenerator_EXECUTABLE Qt6::qhelpgenerator LOCATION)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(_missing_tools)
|
||||
if (NOT DOXYGEN_FOUND)
|
||||
list(APPEND _missing_tools "Doxygen")
|
||||
endif()
|
||||
if (NOT QHelpGenerator_FOUND AND NOT TARGET Qt6::qhelpgenerator)
|
||||
list(APPEND _missing_tools "qhelpgenerator")
|
||||
endif()
|
||||
|
||||
if (_missing_tools)
|
||||
message(WARNING "API dox QCH file will not be generated, tools missing: ${_missing_tools}!")
|
||||
else()
|
||||
_ecm_ensure_qt_qch_targets()
|
||||
|
||||
# prepare base dirs, working file names and other vars
|
||||
if (DEFINED ARGS_BASE_NAME)
|
||||
set(_basename ${ARGS_BASE_NAME})
|
||||
else()
|
||||
set(_basename ${ARGS_NAME})
|
||||
endif()
|
||||
set(_qch_file_basename "${_basename}.qch")
|
||||
set(_tags_file_basename "${_basename}.tags")
|
||||
set(_qch_buildpath "${CMAKE_CURRENT_BINARY_DIR}/${_qch_file_basename}")
|
||||
set(_tags_buildpath "${CMAKE_CURRENT_BINARY_DIR}/${_tags_file_basename}")
|
||||
set(_apidox_builddir "${CMAKE_CURRENT_BINARY_DIR}/${_basename}_ECMQchDoxygen")
|
||||
if (DEFINED ARGS_NAMESPACE)
|
||||
set(_namespace "${ARGS_NAMESPACE}")
|
||||
else()
|
||||
set(_namespace "${ARGS_ORG_DOMAIN}.${ARGS_NAME}")
|
||||
endif()
|
||||
string(REPLACE "." "_" _dotLessVersion ${ARGS_VERSION})
|
||||
set(_versioned_namespace "${_namespace}.${_dotLessVersion}")
|
||||
set(_sources)
|
||||
set(_dep_tagfiles)
|
||||
set(_dep_qch_targets)
|
||||
|
||||
### Create doxygen config file
|
||||
set(_doxygenconfig_file "${CMAKE_CURRENT_BINARY_DIR}/${_basename}_ECMQchDoxygen.config")
|
||||
if (DEFINED ARGS_CONFIG_TEMPLATE)
|
||||
set(_doxygenconfig_template_file "${ARGS_CONFIG_TEMPLATE}")
|
||||
else()
|
||||
set(_doxygenconfig_template_file "${ECM_MODULE_DIR}/ECMQchDoxygen.config.in")
|
||||
endif()
|
||||
set(_doxygen_layout_file "${ECM_MODULE_DIR}/ECMQchDoxygenLayout.xml")
|
||||
# Setup variables used in config file template, ECM_QCH_DOXYGEN_*
|
||||
set(ECM_QCH_DOXYGEN_OUTPUTDIR "\"${_apidox_builddir}\"")
|
||||
set(ECM_QCH_DOXYGEN_TAGFILE "\"${_tags_buildpath}\"")
|
||||
set(ECM_QCH_DOXYGEN_LAYOUTFILE "\"${_doxygen_layout_file}\"")
|
||||
set(ECM_QCH_DOXYGEN_INCLUDE_PATH)
|
||||
foreach(_include_DIR IN LISTS ARGS_INCLUDE_DIRS)
|
||||
if (NOT IS_ABSOLUTE ${_include_DIR})
|
||||
set(_include_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${_include_DIR}")
|
||||
endif()
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
set(ECM_QCH_DOXYGEN_INCLUDE_PATH "${ECM_QCH_DOXYGEN_INCLUDE_PATH} \\\n\"${_include_DIR}\"")
|
||||
endforeach()
|
||||
set(ECM_QCH_DOXYGEN_IMAGEDIRS)
|
||||
foreach(_image_DIR IN LISTS ARGS_IMAGE_DIRS)
|
||||
if (NOT IS_ABSOLUTE ${_image_DIR})
|
||||
set(_image_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${_image_DIR}")
|
||||
endif()
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
set(ECM_QCH_DOXYGEN_IMAGEDIRS "${ECM_QCH_DOXYGEN_IMAGEDIRS} \\\n\"${_image_DIR}\"")
|
||||
endforeach()
|
||||
set(ECM_QCH_DOXYGEN_EXAMPLEDIRS)
|
||||
foreach(_example_DIR IN LISTS ARGS_EXAMPLE_DIRS)
|
||||
if (NOT IS_ABSOLUTE ${_example_DIR})
|
||||
set(_example_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${_example_DIR}")
|
||||
endif()
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
set(ECM_QCH_DOXYGEN_EXAMPLEDIRS "${ECM_QCH_DOXYGEN_EXAMPLEDIRS} \\\n\"${_example_DIR}\"")
|
||||
endforeach()
|
||||
if (ARGS_MD_MAINPAGE)
|
||||
if (NOT IS_ABSOLUTE ${ARGS_MD_MAINPAGE})
|
||||
set(ARGS_MD_MAINPAGE "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_MD_MAINPAGE}")
|
||||
endif()
|
||||
set(ECM_QCH_DOXYGEN_MAINPAGE_MDFILE "\"${ARGS_MD_MAINPAGE}\"")
|
||||
else()
|
||||
set(ECM_QCH_DOXYGEN_MAINPAGE_MDFILE)
|
||||
endif()
|
||||
set(ECM_QCH_DOXYGEN_INPUT)
|
||||
if (ARGS_SOURCE_DIRS)
|
||||
foreach(_source_DIR IN LISTS ARGS_SOURCE_DIRS)
|
||||
if (NOT IS_ABSOLUTE ${_source_DIR})
|
||||
set(_source_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${_source_DIR}")
|
||||
endif()
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
set(ECM_QCH_DOXYGEN_INPUT "${ECM_QCH_DOXYGEN_INPUT} \\\n\"${_source_DIR}\"")
|
||||
endforeach()
|
||||
if (ARGS_MD_MAINPAGE)
|
||||
set(ECM_QCH_DOXYGEN_INPUT "${ECM_QCH_DOXYGEN_INPUT} \\\n\"${ARGS_MD_MAINPAGE}\"")
|
||||
endif()
|
||||
set(ECM_QCH_DOXYGEN_FILE_PATTERNS "*.h *.cpp *.hpp *.hh *.cc *.h++ *.c++ *.hxx *.cxx *.dox *.md")
|
||||
else()
|
||||
foreach(_source IN LISTS ARGS_SOURCES)
|
||||
if (NOT IS_ABSOLUTE ${_source})
|
||||
set(_source "${CMAKE_CURRENT_SOURCE_DIR}/${_source}")
|
||||
endif()
|
||||
list(APPEND _sources "${_source}")
|
||||
endforeach()
|
||||
if (ARGS_MD_MAINPAGE)
|
||||
list(FIND _sources ${ARGS_MD_MAINPAGE} _mainpage_index)
|
||||
if (_mainpage_index STREQUAL -1)
|
||||
list(APPEND _sources "${ARGS_MD_MAINPAGE}")
|
||||
endif()
|
||||
endif()
|
||||
foreach(_source IN LISTS _sources)
|
||||
# concat sources separated by a break, it is no issue that first has also a leading break
|
||||
set(ECM_QCH_DOXYGEN_INPUT "${ECM_QCH_DOXYGEN_INPUT} \\\n\"${_source}\"")
|
||||
endforeach()
|
||||
set(ECM_QCH_DOXYGEN_FILE_PATTERNS "")
|
||||
endif()
|
||||
|
||||
set(ECM_QCH_DOXYGEN_PROJECTNAME ${ARGS_NAME})
|
||||
file(RELATIVE_PATH _builddirrelative_filepath "${_apidox_builddir}/html" ${_qch_buildpath})
|
||||
set(ECM_QCH_DOXYGEN_FILEPATH "\"${_builddirrelative_filepath}\"")
|
||||
set(ECM_QCH_DOXYGEN_PROJECTVERSION ${ARGS_VERSION})
|
||||
string(TOLOWER ${ARGS_NAME} ECM_QCH_DOXYGEN_VIRTUALFOLDER)
|
||||
set(ECM_QCH_DOXYGEN_FULLNAMESPACE ${_versioned_namespace})
|
||||
set(ECM_QCH_DOXYGEN_PREDEFINED_MACROS)
|
||||
foreach(_macro IN LISTS ARGS_PREDEFINED_MACROS)
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
# wrap each macro in quotes, to handle potential blanks and commas
|
||||
string(REPLACE "\"" "\\\"" _macro "${_macro}")
|
||||
set(ECM_QCH_DOXYGEN_PREDEFINED_MACROS "${ECM_QCH_DOXYGEN_PREDEFINED_MACROS} \\\n\"${_macro}\"")
|
||||
endforeach()
|
||||
set(ECM_QCH_DOXYGEN_BLANK_MACROS)
|
||||
foreach(_macro IN LISTS ARGS_BLANK_MACROS)
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
# wrap each macro in quotes, to handle potential blanks and commas
|
||||
string(REPLACE "\"" "\\\"" _macro "${_macro}")
|
||||
set(ECM_QCH_DOXYGEN_BLANK_MACROS "${ECM_QCH_DOXYGEN_BLANK_MACROS} \\\n\"${_macro}=\"")
|
||||
endforeach()
|
||||
|
||||
# create list of tag files for linking other QCH files
|
||||
set(ECM_QCH_DOXYGEN_TAGFILES)
|
||||
_ecm_collect_linkable_qch_targets(_link_qchs ${ARGS_LINK_QCHS})
|
||||
foreach(_link_qch IN LISTS _link_qchs)
|
||||
list(APPEND _dep_qch_targets ${_link_qch})
|
||||
get_target_property(_link_qch_tagfile ${_link_qch} DOXYGEN_TAGFILE)
|
||||
get_target_property(_link_qch_tagfile_build ${_link_qch} DOXYGEN_TAGFILE_BUILD)
|
||||
get_target_property(_link_qch_namespace ${_link_qch} QHP_NAMESPACE)
|
||||
get_target_property(_link_qch_virtualfolder ${_link_qch} QHP_VIRTUALFOLDER)
|
||||
# if same build, then prefer build version over any installed one
|
||||
if (_link_qch_tagfile_build)
|
||||
set(_link_qch_tagfile ${_link_qch_tagfile_build})
|
||||
list(APPEND _dep_tagfiles "${_link_qch_tagfile}")
|
||||
endif()
|
||||
get_property(_linkqchs TARGET ${_link_qch} PROPERTY LINK_QCHS)
|
||||
set(_tagfile_entry "\"${_link_qch_tagfile}=qthelp://${_link_qch_namespace}/${_link_qch_virtualfolder}/\"")
|
||||
# concat dirs separated by a break, it is no issue that first has also a leading break
|
||||
set(ECM_QCH_DOXYGEN_TAGFILES "${ECM_QCH_DOXYGEN_TAGFILES} \\\n${_tagfile_entry}")
|
||||
endforeach()
|
||||
|
||||
set(ECM_QCH_DOXYGEN_WARN_LOGFILE "\"${_doxygenconfig_file}.log\"")
|
||||
if(ARGS_VERBOSE)
|
||||
set(ECM_QCH_DOXYGEN_QUIET "NO")
|
||||
else()
|
||||
set(ECM_QCH_DOXYGEN_QUIET "YES")
|
||||
endif()
|
||||
set(ECM_QCH_DOXYGEN_QHELPGENERATOR_EXECUTABLE ${QHelpGenerator_EXECUTABLE})
|
||||
|
||||
# finally create doxygen config file
|
||||
configure_file(
|
||||
"${_doxygenconfig_template_file}"
|
||||
"${_doxygenconfig_file}"
|
||||
@ONLY
|
||||
)
|
||||
# Doxygen warns verbosely about outdated config entries.
|
||||
# To spare custom code here to generate configuration with no out-dated entries,
|
||||
# instead make use of the doxygen feature to update configuration files.
|
||||
execute_process(
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} -u "${_doxygenconfig_file}"
|
||||
ERROR_VARIABLE _doxygen_update_error
|
||||
RESULT_VARIABLE _doxygen_update_result
|
||||
)
|
||||
if(NOT ${_doxygen_update_result} STREQUAL "0")
|
||||
message(WARNING "Updating the doxygen config file failed: ${_doxygen_update_error}")
|
||||
endif()
|
||||
|
||||
# setup make target
|
||||
set(_qch_INSTALLPATH ${ARGS_QCH_INSTALL_DESTINATION})
|
||||
set(_tags_INSTALLPATH ${ARGS_TAGFILE_INSTALL_DESTINATION})
|
||||
file(RELATIVE_PATH _relative_qch_file ${CMAKE_BINARY_DIR} ${_qch_buildpath})
|
||||
file(RELATIVE_PATH _relative_tags_file ${CMAKE_BINARY_DIR} ${_tags_buildpath})
|
||||
add_custom_command(
|
||||
OUTPUT ${_qch_buildpath} ${_tags_buildpath}
|
||||
COMMENT "Generating ${_relative_qch_file}, ${_relative_tags_file}"
|
||||
COMMAND cmake -E remove_directory "${ECM_QCH_DOXYGEN_OUTPUTDIR}"
|
||||
COMMAND cmake -E make_directory "${ECM_QCH_DOXYGEN_OUTPUTDIR}"
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} "${_doxygenconfig_file}"
|
||||
DEPENDS
|
||||
${_doxygenconfig_file}
|
||||
${_doxygen_layout_file}
|
||||
${_sources}
|
||||
${_dep_tagfiles}
|
||||
${_dep_qch_targets}
|
||||
)
|
||||
add_custom_target(${target_name} ALL DEPENDS ${_qch_buildpath} ${_tags_buildpath})
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
DOXYGEN_TAGFILE "${_qch_INSTALLPATH}/${_tags_file_basename}"
|
||||
DOXYGEN_TAGFILE_NAME "${_tags_file_basename}"
|
||||
DOXYGEN_TAGFILE_INSTALLDIR "${_qch_INSTALLPATH}"
|
||||
DOXYGEN_TAGFILE_BUILD "${_tags_buildpath}"
|
||||
QHP_NAMESPACE "${_namespace}"
|
||||
QHP_NAMESPACE_VERSIONED "${_versioned_namespace}"
|
||||
QHP_VIRTUALFOLDER "${ECM_QCH_DOXYGEN_VIRTUALFOLDER}"
|
||||
)
|
||||
# list as value does not work with set_target_properties
|
||||
set_property(TARGET ${target_name} PROPERTY LINK_QCHS "${ARGS_LINK_QCHS}")
|
||||
|
||||
if (DEFINED ARGS_COMPONENT)
|
||||
set(_component COMPONENT ${ARGS_COMPONENT})
|
||||
else()
|
||||
set(_component)
|
||||
endif()
|
||||
|
||||
# setup installation
|
||||
install(FILES
|
||||
${_qch_buildpath}
|
||||
DESTINATION ${_qch_INSTALLPATH}
|
||||
${_component}
|
||||
)
|
||||
|
||||
install(FILES
|
||||
${_tags_buildpath}
|
||||
DESTINATION ${_tags_INSTALLPATH}
|
||||
${_component}
|
||||
)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_install_qch_export)
|
||||
set(options )
|
||||
set(oneValueArgs FILE DESTINATION COMPONENT)
|
||||
set(multiValueArgs TARGETS)
|
||||
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT DEFINED ARGS_FILE)
|
||||
message(FATAL_ERROR "FILE needs to be defined when calling ecm_install_qch_export().")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ARGS_DESTINATION)
|
||||
message(FATAL_ERROR "DESTINATION needs to be defined when calling ecm_install_qch_export().")
|
||||
endif()
|
||||
|
||||
# TARGETS may be empty (and ARGS_TARGETS will not be defined then by cmake_parse_arguments)
|
||||
|
||||
set(_content
|
||||
"# This file was generated by ecm_install_qch_export(). DO NOT EDIT!
|
||||
"
|
||||
)
|
||||
|
||||
foreach(_target IN LISTS ARGS_TARGETS)
|
||||
set(_target_usable TRUE)
|
||||
|
||||
if (NOT TARGET ${_target})
|
||||
message(STATUS "No such target ${_target} when calling ecm_install_qch_export().")
|
||||
set(_target_usable FALSE)
|
||||
else()
|
||||
foreach(_propertyname
|
||||
DOXYGEN_TAGFILE_NAME
|
||||
DOXYGEN_TAGFILE_INSTALLDIR
|
||||
QHP_NAMESPACE
|
||||
QHP_NAMESPACE_VERSIONED
|
||||
QHP_VIRTUALFOLDER
|
||||
)
|
||||
get_target_property(_property ${_target} ${_propertyname})
|
||||
if(NOT _property)
|
||||
message(STATUS "No property ${_propertyname} set on ${_target} when calling ecm_install_qch_export(). <${_property}>")
|
||||
set(_target_usable FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if(_target_usable)
|
||||
get_target_property(_tagfile_name ${_target} DOXYGEN_TAGFILE_NAME)
|
||||
get_target_property(_tagfile_installdir ${_target} DOXYGEN_TAGFILE_INSTALLDIR)
|
||||
if (NOT IS_ABSOLUTE ${_tagfile_installdir})
|
||||
set(_tagfile_installdir "${CMAKE_INSTALL_PREFIX}/${_tagfile_installdir}")
|
||||
endif()
|
||||
get_target_property(_namespace ${_target} QHP_NAMESPACE)
|
||||
get_target_property(_namespace_versioned ${_target} QHP_NAMESPACE_VERSIONED)
|
||||
get_target_property(_virtualfolder ${_target} QHP_VIRTUALFOLDER)
|
||||
get_property(_linkqchs TARGET ${_target} PROPERTY LINK_QCHS)
|
||||
set(_content "${_content}
|
||||
if (NOT TARGET ${_target})
|
||||
|
||||
add_custom_target(${_target})
|
||||
set_target_properties(${_target} PROPERTIES
|
||||
DOXYGEN_TAGFILE \"${_tagfile_installdir}/${_tagfile_name}\"
|
||||
QHP_NAMESPACE \"${_namespace}\"
|
||||
QHP_NAMESPACE_VERSIONED \"${_namespace_versioned}\"
|
||||
QHP_VIRTUALFOLDER \"${_virtualfolder}\"
|
||||
)
|
||||
set_property(TARGET ${_target} PROPERTY LINK_QCHS ${_linkqchs})
|
||||
|
||||
endif()
|
||||
"
|
||||
)
|
||||
else()
|
||||
message(STATUS "No target exported for ${_target}.")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (NOT IS_ABSOLUTE ${ARGS_FILE})
|
||||
set(ARGS_FILE "${CMAKE_CURRENT_BINARY_DIR}/${ARGS_FILE}")
|
||||
endif()
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT "${ARGS_FILE}"
|
||||
CONTENT "${_content}"
|
||||
)
|
||||
|
||||
if (DEFINED ARGS_COMPONENT)
|
||||
set(_component COMPONENT ${ARGS_COMPONENT})
|
||||
else()
|
||||
set(_component)
|
||||
endif()
|
||||
install(
|
||||
FILES "${ARGS_FILE}"
|
||||
DESTINATION "${ARGS_DESTINATION}"
|
||||
${_component}
|
||||
)
|
||||
|
||||
endfunction()
|
||||
+622
@@ -0,0 +1,622 @@
|
||||
# SPDX-FileCopyrightText: 2019 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMAddQtDesignerPlugin
|
||||
----------------------
|
||||
|
||||
This module provides the ``ecm_add_qtdesignerplugin`` function for generating
|
||||
Qt Designer plugins for custom widgets. Each of those widgets is described
|
||||
using a second function ``ecm_qtdesignerplugin_widget``.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_qtdesignerplugin(<target_name>
|
||||
NAME <name>
|
||||
WIDGETS <widgetid> [<widgetid2> [...]]
|
||||
LINK_LIBRARIES <lib> [<lib2> [...]]
|
||||
INSTALL_DESTINATION <install_path>
|
||||
[OUTPUT_NAME <output_name>]
|
||||
[DEFAULT_GROUP <group>]
|
||||
[DEFAULT_HEADER_CASE <SAME_CASE|LOWER_CASE|UPPER_CASE>]
|
||||
[DEFAULT_HEADER_EXTENSION <header_extension>]
|
||||
[DEFAULT_ICON_DIR <icon_dir>]
|
||||
[INCLUDE_FILES <include_file> [<include_file2> [...]]]
|
||||
[SOURCES <src> [<src2> [...]]]
|
||||
[COMPONENT <component>]
|
||||
)
|
||||
|
||||
``NAME`` specifies the base name to use in the generated sources.
|
||||
The default is <target_name>.
|
||||
|
||||
``WIDGETS`` specifies the widgets the plugin should support. Each widget has
|
||||
to be defined before by a call of ``ecm_qtdesignerplugin_widget`` with the
|
||||
respective <widgetid>, in a scope including the current call.
|
||||
|
||||
``LINK_LIBRARIES`` specifies the libraries to link against. This will be at
|
||||
least the library providing the widget class(es).
|
||||
|
||||
``INSTALL_DESTINATION`` specifies where the generated plugin binary will be
|
||||
installed.
|
||||
|
||||
``OUTPUT_NAME`` specifies the name of the plugin binary. The default is
|
||||
"<target_name>".
|
||||
|
||||
``DEFAULT_GROUP`` specifies the default group in Qt Designer where the
|
||||
widgets will be placed. The default is "Custom".
|
||||
|
||||
``DEFAULT_HEADER_CASE`` specifies how the name of the header is derived from
|
||||
the widget class name. The default is "LOWER_CASE".
|
||||
|
||||
``DEFAULT_HEADER_EXTENSION`` specifies what file name extension is used for
|
||||
the header file derived from the class name. The default is "h".
|
||||
|
||||
``DEFAULT_ICON_DIR`` specifies what file name extension is used for
|
||||
the header file derived from the class name. The default is "pics".
|
||||
|
||||
``INCLUDE_FILES`` specifies additional include files to include with the
|
||||
generated source file. This can be needed for custom code used in
|
||||
initializing or creating widgets.
|
||||
|
||||
``SOURCES`` specifies additional source files to build the plugin from.
|
||||
This can be needed to support custom code used in initializing or
|
||||
creating widgets.
|
||||
|
||||
``COMPONENT`` specifies the installation component name with which the install
|
||||
rules for the generated plugin are associated.
|
||||
|
||||
::
|
||||
|
||||
ecm_qtdesignerplugin_widget(<widgetid>
|
||||
[CLASS_NAME <class_name>]
|
||||
[INCLUDE_FILE <include_file>]
|
||||
[CONTAINER]
|
||||
[ICON <iconfile>]
|
||||
[TOOLTIP <tooltip>]
|
||||
[WHATSTHIS <whatsthis>]
|
||||
[GROUP <group>]
|
||||
[CREATE_WIDGET_CODE_FROM_VARIABLE <create_widget_code_variable>]
|
||||
[INITIALIZE_CODE_FROM_VARIABLE <initialize_code_variable]
|
||||
[DOM_XML_FROM_VARIABLE <dom_xml_variable>]
|
||||
[IMPL_CLASS_NAME <impl_class_name>]
|
||||
[CONSTRUCTOR_ARGS_CODE <constructor_args_code>]
|
||||
[CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE <constructor_args_code_variable>]
|
||||
)
|
||||
|
||||
``CLASS_NAME`` specifies the name of the widget class, including namespaces.
|
||||
The default is "<widgetid>".
|
||||
|
||||
``INCLUDE_FILE`` specifies the include file to use for the class of this
|
||||
widget. The default is derived from <class_name> as configured by the
|
||||
``DEFAULT_HEADER_*`` options of ``ecm_add_qtdesignerplugin``, also replacing
|
||||
any namespace separators with "/".
|
||||
|
||||
``CONTAINER`` specifies, if set, that this widget is a container
|
||||
for other widgets.
|
||||
|
||||
``ICON`` specifies the icon file to use as symbol for this widget.
|
||||
The default is "{lowercased <class_name>}.png" in the default icons dir as
|
||||
configured by the ``DEFAULT_ICON_DIR`` option of
|
||||
``ecm_add_qtdesignerplugin``, if such a file exists.
|
||||
|
||||
``TOOLTIP`` specifies the tooltip text to use for this widget. Default is
|
||||
"<class_name> Widget".
|
||||
|
||||
``WHATSTHIS`` specifies the What's-This text to use for this widget.
|
||||
Defaults to the tooltip.
|
||||
|
||||
``GROUP`` specifies the group in Qt Designer where the widget will be placed.
|
||||
The default is set as configured by the ``DEFAULT_GROUP`` option of
|
||||
``ecm_add_qtdesignerplugin``.
|
||||
|
||||
``CREATE_WIDGET_CODE_FROM_VARIABLE`` specifies the variable to get from the
|
||||
C++ code to use as factory code to create an instance of the widget,
|
||||
for the override of
|
||||
``QDesignerCustomWidgetInterface::createWidget(QWidget* parent)``.
|
||||
The default is "return new <impl_class_name><constructor_args_code>;".
|
||||
|
||||
``INITIALIZE_CODE_FROM_VARIABLE`` specifies the variable to get from the C++
|
||||
code to use with the override of
|
||||
``QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface* core)``.
|
||||
The code has to use the present class member ``m_initialized`` to track and
|
||||
update the state. The default code simply sets ``m_initialized`` to
|
||||
``true``, if it was not before.
|
||||
|
||||
``DOM_XML_FROM_VARIABLE`` specifies the variable to get from the string to
|
||||
use with the optional override of
|
||||
``QDesignerCustomWidgetInterface::domXml()``.
|
||||
Default does not override.
|
||||
|
||||
``IMPL_CLASS_NAME`` specifies the name of the widget class to use for the
|
||||
widget instance with Qt Designer. The default is "<class_name>".
|
||||
|
||||
``CONSTRUCTOR_ARGS_CODE`` specifies the C++ code to use for the constructor
|
||||
arguments with the default of ``CREATE_WIDGET_CODE_FROM_VARIABLE``. Note
|
||||
that the parentheses are required. The default is "(parent)".
|
||||
|
||||
``CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE`` specifies the variable to get from
|
||||
the C++ code instead of passing it directly via ``CONSTRUCTOR_ARGS_CODE``.
|
||||
This can be needed if the code is more complex and e.g. includes ";" chars.
|
||||
|
||||
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_qtdesignerplugin_widget(FooWidget
|
||||
TOOLTIP "Enables to browse foo."
|
||||
GROUP "Views (Foo)"
|
||||
)
|
||||
|
||||
set(BarWidget_CREATE_WIDGET_CODE
|
||||
"
|
||||
auto* widget = new BarWidget(parent);
|
||||
widget->setBar("Example bar");
|
||||
return widget;
|
||||
")
|
||||
|
||||
ecm_qtdesignerplugin_widget(BarWidget
|
||||
TOOLTIP "Displays bars."
|
||||
GROUP "Display (Foo)"
|
||||
CREATE_WIDGET_CODE_FROM_VARIABLE BarWidget_CREATE_WIDGET_CODE
|
||||
)
|
||||
|
||||
ecm_add_qtdesignerplugin(foowidgets
|
||||
NAME FooWidgets
|
||||
OUTPUT_NAME foo2widgets
|
||||
WIDGETS
|
||||
FooWidget
|
||||
BarWidget
|
||||
LINK_LIBRARIES
|
||||
Foo::Widgets
|
||||
INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
Since 5.62.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(FeatureSummary)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||
|
||||
# helper method
|
||||
# escapes string for C++ code
|
||||
function(_ecm_qtdesignerplugin_escape_cpp_string _varName input)
|
||||
string(REPLACE "\"" "\\\"" _string ${input})
|
||||
set(${_varName} "${_string}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# To make the data about the widgets available to the function ecm_add_qtdesignerplugin,
|
||||
# variables are created in the scope of the caller of this method, protected by
|
||||
# a namespace for this macro file, and otherwise from the widget id and the property id:
|
||||
# ECM_QTDESIGNERPLUGIN_${widget}_${property}
|
||||
function(ecm_qtdesignerplugin_widget widget)
|
||||
set(options
|
||||
CONTAINER
|
||||
)
|
||||
set(oneValueArgs
|
||||
CLASS_NAME
|
||||
INCLUDE_FILE
|
||||
ICON
|
||||
TOOLTIP
|
||||
WHATSTHIS
|
||||
GROUP
|
||||
CREATE_WIDGET_CODE_FROM_VARIABLE
|
||||
INITIALIZE_CODE_FROM_VARIABLE
|
||||
DOM_XML_FROM_VARIABLE
|
||||
IMPL_CLASS_NAME
|
||||
CONSTRUCTOR_ARGS_CODE
|
||||
CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE
|
||||
)
|
||||
set(multiValueArgs
|
||||
)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT ARGS_CLASS_NAME)
|
||||
set(ARGS_CLASS_NAME "${widget}")
|
||||
endif()
|
||||
if(NOT ARGS_TOOLTIP)
|
||||
set(ARGS_TOOLTIP "${ARGS_CLASS_NAME} Widget")
|
||||
endif()
|
||||
if(NOT ARGS_WHATSTHIS)
|
||||
set(ARGS_WHATSTHIS "${ARGS_TOOLTIP}")
|
||||
endif()
|
||||
if(ARGS_CONTAINER)
|
||||
set(_is_container TRUE)
|
||||
else()
|
||||
set(_is_container FALSE)
|
||||
endif()
|
||||
if(ARGS_CONSTRUCTOR_ARGS_CODE AND ARGS_CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE)
|
||||
message(FATAL_ERROR "Either CONSTRUCTOR_ARGS_CODE or CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE can be passed when calling ecm_qtdesignerplugin_widget().")
|
||||
endif()
|
||||
if(NOT ARGS_CREATE_WIDGET_CODE_FROM_VARIABLE)
|
||||
if(NOT ARGS_IMPL_CLASS_NAME)
|
||||
set(ARGS_IMPL_CLASS_NAME "${ARGS_CLASS_NAME}")
|
||||
endif()
|
||||
if(ARGS_CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE)
|
||||
set(_constructor_args "${${ARGS_CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE}}")
|
||||
elseif(ARGS_CONSTRUCTOR_ARGS_CODE)
|
||||
set(_constructor_args "${ARGS_CONSTRUCTOR_ARGS_CODE}")
|
||||
else()
|
||||
set(_constructor_args "(parent)")
|
||||
endif()
|
||||
set(_create_widget_code " return new ${ARGS_IMPL_CLASS_NAME}${_constructor_args};")
|
||||
else()
|
||||
set(_create_widget_code "${${ARGS_CREATE_WIDGET_CODE_FROM_VARIABLE}}")
|
||||
endif()
|
||||
if(ARGS_ICON)
|
||||
if (NOT IS_ABSOLUTE ${ARGS_ICON})
|
||||
set(ARGS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_ICON}")
|
||||
endif()
|
||||
if(NOT EXISTS "${ARGS_ICON}")
|
||||
message(FATAL_ERROR "No such file as passed with ICON when calling ecm_qtdesignerplugin_widget(): ${ARGS_ICON}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# store data about widget, so ecm_add_qtdesignerplugin can access it
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_CLASS_NAME "${ARGS_CLASS_NAME}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_INCLUDE_FILE "${ARGS_INCLUDE_FILE}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_TOOLTIP "${ARGS_TOOLTIP}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_WHATSTHIS "${ARGS_WHATSTHIS}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_GROUP "${ARGS_GROUP}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_ICON "${ARGS_ICON}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_IS_CONTAINER "${_is_container}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_CREATE_WIDGET_CODE "${_create_widget_code}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_INITIALIZE_CODE "${${INITIALIZE_CODE_FROM_VARIABLE}}" PARENT_SCOPE)
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_DOM_XML "${${ARGS_DOM_XML_FROM_VARIABLE}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# helper method
|
||||
function(_ecm_qtdesignerplugin_write_widget designer_src_file widget default_group rc_icon_dir)
|
||||
# prepare data
|
||||
set(_classname "${ECM_QTDESIGNERPLUGIN_${widget}_CLASS_NAME}")
|
||||
set(_factory_classname "${_classname}QtDesignerWidgetFactory")
|
||||
string(REPLACE "::" "__" _factory_classname "${_factory_classname}")
|
||||
set(ECM_QTDESIGNERPLUGIN_${widget}_FACTORY_CLASS_NAME "${_factory_classname}" PARENT_SCOPE)
|
||||
if(ECM_QTDESIGNERPLUGIN_${widget}_IS_CONTAINER)
|
||||
set(_is_container "true")
|
||||
else()
|
||||
set(_is_container "false")
|
||||
endif()
|
||||
_ecm_qtdesignerplugin_escape_cpp_string(_tooltip "${ECM_QTDESIGNERPLUGIN_${widget}_TOOLTIP}")
|
||||
_ecm_qtdesignerplugin_escape_cpp_string(_whatsthis "${ECM_QTDESIGNERPLUGIN_${widget}_WHATSTHIS}")
|
||||
set(_group ${ECM_QTDESIGNERPLUGIN_${widget}_GROUP})
|
||||
if(NOT _group)
|
||||
set(_group "${default_group}")
|
||||
endif()
|
||||
_ecm_qtdesignerplugin_escape_cpp_string(_group "${_group}")
|
||||
set(_dom_xml "${ECM_QTDESIGNERPLUGIN_${widget}_DOM_XML}")
|
||||
if(_dom_xml)
|
||||
string(REPLACE "\"" "\\\"" _dom_xml "${_dom_xml}")
|
||||
set(_dom_xml_method " QString domXml() const override { return QStringLiteral(\"${_dom_xml}\"); }")
|
||||
else()
|
||||
set(_dom_xml_method)
|
||||
endif()
|
||||
set(_icon "${ECM_QTDESIGNERPLUGIN_${widget}_ICON}")
|
||||
if(_icon)
|
||||
get_filename_component(_icon_filename ${_icon} NAME)
|
||||
set(_icon_construct "QIcon(QStringLiteral(\":${rc_icon_dir}/${_icon_filename}\"))")
|
||||
else()
|
||||
set(_icon_construct "QIcon()")
|
||||
endif()
|
||||
set(_initialize_code "${ECM_QTDESIGNERPLUGIN_${widget}_INITIALIZE_CODE}")
|
||||
if(NOT _initialize_code)
|
||||
set(_initialize_code
|
||||
" Q_UNUSED(core);
|
||||
|
||||
if (m_initialized) return;
|
||||
|
||||
m_initialized = true;"
|
||||
)
|
||||
endif()
|
||||
|
||||
# write code
|
||||
file(APPEND ${designer_src_file} "
|
||||
class ${_factory_classname}
|
||||
: public QObject
|
||||
, public QDesignerCustomWidgetInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QDesignerCustomWidgetInterface)
|
||||
|
||||
public:
|
||||
explicit ${_factory_classname}(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
, m_initialized(false)
|
||||
{}
|
||||
|
||||
~${_factory_classname}() override {}
|
||||
|
||||
public: // QDesignerCustomWidgetInterface API
|
||||
bool isInitialized() const override { return m_initialized; }
|
||||
${_dom_xml_method}
|
||||
bool isContainer() const override { return ${_is_container}; }
|
||||
QIcon icon() const override { return ${_icon_construct}; }
|
||||
QString group() const override { return QStringLiteral(\"${_group}\"); }
|
||||
QString includeFile() const override { return QStringLiteral(\"${ECM_QTDESIGNERPLUGIN_${widget}_INCLUDE_FILE}\"); }
|
||||
QString name() const override { return QStringLiteral(\"${_classname}\"); }
|
||||
QString toolTip() const override { return QStringLiteral(\"${_tooltip}\"); }
|
||||
QString whatsThis() const override { return QStringLiteral(\"${_whatsthis}\"); }
|
||||
|
||||
QWidget* createWidget(QWidget* parent) override
|
||||
{
|
||||
${ECM_QTDESIGNERPLUGIN_${widget}_CREATE_WIDGET_CODE}
|
||||
}
|
||||
|
||||
void initialize(QDesignerFormEditorInterface* core) override
|
||||
{
|
||||
${_initialize_code}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_initialized;
|
||||
};
|
||||
"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# helper method
|
||||
function(_ecm_qtdesignerplugin_write_icon_qrc_file rc_file rc_icon_dir)
|
||||
set(_icons ${ARGN})
|
||||
file(WRITE ${rc_file}
|
||||
"<!DOCTYPE RCC><RCC version=\"1.0\">
|
||||
<!-- DO NOT EDIT! Generated from ecm_add_qtdesignerplugin() -->
|
||||
<qresource prefix=\"${rc_icon_dir}\">
|
||||
"
|
||||
)
|
||||
foreach(_icon ${_icons})
|
||||
get_filename_component(_icon_filename ${_icon} NAME)
|
||||
file(APPEND ${rc_file} "<file alias=\"${_icon_filename}\">${_icon}</file>\n")
|
||||
endforeach()
|
||||
file(APPEND ${rc_file}
|
||||
"</qresource>
|
||||
</RCC>
|
||||
"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# This needs to be a macro not a function because of the nested
|
||||
# find_package() call, which will set some variables.
|
||||
macro(ecm_add_qtdesignerplugin target)
|
||||
set(options
|
||||
)
|
||||
set(oneValueArgs
|
||||
NAME
|
||||
OUTPUT_NAME
|
||||
INSTALL_DESTINATION
|
||||
DEFAULT_GROUP
|
||||
COMPONENT
|
||||
DEFAULT_HEADER_CASE
|
||||
DEFAULT_HEADER_EXTENSION
|
||||
DEFAULT_ICON_DIR
|
||||
)
|
||||
set(multiValueArgs
|
||||
WIDGETS
|
||||
LINK_LIBRARIES
|
||||
INCLUDE_FILES
|
||||
SOURCES
|
||||
)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# args sanity check
|
||||
if (NOT ARGS_WIDGETS)
|
||||
message(FATAL_ERROR "No WIDGETS passed when calling ecm_add_qtdesignerplugin().")
|
||||
endif()
|
||||
foreach(_widget ${ARGS_WIDGETS})
|
||||
# using _CLASS_NAME as sample property to find if defined
|
||||
if (NOT ECM_QTDESIGNERPLUGIN_${_widget}_CLASS_NAME)
|
||||
message(FATAL_ERROR "Undefined widget passed when calling ecm_add_qtdesignerplugin(): ${_widget}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT ARGS_NAME)
|
||||
set(ARGS_NAME "${target}")
|
||||
endif()
|
||||
if(NOT ARGS_DEFAULT_GROUP)
|
||||
set(ARGS_DEFAULT_GROUP "Custom")
|
||||
endif()
|
||||
if(NOT ARGS_DEFAULT_HEADER_EXTENSION)
|
||||
set(ARGS_DEFAULT_HEADER_EXTENSION "h")
|
||||
endif()
|
||||
if(NOT ARGS_DEFAULT_HEADER_CASE)
|
||||
set(ARGS_DEFAULT_HEADER_CASE "LOWER_CASE")
|
||||
else()
|
||||
set(_allowed_values "LOWER_CASE" "UPPER_CASE" "SAME_CASE")
|
||||
list(FIND _allowed_values "${ARGS_DEFAULT_HEADER_CASE}" _index)
|
||||
if(_index EQUAL "-1")
|
||||
message(FATAL_ERROR "Unexpected value for DEFAULT_HEADER_CASE argument to ecm_add_qtdesignerplugin(): ${ARGS_DEFAULT_HEADER_CASE}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT ARGS_DEFAULT_ICON_DIR)
|
||||
set(ARGS_DEFAULT_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pics")
|
||||
else()
|
||||
if (NOT IS_ABSOLUTE ${ARGS_DEFAULT_ICON_DIR})
|
||||
set(ARGS_DEFAULT_ICON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DEFAULT_ICON_DIR}")
|
||||
endif()
|
||||
if(NOT EXISTS "${ARGS_DEFAULT_ICON_DIR}")
|
||||
message(FATAL_ERROR "No such directory as passed with DEFAULT_ICON_DIR when calling ecm_add_qtdesignerplugin(): ${ARGS_DEFAULT_ICON_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Check deps
|
||||
if(NOT Qt${QT_MAJOR_VERSION}UiPlugin_FOUND)
|
||||
find_package(Qt${QT_MAJOR_VERSION}UiPlugin QUIET CONFIG)
|
||||
set_package_properties(Qt${QT_MAJOR_VERSION}UiPlugin PROPERTIES
|
||||
PURPOSE "Required to build Qt Designer plugins"
|
||||
TYPE REQUIRED
|
||||
)
|
||||
endif()
|
||||
|
||||
# setup plugin only if uiplugin lib was found, as we do not abort hard the cmake run otherwise
|
||||
if (Qt${QT_MAJOR_VERSION}UiPlugin_FOUND)
|
||||
set(_generation_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}_ECMQtDesignerPlugin")
|
||||
file(MAKE_DIRECTORY "${_generation_dir}")
|
||||
set(_rc_icon_dir "/${ARGS_NAME}/designer")
|
||||
|
||||
# process defaults for widgets
|
||||
foreach(_widget ${ARGS_WIDGETS})
|
||||
set(_class_name "${ECM_QTDESIGNERPLUGIN_${_widget}_CLASS_NAME}")
|
||||
# include file
|
||||
set(_include_file "${ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE}")
|
||||
if(NOT _include_file)
|
||||
set(_include_file "${_class_name}")
|
||||
if (ARGS_DEFAULT_HEADER_CASE STREQUAL "LOWER_CASE")
|
||||
string(TOLOWER "${_include_file}" _include_file)
|
||||
elseif(ARGS_DEFAULT_HEADER_CASE STREQUAL "UPPER_CASE")
|
||||
string(TOUPPER "${_include_file}" _include_file)
|
||||
endif()
|
||||
# turn any namespaces into dir levels
|
||||
string(REPLACE "::" "/" _include_file ${_include_file})
|
||||
set(ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE "${_include_file}.${ARGS_DEFAULT_HEADER_EXTENSION}")
|
||||
endif()
|
||||
# icon
|
||||
set(_icon "${ECM_QTDESIGNERPLUGIN_${_widget}_ICON}")
|
||||
if(NOT _icon)
|
||||
string(TOLOWER "${_class_name}" _icon)
|
||||
# handle any namespaces
|
||||
string(REPLACE "::" "_" _icon "${_icon}")
|
||||
set(_icon "${ARGS_DEFAULT_ICON_DIR}/${_icon}.png")
|
||||
if(EXISTS "${_icon}")
|
||||
set(ECM_QTDESIGNERPLUGIN_${_widget}_ICON "${_icon}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(_plugin_src_file "${_generation_dir}/designerplugin.cpp")
|
||||
set(_srcs
|
||||
${ARGS_SOURCES}
|
||||
${_plugin_src_file}
|
||||
)
|
||||
|
||||
set(_icons)
|
||||
foreach(_widget ${ARGS_WIDGETS})
|
||||
list(APPEND _icons "${ECM_QTDESIGNERPLUGIN_${_widget}_ICON}")
|
||||
endforeach()
|
||||
|
||||
# generate qrc file with icons
|
||||
if (_icons)
|
||||
set(_rc_file "${_generation_dir}/designerplugin.rc")
|
||||
set(_rc_work_file "${_rc_file}.work")
|
||||
|
||||
_ecm_qtdesignerplugin_write_icon_qrc_file("${_rc_work_file}" "${_rc_icon_dir}" ${_icons})
|
||||
# avoid rebuilding if there was no change
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_rc_work_file}" "${_rc_file}"
|
||||
)
|
||||
file(REMOVE "${_rc_work_file}")
|
||||
|
||||
if (QT_MAJOR_VERSION EQUAL "5")
|
||||
qt5_add_resources(_srcs ${_rc_file})
|
||||
else()
|
||||
qt6_add_resources(_srcs ${_rc_file})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# generate source file
|
||||
set(_collection_classname "${ARGS_NAME}QtDesignerWidgetCollection")
|
||||
|
||||
set(_include_files
|
||||
# classes inherited
|
||||
QDesignerCustomWidgetCollectionInterface
|
||||
QDesignerCustomWidgetInterface
|
||||
QObject
|
||||
# classes used
|
||||
QIcon
|
||||
QString
|
||||
${ARGS_INCLUDE_FILES}
|
||||
)
|
||||
foreach(_widget ${ARGS_WIDGETS})
|
||||
list(APPEND _include_files ${ECM_QTDESIGNERPLUGIN_${_widget}_INCLUDE_FILE})
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES _include_files)
|
||||
|
||||
set(_plugin_src_work_file "${_plugin_src_file}.work")
|
||||
file(WRITE ${_plugin_src_work_file} "// DO NOT EDIT! Generated from ecm_add_qtdesignerplugin()\n\n")
|
||||
foreach(_include_file ${_include_files})
|
||||
if (NOT ${_include_file} MATCHES "^[\"<]")
|
||||
set(_include_file "<${_include_file}>")
|
||||
endif()
|
||||
file(APPEND ${_plugin_src_work_file} "#include ${_include_file}\n")
|
||||
endforeach()
|
||||
foreach(_widget ${ARGS_WIDGETS})
|
||||
_ecm_qtdesignerplugin_write_widget(${_plugin_src_work_file} ${_widget} ${ARGS_DEFAULT_GROUP} ${_rc_icon_dir})
|
||||
endforeach()
|
||||
file(APPEND ${_plugin_src_work_file} "
|
||||
class ${_collection_classname}
|
||||
: public QObject
|
||||
, public QDesignerCustomWidgetCollectionInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(
|
||||
QDesignerCustomWidgetCollectionInterface
|
||||
)
|
||||
|
||||
Q_PLUGIN_METADATA(IID \"org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface\")
|
||||
|
||||
public:
|
||||
explicit ${_collection_classname}(QObject* parent = nullptr);
|
||||
|
||||
public: // QDesignerCustomWidgetCollectionInterface API
|
||||
QList<QDesignerCustomWidgetInterface*> customWidgets() const override;
|
||||
|
||||
private:
|
||||
QList<QDesignerCustomWidgetInterface*> m_widgetFactories;
|
||||
};
|
||||
|
||||
${_collection_classname}::${_collection_classname}(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_widgetFactories = QList<QDesignerCustomWidgetInterface*>{
|
||||
"
|
||||
)
|
||||
foreach(_widget ${ARGS_WIDGETS})
|
||||
file(APPEND ${_plugin_src_work_file} " new ${ECM_QTDESIGNERPLUGIN_${_widget}_FACTORY_CLASS_NAME}(this),\n")
|
||||
endforeach()
|
||||
file(APPEND ${_plugin_src_work_file}
|
||||
" };
|
||||
}
|
||||
|
||||
QList<QDesignerCustomWidgetInterface*> ${_collection_classname}::customWidgets() const
|
||||
{
|
||||
return m_widgetFactories;
|
||||
}
|
||||
|
||||
#include \"designerplugin.moc\"
|
||||
"
|
||||
)
|
||||
|
||||
# avoid rebuilding if there was no change
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_plugin_src_work_file}" "${_plugin_src_file}"
|
||||
)
|
||||
file(REMOVE "${_plugin_src_work_file}")
|
||||
|
||||
# setup plugin binary
|
||||
add_library(${target} MODULE ${_srcs})
|
||||
if(NOT WIN32)
|
||||
# Since there are no libraries provided by this module,
|
||||
# there is no point including the build tree in RPath,
|
||||
# and then having to edit it at install time.
|
||||
set_target_properties(${target} PROPERTIES
|
||||
SKIP_BUILD_RPATH TRUE
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
)
|
||||
endif()
|
||||
if (ARGS_OUTPUT_NAME)
|
||||
set_target_properties(${target} PROPERTIES
|
||||
OUTPUT_NAME ${ARGS_OUTPUT_NAME}
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(${target} ${ARGS_LINK_LIBRARIES} Qt${QT_MAJOR_VERSION}::UiPlugin)
|
||||
|
||||
if (DEFINED ARGS_COMPONENT)
|
||||
set(_component COMPONENT ${ARGS_COMPONENT})
|
||||
else()
|
||||
set(_component)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${target} DESTINATION ${ARGS_INSTALL_DESTINATION} ${_component})
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -0,0 +1,176 @@
|
||||
# SPDX-FileCopyrightText: 2013 Alexander Richardson <arichardson.kde@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMAddTests
|
||||
-----------
|
||||
|
||||
Convenience functions for adding tests.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_tests(<sources>
|
||||
LINK_LIBRARIES <library> [<library> [...]]
|
||||
[NAME_PREFIX <prefix>]
|
||||
[GUI]
|
||||
[TARGET_NAMES_VAR <target_names_var>]
|
||||
[TEST_NAMES_VAR <test_names_var>]
|
||||
[WORKING_DIRECTORY <dir>] # Since 5.111
|
||||
)
|
||||
|
||||
A convenience function for adding multiple tests, each consisting of a
|
||||
single source file. For each file in <sources>, an executable target will be
|
||||
created (the name of which will be the basename of the source file). This
|
||||
will be linked against the libraries given with ``LINK_LIBRARIES``. Each
|
||||
executable will be added as a test with the same name.
|
||||
|
||||
If ``NAME_PREFIX`` is given, this prefix will be prepended to the test names, but
|
||||
not the target names. As a result, it will not prevent clashes between tests
|
||||
with the same name in different parts of the project, but it can be used to
|
||||
give an indication of where to look for a failing test.
|
||||
|
||||
If the flag ``GUI`` is passed the test binaries will be GUI executables, otherwise
|
||||
the resulting binaries will be console applications (regardless of the value
|
||||
of ``CMAKE_WIN32_EXECUTABLE`` or ``CMAKE_MACOSX_BUNDLE``). Be aware that this changes
|
||||
the executable entry point on Windows (although some frameworks, such as Qt,
|
||||
abstract this difference away).
|
||||
|
||||
The tests will be build with ``-DQT_FORCE_ASSERTS`` to enable assertions in the
|
||||
test executable even for release builds.
|
||||
|
||||
The ``TARGET_NAMES_VAR`` and ``TEST_NAMES_VAR`` arguments, if given, should specify a
|
||||
variable name to receive the list of generated target and test names,
|
||||
respectively. This makes it convenient to apply properties to them as a
|
||||
whole, for example, using ``set_target_properties()`` or ``set_tests_properties()``.
|
||||
|
||||
The generated target executables will have the effects of ``ecm_mark_as_test()``
|
||||
(from the :module:`ECMMarkAsTest` module) applied to it.
|
||||
|
||||
``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY
|
||||
<https://cmake.org/cmake/help/latest/prop_test/WORKING_DIRECTORY.html>`_
|
||||
in which to execute the test. By default the test will be run in
|
||||
``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using
|
||||
generator expressions. Since 5.111.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_test(
|
||||
<sources>
|
||||
LINK_LIBRARIES <library> [<library> [...]]
|
||||
[TEST_NAME <name>]
|
||||
[NAME_PREFIX <prefix>]
|
||||
[GUI]
|
||||
[WORKING_DIRECTORY <dir>] # Since 5.111
|
||||
)
|
||||
|
||||
This is a single-test form of ``ecm_add_tests`` that allows multiple source files
|
||||
to be used for a single test. If using multiple source files, ``TEST_NAME`` must
|
||||
be given; this will be used for both the target and test names (and, as with
|
||||
``ecm_add_tests()``, the ``NAME_PREFIX`` argument will be prepended to the test name).
|
||||
|
||||
``WORKING_DIRECTORY`` sets the test property `WORKING_DIRECTORY
|
||||
<https://cmake.org/cmake/help/latest/prop_test/WORKING_DIRECTORY.html>`_
|
||||
in which to execute the test. By default the test will be run in
|
||||
``${CMAKE_CURRENT_BINARY_DIR}``. The working directory can be specified using
|
||||
generator expressions. Since 5.111.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(ECMMarkAsTest)
|
||||
include(ECMMarkNonGuiExecutable)
|
||||
|
||||
function(ecm_add_test)
|
||||
set(options GUI)
|
||||
# TARGET_NAME_VAR and TEST_NAME_VAR are undocumented args used by
|
||||
# ecm_add_tests
|
||||
set(oneValueArgs TEST_NAME NAME_PREFIX TARGET_NAME_VAR TEST_NAME_VAR WORKING_DIRECTORY)
|
||||
set(multiValueArgs LINK_LIBRARIES)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
set(_sources ${ARG_UNPARSED_ARGUMENTS})
|
||||
list(LENGTH _sources _sourceCount)
|
||||
if(ARG_TEST_NAME)
|
||||
set(_targetname ${ARG_TEST_NAME})
|
||||
elseif(${_sourceCount} EQUAL "1")
|
||||
#use the source file name without extension as the testname
|
||||
get_filename_component(_targetname ${_sources} NAME_WE)
|
||||
else()
|
||||
#more than one source file passed, but no test name given -> error
|
||||
message(FATAL_ERROR "ecm_add_test() called with multiple source files but without setting \"TEST_NAME\"")
|
||||
endif()
|
||||
|
||||
set(_testname ${ARG_NAME_PREFIX}${_targetname})
|
||||
set(gui_args)
|
||||
if(ARG_GUI)
|
||||
set(gui_args WIN32 MACOSX_BUNDLE)
|
||||
endif()
|
||||
add_executable(${_targetname} ${gui_args} ${_sources})
|
||||
if(NOT ARG_GUI)
|
||||
ecm_mark_nongui_executable(${_targetname})
|
||||
endif()
|
||||
set(test_args)
|
||||
if(DEFINED ARG_WORKING_DIRECTORY)
|
||||
list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY})
|
||||
endif()
|
||||
add_test(NAME ${_testname} COMMAND ${_targetname} ${test_args})
|
||||
target_link_libraries(${_targetname} ${ARG_LINK_LIBRARIES})
|
||||
target_compile_definitions(${_targetname} PRIVATE -DQT_FORCE_ASSERTS)
|
||||
ecm_mark_as_test(${_targetname})
|
||||
if (CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set(_plugin_path ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
|
||||
if (DEFINED ENV{QT_PLUGIN_PATH})
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
# https://stackoverflow.com/questions/59862894/how-do-i-make-a-list-in-cmake-with-the-semicolon-value
|
||||
set(PATHSEP "\\\;") # Don't want cmake to treat it like a list
|
||||
else() # e.g. Linux
|
||||
set(PATHSEP ":")
|
||||
endif()
|
||||
set(_plugin_path "${_plugin_path}${PATHSEP}$ENV{QT_PLUGIN_PATH}")
|
||||
endif()
|
||||
set_property(TEST ${_testname} PROPERTY ENVIRONMENT "QT_PLUGIN_PATH=${_plugin_path}")
|
||||
endif()
|
||||
if (ARG_TARGET_NAME_VAR)
|
||||
set(${ARG_TARGET_NAME_VAR} "${_targetname}" PARENT_SCOPE)
|
||||
endif()
|
||||
if (ARG_TEST_NAME_VAR)
|
||||
set(${ARG_TEST_NAME_VAR} "${_testname}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(ecm_add_tests)
|
||||
set(options GUI)
|
||||
set(oneValueArgs NAME_PREFIX TARGET_NAMES_VAR TEST_NAMES_VAR WORKING_DIRECTORY)
|
||||
set(multiValueArgs LINK_LIBRARIES)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
if(ARG_GUI)
|
||||
set(_exe_type GUI)
|
||||
else()
|
||||
set(_exe_type "")
|
||||
endif()
|
||||
set(test_args)
|
||||
if(DEFINED ARG_WORKING_DIRECTORY)
|
||||
list(APPEND test_args WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY})
|
||||
endif()
|
||||
set(test_names)
|
||||
set(target_names)
|
||||
foreach(_test_source ${ARG_UNPARSED_ARGUMENTS})
|
||||
ecm_add_test(${_test_source}
|
||||
NAME_PREFIX ${ARG_NAME_PREFIX}
|
||||
LINK_LIBRARIES ${ARG_LINK_LIBRARIES}
|
||||
TARGET_NAME_VAR target_name
|
||||
TEST_NAME_VAR test_name
|
||||
${_exe_type}
|
||||
${test_args}
|
||||
)
|
||||
list(APPEND _test_names "${test_name}")
|
||||
list(APPEND _target_names "${target_name}")
|
||||
endforeach()
|
||||
if (ARG_TARGET_NAMES_VAR)
|
||||
set(${ARG_TARGET_NAMES_VAR} "${_target_names}" PARENT_SCOPE)
|
||||
endif()
|
||||
if (ARG_TEST_NAMES_VAR)
|
||||
set(${ARG_TEST_NAMES_VAR} "${_test_names}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
# SPDX-FileCopyrightText: 2020 Andreas Cord-Landwehr <cordlandwehr@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMCheckOutboundLicense
|
||||
-----------------------
|
||||
|
||||
Assert that source file licenses are compatible with a desired outbound license
|
||||
of a compiled binary artifact (e.g., library, plugin or application).
|
||||
|
||||
This module provides the ``ecm_check_outbound_license`` function that
|
||||
generates unit tests for checking the compatibility of license statements.
|
||||
The license statements in all tested files are required to be added by using
|
||||
the SPDX marker ``SPDX-License-Identifier``.
|
||||
|
||||
During the CMake configuration of the project, a temporary license bill of
|
||||
materials (BOM) in SPDX format is generated by calling the REUSE tool
|
||||
(see <https://reuse.software>). That BOM is parsed and license computations
|
||||
based on an internal compatibility matrix are performed.
|
||||
|
||||
Preconditions for using this module:
|
||||
* All tested input source files must contain the SPDX-License-Identifier tag.
|
||||
* Python3 must be available.
|
||||
* The REUSE tool must be available, which generates the bill-of-materials
|
||||
by running ``reuse spdx`` on the tested directory.
|
||||
|
||||
When this module is included, a ``SKIP_LICENSE_TESTS`` option is added (default
|
||||
``OFF``). Turning this option on skips the generation of license tests, which might
|
||||
be convenient if licenses shall not be tested in all build configurations.
|
||||
|
||||
::
|
||||
|
||||
ecm_check_outbound_license(LICENSES <outbound-licenses>
|
||||
FILES <source-files>
|
||||
[TEST_NAME <name>]
|
||||
[WILL_FAIL])
|
||||
|
||||
This method adds a custom unit test to ensure the specified outbound license to be
|
||||
compatible with the specified license headers. Note that a convenient way is to use
|
||||
the CMake ``GLOB`` argument of the ``FILE`` function.
|
||||
|
||||
``LICENSES`` : List of one or multiple outbound license regarding which the compatibility of the source code files shall be tested.
|
||||
Currently, the following values are supported (values are SPDX registry identifiers):
|
||||
* MIT
|
||||
* BSD-2-Clause
|
||||
* BSD-3-Clause
|
||||
* LGPL-2.0-only
|
||||
* LGPL-2.1-only
|
||||
* LGPL-3.0-only
|
||||
* GPL-2.0-only
|
||||
* GPL-3.0-only
|
||||
|
||||
``FILES:`` : List of source files that contain valid SPDX-License-Identifier markers.
|
||||
The paths can be relative to the CMake file that generates the test case
|
||||
or be absolute paths.
|
||||
|
||||
``TEST_NAME`` : Optional parameter that defines the name of the generated test case.
|
||||
If no name is defined, the relative path to the test directory with appended
|
||||
license name is used. Every test has ``licensecheck_`` as prefix.
|
||||
|
||||
``WILL_FAIL`` : Optional parameter that inverts the test result. This parameter is usually only
|
||||
used for tests of the module.
|
||||
|
||||
Since 5.75.0
|
||||
#]=======================================================================]
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
option(SKIP_LICENSE_TESTS "Skip outbound license tests" OFF)
|
||||
|
||||
find_package(Python3)
|
||||
set_package_properties(Python3 PROPERTIES
|
||||
PURPOSE "Required to run tests of module ECMCheckOutboundLicense"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(ReuseTool)
|
||||
set_package_properties(ReuseTool PROPERTIES
|
||||
PURPOSE "Required to run tests of module ECMCheckOutboundLicense"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
if (NOT SKIP_LICENSE_TESTS AND NOT REUSETOOL_FOUND)
|
||||
add_feature_info(SPDX_LICENSE_TESTING FALSE "Automatic license testing based on SPDX definitions (requires reuse tool)")
|
||||
message(WARNING "Reuse tool not found, skipping test generation")
|
||||
else()
|
||||
add_feature_info(SPDX_LICENSE_TESTING TRUE "Automatic license testing based on SPDX definitions (requires reuse tool)")
|
||||
endif()
|
||||
|
||||
set(SPDX_BOM_OUTPUT "${CMAKE_BINARY_DIR}/spdx.txt")
|
||||
|
||||
# test fixture for generating SPDX bill of materials
|
||||
if(SKIP_LICENSE_TESTS OR NOT REUSETOOL_FOUND)
|
||||
message(STATUS "Skipping execution of outbound license tests.")
|
||||
else()
|
||||
add_test(
|
||||
NAME generate_spdx_bom
|
||||
COMMAND reuse spdx -o ${SPDX_BOM_OUTPUT}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
set_tests_properties(generate_spdx_bom PROPERTIES FIXTURES_SETUP SPDX_BOM)
|
||||
endif()
|
||||
|
||||
function(ecm_check_outbound_license)
|
||||
if(SKIP_LICENSE_TESTS OR NOT REUSETOOL_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_options WILL_FAIL)
|
||||
set(_oneValueArgs TEST_NAME)
|
||||
set(_multiValueArgs LICENSES FILES)
|
||||
cmake_parse_arguments(ARG "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN} )
|
||||
|
||||
if(NOT ARG_LICENSES)
|
||||
message(FATAL_ERROR "No LICENSES argument given to ecm_check_outbound_license")
|
||||
endif()
|
||||
|
||||
if(NOT ARG_FILES)
|
||||
message(WARNING "No FILES argument given to ecm_check_outbound_license")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT ARG_TEST_NAME)
|
||||
# compute test name based on licensecheck_<relative-path>_<licence> if not name given
|
||||
string(REPLACE "${CMAKE_SOURCE_DIR}/" "" TEMP_TEST_NAME "${CMAKE_CURRENT_SOURCE_DIR}_${ARG_LICENSE}")
|
||||
string(MAKE_C_IDENTIFIER ${TEMP_TEST_NAME} ARG_TEST_NAME)
|
||||
endif()
|
||||
|
||||
# generate file with list of relative file paths
|
||||
string(REPLACE "${CMAKE_BINARY_DIR}/" "" RELATIVE_PREFIX_PATH ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(OUTPUT_FILE ${CMAKE_BINARY_DIR}/licensecheck_${ARG_TEST_NAME}.txt)
|
||||
file(REMOVE ${OUTPUT_FILE})
|
||||
foreach(_file ${ARG_FILES})
|
||||
# check script expects files to start with "./", which must be relative to CMAKE_SOURCE_DIR
|
||||
if (IS_ABSOLUTE ${_file})
|
||||
string(REPLACE ${CMAKE_SOURCE_DIR} "." TEMPORARY_PATH ${_file})
|
||||
file(APPEND ${OUTPUT_FILE} "${TEMPORARY_PATH}\n")
|
||||
else()
|
||||
file(APPEND ${OUTPUT_FILE} "./${RELATIVE_PREFIX_PATH}/${_file}\n")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
file(COPY ${ECM_MODULE_DIR}/check-outbound-license.py DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
foreach(_license ${ARG_LICENSES})
|
||||
string(MAKE_C_IDENTIFIER ${_license} LICENSE_ID)
|
||||
add_test(
|
||||
NAME licensecheck_${ARG_TEST_NAME}_${LICENSE_ID}
|
||||
COMMAND python3 ${CMAKE_BINARY_DIR}/check-outbound-license.py -l ${_license} -s ${SPDX_BOM_OUTPUT} -i ${OUTPUT_FILE}
|
||||
)
|
||||
set_tests_properties(licensecheck_${ARG_TEST_NAME}_${LICENSE_ID} PROPERTIES FIXTURES_REQUIRED SPDX_BOM)
|
||||
set_tests_properties(licensecheck_${ARG_TEST_NAME}_${LICENSE_ID} PROPERTIES WILL_FAIL ${ARG_WILL_FAIL})
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,80 @@
|
||||
# SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMConfiguredInstall
|
||||
--------------------
|
||||
|
||||
Takes a list of files, runs ``configure_file`` on each and installs the resultant configured files in the given location.
|
||||
|
||||
Any suffix of ".in" in the passed file names will be stripped from the file name at the installed location.
|
||||
|
||||
::
|
||||
|
||||
ecm_install_configured_files(
|
||||
INPUT <file> [<file2> [...]]
|
||||
DESTINATION <INSTALL_DIRECTORY>
|
||||
[COPYONLY]
|
||||
[ESCAPE_QUOTES]
|
||||
[@ONLY]
|
||||
[COMPONENT <component>])
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_install_configured_files(INPUT foo.txt.in DESTINATION ${KDE_INSTALL_DATADIR} @ONLY)
|
||||
|
||||
This will install the file as foo.txt with any cmake variable replacements made into the data directory.
|
||||
|
||||
Since 5.73.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ecm_install_configured_files)
|
||||
set(options COPYONLY ESCAPE_QUOTES @ONLY)
|
||||
set(oneValueArgs DESTINATION COMPONENT)
|
||||
set(multiValueArgs INPUT)
|
||||
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}"
|
||||
"${multiValueArgs}" ${ARGN})
|
||||
|
||||
|
||||
if(ARGS_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown arguments given to ecm_install_configured_file(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if (NOT ARGS_DESTINATION)
|
||||
message(FATAL_ERROR "missing DESTINATION argument to ECMConfiguredInstall")
|
||||
endif()
|
||||
|
||||
foreach(_input ${ARGS_INPUT})
|
||||
# convert absolute paths
|
||||
get_filename_component(_name ${_input} NAME)
|
||||
|
||||
string(REGEX REPLACE "\\.in$" "" _name ${_name})
|
||||
|
||||
set(_out_file ${CMAKE_CURRENT_BINARY_DIR}/${_name})
|
||||
|
||||
set(_configure_args)
|
||||
if (ARGS_COPY_ONLY)
|
||||
list(APPEND _configure_args COPY_ONLY)
|
||||
endif()
|
||||
if (ARGS_ESCAPE_QUOTES)
|
||||
list(APPEND _configure_args ESCAPE_QUOTES)
|
||||
endif()
|
||||
if (ARGS_@ONLY)
|
||||
list(APPEND _configure_args @ONLY)
|
||||
endif()
|
||||
|
||||
configure_file(${_input} ${_out_file} ${_configure_args})
|
||||
|
||||
if (DEFINED ARGS_COMPONENT)
|
||||
set(_component COMPONENT ${ARGS_COMPONENT})
|
||||
else()
|
||||
set(_component)
|
||||
endif()
|
||||
|
||||
install(FILES ${_out_file} DESTINATION ${ARGS_DESTINATION} ${_component})
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMCoverageOption
|
||||
--------------------
|
||||
|
||||
Allow users to easily enable GCov code coverage support.
|
||||
|
||||
Code coverage allows you to check how much of your codebase is covered by
|
||||
your tests. This module makes it easy to build with support for
|
||||
`GCov <https://gcc.gnu.org/onlinedocs/gcc/Gcov.html>`_.
|
||||
|
||||
When this module is included, a ``BUILD_COVERAGE`` option is added (default
|
||||
``OFF``). Turning this option on enables GCC's coverage instrumentation, and
|
||||
links against ``libgcov``.
|
||||
|
||||
.. note::
|
||||
This will probably break the build if you are not using GCC.
|
||||
|
||||
Since 1.3.0.
|
||||
#]=======================================================================]
|
||||
|
||||
option(BUILD_COVERAGE "Build the project with gcov support" OFF)
|
||||
|
||||
if(BUILD_COVERAGE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov")
|
||||
endif()
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
# SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMCreateQmFromPoFiles
|
||||
----------------------
|
||||
|
||||
.. warning:: This module is deprecated and will be removed by ECM 1.0. Use
|
||||
:module:`ECMPoQmTools` instead.
|
||||
|
||||
Generate QTranslator (.qm) catalogs from Gettext (.po) catalogs.
|
||||
|
||||
::
|
||||
|
||||
ecm_create_qm_from_po_files(PO_FILES <file1>... <fileN>
|
||||
[CATALOG_NAME <catalog_name>]
|
||||
[INSTALL_DESTINATION <install_destination>])
|
||||
|
||||
Creates the necessary rules to compile .po files into .qm files, and install
|
||||
them.
|
||||
|
||||
The .qm files are installed in ``<install_destination>/<lang>/LC_MESSAGES``,
|
||||
where <install_destination> is the INSTALL_DESTINATION argument and <lang> is
|
||||
extracted from the "Language" field inside the .po file.
|
||||
|
||||
INSTALL_DESTINATION defaults to ``${LOCALE_INSTALL_DIR}`` if defined,
|
||||
otherwise it uses ``${CMAKE_INSTALL_LOCALEDIR}`` if that is defined, otherwise
|
||||
it uses ``share/locale``.
|
||||
|
||||
CATALOG_NAME defines the name of the installed .qm files. If set, .qm files
|
||||
will be installed as ``<catalog_name>.qm``. If not set .qm files will be named
|
||||
after the name of their source .po file.
|
||||
|
||||
Setting the catalog name is useful when all .po files for a target are kept
|
||||
in a single source directory. For example, the "mylib" probject might keep all
|
||||
its translations in a "po" directory, like this::
|
||||
|
||||
po/
|
||||
es.po
|
||||
fr.po
|
||||
|
||||
Without setting CATALOG_NAME, those .po will be turned into .qm and installed
|
||||
as::
|
||||
|
||||
share/locale/fr/LC_MESSAGES/fr.qm
|
||||
share/locale/es/LC_MESSAGES/es.qm
|
||||
|
||||
If CATALOG_NAME is set to "mylib", they will be installed as::
|
||||
|
||||
share/locale/fr/LC_MESSAGES/mylib.qm
|
||||
share/locale/es/LC_MESSAGES/mylib.qm
|
||||
|
||||
Which is what the loader created by ecm_create_qm_loader() expects.
|
||||
|
||||
ecm_create_qm_from_po_files() creates a "translation" target. This target
|
||||
builds all .po files into .qm files.
|
||||
|
||||
::
|
||||
|
||||
ecm_create_qm_loader(<source_files_var> <catalog_name>)
|
||||
|
||||
ecm_create_qm_loader() generates a C++ file which ensures translations are
|
||||
automatically loaded at startup. The path of the .cpp file is appended to
|
||||
<source_files_var>. Typical usage is like:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set(mylib_SRCS foo.cpp bar.cpp)
|
||||
ecm_create_qm_loader(mylib_SRCS mylib)
|
||||
add_library(mylib ${mylib_SRCS})
|
||||
|
||||
This generates a C++ file which loads "mylib.qm" at startup, assuming it has
|
||||
been installed by ecm_create_qm_from_po_files(), and compiles it into ``mylib``.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
message(AUTHOR_WARNING "ECMCreateQmFromPoFiles is deprecated and will be removed before the release of Extra CMake Modules 1.0. Use ECMPoQmTools instead.")
|
||||
|
||||
# Stolen from FindGettext.cmake
|
||||
function(_ECM_QM_GET_UNIQUE_TARGET_NAME _name _unique_name)
|
||||
set(propertyName "_ECM_QM_UNIQUE_COUNTER_${_name}")
|
||||
get_property(currentCounter GLOBAL PROPERTY "${propertyName}")
|
||||
if(NOT currentCounter)
|
||||
set(currentCounter 1)
|
||||
endif()
|
||||
set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE)
|
||||
math(EXPR currentCounter "${currentCounter} + 1")
|
||||
set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} )
|
||||
endfunction()
|
||||
|
||||
function(_ECM_QM_EXTRACT_LANGUAGE out_language po_file)
|
||||
file(READ ${po_file} content)
|
||||
# msginit uses "Language: <lang>" but lconvert uses "X-Language: <lang>"
|
||||
string(REGEX MATCH "\"(X-)?Language: ([a-z_A-Z]+)" match "${content}")
|
||||
if (NOT match)
|
||||
message(FATAL_ERROR "_ECM_QM_EXTRACT_LANGUAGE: Could not extract language from ${po_file}")
|
||||
endif()
|
||||
set(${out_language} ${CMAKE_MATCH_2} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_ECM_QM_CREATE_TARGET install_destination catalog_name)
|
||||
# Find lconvert
|
||||
if(TARGET Qt5::lconvert)
|
||||
set(lconvert_executable Qt5::lconvert)
|
||||
else()
|
||||
# Qt < 5.3.1 does not define Qt5::lconvert
|
||||
get_target_property(lrelease_location Qt5::lrelease LOCATION)
|
||||
get_filename_component(lrelease_path ${lrelease_location} PATH)
|
||||
find_program(lconvert_executable
|
||||
NAMES lconvert-qt5 lconvert
|
||||
PATHS ${lrelease_path}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
|
||||
if (catalog_name)
|
||||
set(install_args RENAME ${catalog_name}.qm)
|
||||
else()
|
||||
set(install_args)
|
||||
endif()
|
||||
|
||||
foreach (it ${ARGN})
|
||||
get_filename_component(filename_base ${it} ABSOLUTE)
|
||||
get_filename_component(filename_base ${it} NAME_WE)
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(tsfile ${CMAKE_CURRENT_BINARY_DIR}/${filename_base}.ts)
|
||||
set(qmfile ${CMAKE_CURRENT_BINARY_DIR}/${filename_base}.qm)
|
||||
|
||||
_ECM_QM_EXTRACT_LANGUAGE(language ${it})
|
||||
|
||||
# lconvert from .po to .ts and then run lupdate to generate the correct
|
||||
# strings. Finally run lrelease to create the .qm files.
|
||||
add_custom_command(OUTPUT ${qmfile}
|
||||
COMMAND ${lconvert_executable}
|
||||
ARGS -i ${it} -o ${tsfile} -target-language ${language}
|
||||
COMMAND Qt5::lrelease
|
||||
ARGS -removeidentical -silent ${tsfile} -qm ${qmfile}
|
||||
DEPENDS ${it}
|
||||
)
|
||||
install(
|
||||
FILES ${qmfile}
|
||||
DESTINATION ${install_destination}/${language}/LC_MESSAGES
|
||||
${install_args}
|
||||
)
|
||||
set(qmfiles ${qmfiles} ${qmfile})
|
||||
endforeach()
|
||||
|
||||
if(NOT TARGET translations)
|
||||
add_custom_target(translations ALL)
|
||||
endif()
|
||||
_ecm_qm_get_unique_target_name(translations target_name)
|
||||
add_custom_target(${target_name} DEPENDS ${qmfiles})
|
||||
add_dependencies(translations ${target_name})
|
||||
endfunction()
|
||||
|
||||
function(ECM_CREATE_QM_LOADER out_var catalog_name)
|
||||
# catalog_name is used in ECMQmLoader.cpp.in
|
||||
configure_file(${ECM_MODULE_DIR}/ECMQmLoader.cpp.in ECMQmLoader.cpp @ONLY)
|
||||
set(${out_var} ${${out_var}} ${CMAKE_CURRENT_BINARY_DIR}/ECMQmLoader.cpp PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(ECM_CREATE_QM_FROM_PO_FILES)
|
||||
# This gives us Qt5::lrelease and Qt5::lupdate but unfortunately no Qt5::lconvert
|
||||
# See https://bugreports.qt-project.org/browse/QTBUG-37937
|
||||
find_package(Qt5LinguistTools CONFIG REQUIRED)
|
||||
|
||||
foreach (arg ${ARGN})
|
||||
if (arg STREQUAL "PO_DIR")
|
||||
_ecm_create_qm_from_po_files_legacy(${ARGN})
|
||||
return()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(options)
|
||||
set(oneValueArgs CATALOG_NAME INSTALL_DESTINATION)
|
||||
set(multiValueArgs PO_FILES)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ARGS_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to ECM_CREATE_QM_FROM_PO_FILES(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_PO_FILES)
|
||||
message(FATAL_ERROR "ECM_CREATE_QM_FROM_PO_FILES(): Must be called with PO_FILES argument")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_INSTALL_DESTINATION)
|
||||
if (LOCALE_INSTALL_DIR)
|
||||
set(ARGS_INSTALL_DESTINATION "${LOCALE_INSTALL_DIR}")
|
||||
elseif (CMAKE_INSTALL_LOCALEDIR)
|
||||
set(ARGS_INSTALL_DESTINATION "${CMAKE_INSTALL_LOCALEDIR}")
|
||||
else()
|
||||
set(ARGS_INSTALL_DESTINATION share/locale)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
_ecm_qm_create_target(${ARGS_INSTALL_DESTINATION} "${ARGS_CATALOG_NAME}" ${ARGS_PO_FILES})
|
||||
endfunction()
|
||||
|
||||
# Handles the syntax exposed in ECM 0.0.12, shipped with KDE Frameworks 5.0beta1
|
||||
#
|
||||
# This is a macro so that the value written in ${ARGS_CREATE_LOADER} is
|
||||
# correctly propagated to ECM_CREATE_QM_FROM_PO_FILES parent scope. If it were
|
||||
# not a macro, ECM_CREATE_QM_FROM_PO_FILES would have to ckeck if
|
||||
# CREATE_LOADER is in the arguments and propagate the value itself.
|
||||
macro(_ECM_CREATE_QM_FROM_PO_FILES_LEGACY)
|
||||
set(options)
|
||||
set(oneValueArgs PO_DIR POT_NAME DATA_INSTALL_DIR DATA_INSTALL_SUB_DIR CREATE_LOADER)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ARGS_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to _ECM_CREATE_QM_FROM_PO_FILES_LEGACY(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_POT_NAME)
|
||||
message(FATAL_ERROR "Required argument POT_NAME missing in _ECM_CREATE_QM_FROM_PO_FILES_LEGACY() call")
|
||||
endif()
|
||||
get_filename_component(catalog_name ${ARGS_POT_NAME} NAME_WE)
|
||||
|
||||
if (LOCALE_INSTALL_DIR)
|
||||
set(install_destination "${LOCALE_INSTALL_DIR}")
|
||||
elseif (CMAKE_INSTALL_LOCALEDIR)
|
||||
set(install_destination "${CMAKE_INSTALL_LOCALEDIR}")
|
||||
else()
|
||||
set(install_destination share/locale)
|
||||
endif()
|
||||
|
||||
file(GLOB po_files "${ARGS_PO_DIR}/*.po")
|
||||
_ecm_qm_create_target(${install_destination} "${catalog_name}" ${po_files})
|
||||
|
||||
if (ARGS_CREATE_LOADER)
|
||||
ecm_create_qm_loader(loader ${catalog_name})
|
||||
set(${ARGS_CREATE_LOADER} ${loader} PARENT_SCOPE)
|
||||
endif()
|
||||
endmacro()
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
# SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMDeprecationSettings
|
||||
-----------------------
|
||||
|
||||
This module provides the ``ecm_set_disabled_deprecation_versions`` function setting the excluding
|
||||
deprecated API for Qt and KF projects.
|
||||
|
||||
This method expects pairs of the identifier and deprecation version.
|
||||
For the identifier ``QT`` this functions adds the definition ``QT_DISABLE_DEPRECATED_BEFORE`` with the given version in a hexadecimal format.
|
||||
Otherwise the name for the definition is generated using ``${IDENTIFIER}_DISABLE_DEPRECATED_BEFORE_AND_AT``,
|
||||
following the naming of the generated code in :module:`ECMGenerateExportHeader`.
|
||||
The version for the definition can be overwritten, by passing definition name and the deprecation version
|
||||
as a CMake definition. This allows one to exclude deprecations without having to edit the CMakeLists.txt file.
|
||||
|
||||
This module provides the following function:
|
||||
|
||||
::
|
||||
|
||||
ecm_set_disabled_deprecation_versions(
|
||||
[DISABLE_NEWER_WARNINGS] # since 5.96
|
||||
[<identifier> <deprecation_version>]
|
||||
[<identifier2> <deprecation_version2>]
|
||||
)
|
||||
|
||||
``DISABLE_NEWER_WARNINGS`` disables additionally the compiler warnings for API deprecated in newer versions
|
||||
of the same major version.
|
||||
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set(QT_MIN_VERSION "5.15.2")
|
||||
set(KF5_MIN_VERSION "5.90")
|
||||
|
||||
ecm_set_disabled_deprecation_versions(
|
||||
QT ${QT_MIN_VERSION}
|
||||
KF ${KF5_MIN_VERSION}
|
||||
KCOREADDONS 5.89.0 # In case we depend on deprecated KCoreAddons API
|
||||
)
|
||||
|
||||
|
||||
|
||||
Since 5.91
|
||||
#]=======================================================================]
|
||||
|
||||
function (ecm_set_disabled_deprecation_versions)
|
||||
cmake_parse_arguments(ARGS "SHOW_DEPRECATIONS;DISABLE_NEWER_WARNINGS" "" "" ${ARGN})
|
||||
|
||||
# support legacy initial flag to opt-in to warnings
|
||||
if (ARGS_SHOW_DEPRECATIONS)
|
||||
message(DEPRECATION "SHOW_DEPRECATIONS is deprecated, since 5.96 warnings are enabled by default.")
|
||||
endif()
|
||||
if (ARGS_SHOW_DEPRECATIONS AND ARGS_DISABLE_NEWER_WARNINGS)
|
||||
message(FATAL_ERROR "SHOW_DEPRECATIONS && DISABLE_NEWER_WARNINGS cannot be set both.")
|
||||
endif()
|
||||
set(show_newer_warnings TRUE)
|
||||
if (ARGS_DISABLE_NEWER_WARNINGS)
|
||||
set(show_newer_warnings FALSE)
|
||||
endif()
|
||||
|
||||
list(LENGTH ARGS_UNPARSED_ARGUMENTS PAIR_COUNT)
|
||||
math(EXPR is_even_number "${PAIR_COUNT} % 2")
|
||||
if (NOT is_even_number EQUAL 0)
|
||||
message(FATAL_ERROR "Expected number of arguments is an even number of identifiers and version")
|
||||
endif()
|
||||
math(EXPR number_pairs "(${PAIR_COUNT} / 2) - 1")
|
||||
foreach (it RANGE ${number_pairs})
|
||||
# get values
|
||||
math(EXPR current_index "${it} * 2")
|
||||
list(GET ARGS_UNPARSED_ARGUMENTS ${current_index} DEPRECATION_NAME)
|
||||
math(EXPR next_index "(${it} *2) + 1")
|
||||
list(GET ARGS_UNPARSED_ARGUMENTS ${next_index} DEPRECATION_VERSION)
|
||||
|
||||
# get the string identifier for the target definition
|
||||
string(COMPARE EQUAL ${DEPRECATION_NAME} "QT" IS_QT_DEPRECATION)
|
||||
if (IS_QT_DEPRECATION)
|
||||
set(DEPRECATION_DEFINITION_NAME QT_DISABLE_DEPRECATED_BEFORE)
|
||||
else()
|
||||
set(DEPRECATION_DEFINITION_NAME ${DEPRECATION_NAME}_DISABLE_DEPRECATED_BEFORE_AND_AT)
|
||||
endif()
|
||||
# we want to be able to set this version without being forced to edit the CMakeLists.txt file
|
||||
if (${${DEPRECATION_DEFINITION_NAME}})
|
||||
set(DEPRECATION_VERSION "${${DEPRECATION_DEFINITION_NAME}}")
|
||||
endif()
|
||||
|
||||
# make a sanity check to make sure we do not get malformed versions
|
||||
_ecm_version_triple_sanity_check("${DEPRECATION_VERSION}")
|
||||
|
||||
# add the actual compile definition with the given hex value
|
||||
_ecm_geh_generate_hex_number_from_version(DEPRECATION_HEX_VERSION ${DEPRECATION_VERSION})
|
||||
add_definitions(-D${DEPRECATION_DEFINITION_NAME}=${DEPRECATION_HEX_VERSION})
|
||||
|
||||
# Set the version for the deprecation warnings
|
||||
if (show_newer_warnings)
|
||||
string(REGEX MATCH "([0-9]+)\\." _ ${DEPRECATION_VERSION})
|
||||
if (NOT CMAKE_MATCH_1)
|
||||
message(FATAL_ERROR "Failed to get major version from ${DEPRECATION_VERSION}")
|
||||
endif()
|
||||
# Add 1 to the major version and store it as a hex value
|
||||
math(EXPR next_major_version "(${CMAKE_MATCH_1} + 1) * 65536 " OUTPUT_FORMAT HEXADECIMAL)
|
||||
add_definitions(-D${DEPRECATION_NAME}_DEPRECATED_WARNINGS_SINCE=${next_major_version})
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# helper method
|
||||
function(_ecm_geh_generate_hex_number_from_version _var_name _version)
|
||||
set(_hexnumber 0)
|
||||
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)(\\.([0-9]+))?$" _ ${_version})
|
||||
|
||||
# Set the patch version to 0, if none is specified by the regex.
|
||||
# This is the case for min. versions that don't specify the patch level, like in the snipped of the method docs.
|
||||
if (NOT CMAKE_MATCH_4)
|
||||
set(CMAKE_MATCH_4 "0")
|
||||
endif()
|
||||
|
||||
math(EXPR _hexnumber "${CMAKE_MATCH_1}*65536 + ${CMAKE_MATCH_2}*256 + ${CMAKE_MATCH_4}" OUTPUT_FORMAT HEXADECIMAL)
|
||||
set(${_var_name} ${_hexnumber} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function (_ecm_version_triple_sanity_check unchecked_version)
|
||||
# helper string
|
||||
set(_version_triple_regexp "^([0-9]+)\\.([0-9]+)(\\.([0-9]+))?$")
|
||||
# args sanity check
|
||||
if (NOT unchecked_version)
|
||||
message(FATAL_ERROR "No VERSION passed when calling ecm_set_deprecation_versions().")
|
||||
elseif(NOT unchecked_version MATCHES ${_version_triple_regexp})
|
||||
message(FATAL_ERROR "VERSION ${unchecked_version} expected to be in x.y.z format when calling ecm_set_deprecation_versions().")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,167 @@
|
||||
# SPDX-FileCopyrightText: 2014 Mathieu Tarral <mathieu.tarral@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMEnableSanitizers
|
||||
-------------------
|
||||
|
||||
Enable compiler sanitizer flags.
|
||||
|
||||
The following sanitizers are supported:
|
||||
|
||||
- Address Sanitizer
|
||||
- Memory Sanitizer
|
||||
- Thread Sanitizer
|
||||
- Leak Sanitizer
|
||||
- Undefined Behaviour Sanitizer
|
||||
|
||||
All of them are implemented in Clang, depending on your version, and
|
||||
there is an work in progress in GCC, where some of them are currently
|
||||
implemented.
|
||||
|
||||
This module will check your current compiler version to see if it
|
||||
supports the sanitizers that you want to enable
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Simply add::
|
||||
|
||||
include(ECMEnableSanitizers)
|
||||
|
||||
to your ``CMakeLists.txt``. Note that this module is included in
|
||||
:kde-module:`KDECompilerSettings`, so projects using that module do not need to also
|
||||
include this one.
|
||||
|
||||
The sanitizers are not enabled by default. Instead, you must set
|
||||
``ECM_ENABLE_SANITIZERS`` (either in your ``CMakeLists.txt`` or on the
|
||||
command line) to a semicolon-separated list of sanitizers you wish to enable.
|
||||
The options are:
|
||||
|
||||
- address
|
||||
- memory
|
||||
- thread
|
||||
- leak
|
||||
- undefined
|
||||
- fuzzer
|
||||
|
||||
The sanitizers "address", "memory" and "thread" are mutually exclusive. You
|
||||
cannot enable two of them in the same build.
|
||||
|
||||
"leak" requires the "address" sanitizer.
|
||||
|
||||
.. note::
|
||||
|
||||
To reduce the overhead induced by the instrumentation of the sanitizers, it
|
||||
is advised to enable compiler optimizations (``-O1`` or higher).
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
This is an example of usage::
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' ..
|
||||
|
||||
.. note::
|
||||
|
||||
Most of the sanitizers will require Clang. To enable it, use::
|
||||
|
||||
-DCMAKE_CXX_COMPILER=clang++
|
||||
|
||||
Since 1.3.0.
|
||||
#]=======================================================================]
|
||||
|
||||
# MACRO check_compiler_version
|
||||
#-----------------------------
|
||||
macro (check_compiler_version gcc_required_version clang_required_version msvc_required_version)
|
||||
if (
|
||||
(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "GNU"
|
||||
AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_required_version}
|
||||
)
|
||||
OR
|
||||
(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "Clang"
|
||||
AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_required_version}
|
||||
)
|
||||
OR
|
||||
(
|
||||
CMAKE_CXX_COMPILER_ID MATCHES "MSVC"
|
||||
AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${msvc_required_version}
|
||||
)
|
||||
)
|
||||
# error !
|
||||
message(FATAL_ERROR "You ask to enable the sanitizer ${CUR_SANITIZER},
|
||||
but your compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}
|
||||
does not support it !
|
||||
You should use at least GCC ${gcc_required_version}, Clang ${clang_required_version}
|
||||
or MSVC ${msvc_required_version}
|
||||
(99.99 means not implemented yet)")
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
# MACRO check_compiler_support
|
||||
#------------------------------
|
||||
macro (enable_sanitizer_flags sanitize_option)
|
||||
if (${sanitize_option} MATCHES "address")
|
||||
check_compiler_version("4.8" "3.1" "19.28")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=address")
|
||||
else()
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls")
|
||||
set(XSAN_LINKER_FLAGS "asan")
|
||||
endif()
|
||||
elseif (${sanitize_option} MATCHES "thread")
|
||||
check_compiler_version("4.8" "3.1" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=thread")
|
||||
set(XSAN_LINKER_FLAGS "tsan")
|
||||
elseif (${sanitize_option} MATCHES "memory")
|
||||
check_compiler_version("99.99" "3.1" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=memory")
|
||||
elseif (${sanitize_option} MATCHES "leak")
|
||||
check_compiler_version("4.9" "3.4" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=leak")
|
||||
set(XSAN_LINKER_FLAGS "lsan")
|
||||
elseif (${sanitize_option} MATCHES "undefined")
|
||||
check_compiler_version("4.9" "3.1" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls")
|
||||
elseif (${sanitize_option} MATCHES "fuzzer")
|
||||
check_compiler_version("99.99" "6.0" "99.99")
|
||||
set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer")
|
||||
else ()
|
||||
message(FATAL_ERROR "Compiler sanitizer option \"${sanitize_option}\" not supported.")
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
if (ECM_ENABLE_SANITIZERS)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
# for each element of the ECM_ENABLE_SANITIZERS list
|
||||
foreach ( CUR_SANITIZER ${ECM_ENABLE_SANITIZERS} )
|
||||
# lowercase filter
|
||||
string(TOLOWER ${CUR_SANITIZER} CUR_SANITIZER)
|
||||
# check option and enable appropriate flags
|
||||
enable_sanitizer_flags ( ${CUR_SANITIZER} )
|
||||
# TODO: GCC will not link pthread library if enabled ASan
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XSAN_COMPILE_FLAGS}" )
|
||||
endif()
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" )
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
link_libraries(${XSAN_LINKER_FLAGS})
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
string(REPLACE "-Wl,--no-undefined " "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ")
|
||||
string(REPLACE "-Wl,--no-undefined " "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ")
|
||||
endif ()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Tried to enable sanitizers (-DECM_ENABLE_SANITIZERS=${ECM_ENABLE_SANITIZERS}), \
|
||||
but compiler (${CMAKE_CXX_COMPILER_ID}) does not have sanitizer support")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,38 @@
|
||||
# SPDX-FileCopyrightText: 2023 David Faure <faure@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMFeatureSummary
|
||||
-----------------
|
||||
|
||||
Call feature_summary(), except when being called from a subdirectory.
|
||||
This ensures that frameworks being used as submodules by third-party applications
|
||||
do not call feature_summary(), so that it doesn't end up being called multiple
|
||||
times in the same cmake run.
|
||||
|
||||
|
||||
::
|
||||
|
||||
include(ECMFeatureSummary)
|
||||
ecm_feature_summary([... see feature_summary documentation ...])
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(ECM REQUIRED)
|
||||
include(ECMFeatureSummary)
|
||||
ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
Since 5.247
|
||||
#]=======================================================================]
|
||||
|
||||
include(FeatureSummary)
|
||||
function(ecm_feature_summary)
|
||||
|
||||
if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
feature_summary(${ARGV})
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
@@ -0,0 +1,277 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMFindModuleHelpers
|
||||
--------------------
|
||||
|
||||
Helper macros for find modules: ``ecm_find_package_version_check()``,
|
||||
``ecm_find_package_parse_components()`` and
|
||||
``ecm_find_package_handle_library_components()``.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_package_version_check(<name>)
|
||||
|
||||
Prints warnings if the CMake version or the project's required CMake version
|
||||
is older than that required by extra-cmake-modules.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_package_parse_components(<name>
|
||||
RESULT_VAR <variable>
|
||||
KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||
[SKIP_DEPENDENCY_HANDLING])
|
||||
|
||||
This macro will populate <variable> with a list of components found in
|
||||
<name>_FIND_COMPONENTS, after checking that all those components are in the
|
||||
list of ``KNOWN_COMPONENTS``; if there are any unknown components, it will print
|
||||
an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
|
||||
``return()``.
|
||||
|
||||
The order of components in <variable> is guaranteed to match the order they
|
||||
are listed in the ``KNOWN_COMPONENTS`` argument.
|
||||
|
||||
If ``SKIP_DEPENDENCY_HANDLING`` is not set, for each component the variable
|
||||
<name>_<component>_component_deps will be checked for dependent components.
|
||||
If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||
dependencies will also be added to <variable>.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_package_handle_library_components(<name>
|
||||
COMPONENTS <component> [<component> [...]]
|
||||
[SKIP_DEPENDENCY_HANDLING])
|
||||
[SKIP_PKG_CONFIG])
|
||||
|
||||
Creates an imported library target for each component. The operation of this
|
||||
macro depends on the presence of a number of CMake variables.
|
||||
|
||||
The <name>_<component>_lib variable should contain the name of this library,
|
||||
and <name>_<component>_header variable should contain the name of a header
|
||||
file associated with it (whatever relative path is normally passed to
|
||||
'#include'). <name>_<component>_header_subdir variable can be used to specify
|
||||
which subdirectory of the include path the headers will be found in.
|
||||
``ecm_find_package_components()`` will then search for the library
|
||||
and include directory (creating appropriate cache variables) and create an
|
||||
imported library target named <name>::<component>.
|
||||
|
||||
Additional variables can be used to provide additional information:
|
||||
|
||||
If ``SKIP_PKG_CONFIG``, the <name>_<component>_pkg_config variable is set, and
|
||||
pkg-config is found, the pkg-config module given by
|
||||
<name>_<component>_pkg_config will be searched for and used to help locate the
|
||||
library and header file. It will also be used to set
|
||||
<name>_<component>_VERSION.
|
||||
|
||||
Note that if version information is found via pkg-config,
|
||||
<name>_<component>_FIND_VERSION can be set to require a particular version
|
||||
for each component.
|
||||
|
||||
If ``SKIP_DEPENDENCY_HANDLING`` is not set, the ``INTERFACE_LINK_LIBRARIES`` property
|
||||
of the imported target for <component> will be set to contain the imported
|
||||
targets for the components listed in <name>_<component>_component_deps.
|
||||
<component>_FOUND will also be set to ``FALSE`` if any of the components in
|
||||
<name>_<component>_component_deps are not found. This requires the components
|
||||
in <name>_<component>_component_deps to be listed before <component> in the
|
||||
``COMPONENTS`` argument.
|
||||
|
||||
The following variables will be set:
|
||||
|
||||
``<name>_TARGETS``
|
||||
the imported targets
|
||||
``<name>_LIBRARIES``
|
||||
the found libraries
|
||||
``<name>_INCLUDE_DIRS``
|
||||
the combined required include directories for the components
|
||||
``<name>_DEFINITIONS``
|
||||
the "other" CFLAGS provided by pkg-config, if any
|
||||
``<name>_VERSION``
|
||||
the value of ``<name>_<component>_VERSION`` for the first component that
|
||||
has this variable set (note that components are searched for in the order
|
||||
they are passed to the macro), although if it is already set, it will not
|
||||
be altered
|
||||
|
||||
.. note::
|
||||
These variables are never cleared, so if
|
||||
``ecm_find_package_handle_library_components()`` is called multiple times with
|
||||
different components (typically because of multiple ``find_package()`` calls) then
|
||||
``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||
call (although no duplicates).
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
macro(ecm_find_package_version_check module_name)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.16.0)
|
||||
message(FATAL_ERROR "CMake 3.16.0 is required by Find${module_name}.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use Find${module_name}.cmake")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_parse_components module_name)
|
||||
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fppc_oneValueArgs RESULT_VAR)
|
||||
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPPC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_RESULT_VAR)
|
||||
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
|
||||
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if(${module_name}_FIND_COMPONENTS)
|
||||
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
|
||||
|
||||
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
|
||||
# Make sure deps are included
|
||||
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
|
||||
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
|
||||
if("${ecm_fppc_index}" STREQUAL "-1")
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
|
||||
endif()
|
||||
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Skipping dependency handling for ${module_name}")
|
||||
endif()
|
||||
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
|
||||
|
||||
# This makes sure components are listed in the same order as
|
||||
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
|
||||
set(${ECM_FPPC_RESULT_VAR})
|
||||
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
|
||||
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
|
||||
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
|
||||
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
|
||||
endif()
|
||||
endforeach()
|
||||
# if there are any left, they are unknown components
|
||||
if(ecm_fppc_requestedComps)
|
||||
set(ecm_fppc_msgType STATUS)
|
||||
if(${module_name}_FIND_REQUIRED)
|
||||
set(ecm_fppc_msgType FATAL_ERROR)
|
||||
endif()
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_handle_library_components module_name)
|
||||
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fpwc_oneValueArgs)
|
||||
set(ecm_fpwc_multiValueArgs COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPWC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPWC_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package(PkgConfig QUIET)
|
||||
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
|
||||
set(ecm_fpwc_dep_vars)
|
||||
set(ecm_fpwc_dep_targets)
|
||||
if(NOT SKIP_DEPENDENCY_HANDLING)
|
||||
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
|
||||
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
|
||||
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
|
||||
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
|
||||
${${module_name}_${ecm_fpwc_comp}_pkg_config})
|
||||
endif()
|
||||
|
||||
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
|
||||
)
|
||||
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
|
||||
if(NOT ${module_name}_VERSION)
|
||||
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
|
||||
endif()
|
||||
|
||||
set(FPHSA_NAME_MISMATCHED 1)
|
||||
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
|
||||
FOUND_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_FOUND
|
||||
REQUIRED_VARS
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
${ecm_fpwc_dep_vars}
|
||||
VERSION_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_VERSION
|
||||
)
|
||||
unset(FPHSA_NAME_MISMATCHED)
|
||||
|
||||
mark_as_advanced(
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(${module_name}_${ecm_fpwc_comp}_FOUND)
|
||||
list(APPEND ${module_name}_LIBRARIES
|
||||
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
|
||||
list(APPEND ${module_name}_INCLUDE_DIRS
|
||||
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
|
||||
set(${module_name}_DEFINITIONS
|
||||
${${module_name}_DEFINITIONS}
|
||||
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
|
||||
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
|
||||
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
|
||||
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
|
||||
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${module_name}_TARGETS
|
||||
"${module_name}::${ecm_fpwc_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(${module_name}_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
|
||||
endif()
|
||||
if(${module_name}_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
|
||||
endif()
|
||||
if(${module_name}_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
|
||||
endif()
|
||||
if(${module_name}_TARGETS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -0,0 +1,64 @@
|
||||
# SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
# SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMFindQmlModule
|
||||
----------------
|
||||
|
||||
Find QML import modules through a ``find_qmlmodule()`` call.
|
||||
It looks for the qmldir and uses the qmlplugindump if needed application to find the plugins and sets them up as
|
||||
runtime dependencies.
|
||||
This is useful so that when we configure a project we are notified when some
|
||||
QML imports are not present in the system.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_qmlmodule(<module_name>
|
||||
<version> # Optional for Qt6 builds
|
||||
[REQUIRED] # Since 6.0
|
||||
)
|
||||
|
||||
Usage example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_find_qmlmodule(org.kde.kirigami 2.1)
|
||||
ecm_find_qmlmodule(org.kde.kirigami 2.1 REQUIRED) # CMake will fail if the required version is not found
|
||||
ecm_find_qmlmodule(org.kde.kirigami) # Find it without a given version
|
||||
ecm_find_qmlmodule(org.kde.kirigami REQUIRED) # CMake will fail if it is not found
|
||||
|
||||
Since 5.38.0.
|
||||
#]=======================================================================]
|
||||
|
||||
set(MODULES_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
function(ecm_find_qmlmodule MODULE_NAME)
|
||||
if (QT_MAJOR_VERSION STREQUAL 6)
|
||||
cmake_parse_arguments(ARG REQUIRED "" "" ${ARGN})
|
||||
if (ARG_UNPARSED_ARGUMENTS)
|
||||
list(GET ARG_UNPARSED_ARGUMENTS 0 VERSION) # If we have any unparsed args, that should be the version
|
||||
endif()
|
||||
set(ARGN "") # The find_package call below should not recieve arguments in KF6
|
||||
else()
|
||||
list(GET ARGN 0 VERSION)
|
||||
list(REMOVE_AT ARGN 0)
|
||||
endif()
|
||||
|
||||
set(GENMODULE "${MODULE_NAME}-QMLModule")
|
||||
configure_file("${MODULES_DIR}/ECMFindQmlModule.cmake.in" "Find${GENMODULE}.cmake" @ONLY)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_BINARY_DIR}" ${CMAKE_MODULE_PATH})
|
||||
find_package(${GENMODULE} ${ARGN})
|
||||
|
||||
if(COMMAND set_package_properties)
|
||||
if (ARG_REQUIRED)
|
||||
set(TYPE_STRING TYPE REQUIRED)
|
||||
else()
|
||||
set(TYPE_STRING TYPE RUNTIME)
|
||||
endif()
|
||||
set_package_properties(${GENMODULE} PROPERTIES
|
||||
DESCRIPTION "QML module '${MODULE_NAME}' is a runtime dependency."
|
||||
${TYPE_STRING})
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,78 @@
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
# SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#=============================================================================
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
include("${ECM_MODULE_DIR}/QtVersionOption.cmake")
|
||||
include("${ECM_MODULE_DIR}/ECMQueryQt.cmake")
|
||||
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
include(${ECM_MODULE_DIR}/ECMQmlModule.cmake)
|
||||
# Get the qmldir file
|
||||
_ecm_qmlmodule_uri_to_path(MODULEDIR "@MODULE_NAME@" "@VERSION@")
|
||||
set(KDE_QMLDIR "${KDE_INSTALL_FULL_QMLDIR}/${MODULEDIR}")
|
||||
find_file(QMLDIR_FILE qmldir ${KDE_QMLDIR} NO_CACHE)
|
||||
if (NOT QMLDIR_FILE) # Check the install destination, the QT_PLUGIN_PATH might not be set up correctly at this point
|
||||
# Check the Qt installation
|
||||
ecm_query_qt(qt_qml_dir QT_INSTALL_QML)
|
||||
set(QMLDIR_FILE "${qt_qml_dir}/${MODULEDIR}/qmldir")
|
||||
if (NOT EXISTS "${QMLDIR_FILE}")
|
||||
message(WARNING "qmldir not found in ${KDE_QMLDIR} or ${QMLDIR_FILE}")
|
||||
set(MODULE_NOTFOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT MODULE_NOTFOUND AND NOT "@VERSION@" STREQUAL "") # Check if we even need to check the version
|
||||
find_file(VERSION_FILE kde-qmlmodule.version ${KDE_QMLDIR} NO_CACHE)
|
||||
if (VERSION_FILE)
|
||||
file(READ "${VERSION_FILE}" FILE_CONTENTS)
|
||||
if ("${FILE_CONTENTS}" MATCHES "([0-9]+(\\.[0-9]+)*)")
|
||||
if ("${CMAKE_MATCH_1}" VERSION_GREATER_EQUAL "@VERSION@")
|
||||
set(@GENMODULE@_FOUND TRUE)
|
||||
endif()
|
||||
set(@GENMODULE@_VERSION "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
else()
|
||||
file(READ "${QMLDIR_FILE}" FILE_CONTENTS)
|
||||
if ("${FILE_CONTENTS}" MATCHES "# KDE-qmldir-Version: ([0-9]+(\\.[0-9]+)*)")
|
||||
if ("${CMAKE_MATCH_1}" VERSION_GREATER_EQUAL "@VERSION@")
|
||||
set(@GENMODULE@_FOUND TRUE)
|
||||
endif()
|
||||
set(@GENMODULE@_VERSION "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
elseif(NOT MODULE_NOTFOUND) # if we don't have a specific version and the qmldir file was found, we are all set
|
||||
set(@GENMODULE@_FOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If we haven't checked the version above, use qmlplugindump
|
||||
if (NOT CMAKE_CROSSCOMPILING AND NOT MODULE_NOTFOUND AND NOT @GENMODULE@_FOUND AND (NOT DEFINED QT6_IS_SHARED_LIBS_BUILD OR QT6_IS_SHARED_LIBS_BUILD))
|
||||
if (QT_MAJOR_VERSION EQUAL "6")
|
||||
find_package(Qt6 COMPONENTS QmlTools REQUIRED)
|
||||
get_target_property(QMLPLUGINDUMP_PROGRAM Qt6::qmlplugindump LOCATION)
|
||||
else()
|
||||
ecm_query_qt(qt_binaries_dir QT_HOST_BINS)
|
||||
find_program(QMLPLUGINDUMP_PROGRAM NAMES qmlplugindump HINTS ${qt_binaries_dir})
|
||||
endif()
|
||||
|
||||
set(ENV{QML2_IMPORT_PATH} ${KDE_INSTALL_FULL_QMLDIR})
|
||||
execute_process(COMMAND "${QMLPLUGINDUMP_PROGRAM}" "@MODULE_NAME@" "@VERSION@" ERROR_VARIABLE ERRORS_OUTPUT OUTPUT_VARIABLE DISREGARD_VARIABLE RESULT_VARIABLE ExitCode TIMEOUT 30)
|
||||
if(ExitCode EQUAL 0)
|
||||
set(@GENMODULE@_FOUND TRUE)
|
||||
else()
|
||||
message(STATUS "qmlplugindump failed for @MODULE_NAME@.")
|
||||
set(@GENMODULE@_FOUND FALSE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(@GENMODULE@_FOUND ${@GENMODULE@_FOUND} PARENT_SCOPE)
|
||||
set(@GENMODULE@_VERSION ${@GENMODULE@_VERSION} PARENT_SCOPE)
|
||||
find_package_handle_standard_args(@GENMODULE@
|
||||
VERSION_VAR @GENMODULE@_VERSION
|
||||
REQUIRED_VARS @GENMODULE@_FOUND
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
# SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
|
||||
# SPDX-FileCopyrightText: 2020 Henri Chain <henri.chain@enioka.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGenerateDBusServiceFile
|
||||
---------------------------
|
||||
|
||||
This module provides the ``ecm_generate_dbus_service_file`` function for
|
||||
generating and installing a D-Bus service file.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_dbus_service_file(
|
||||
NAME <service name>
|
||||
EXECUTABLE <executable>
|
||||
[SYSTEMD_SERVICE <systemd service>]
|
||||
DESTINATION <install_path>
|
||||
[RENAME <dbus service filename>] # Since 5.75
|
||||
)
|
||||
|
||||
A D-Bus service file ``<service name>.service`` will be generated and installed
|
||||
in the relevant D-Bus config location. This filename can be customized with RENAME.
|
||||
|
||||
``<executable>`` must be an absolute path to the installed service executable. When using it with
|
||||
:kde-module:`KDEInstallDirs` it needs to be the ``_FULL_`` variant of the path variable.
|
||||
|
||||
.. note::
|
||||
On Windows, the macro will only use the file name part of ``<executable>`` since D-Bus
|
||||
service executables are to be installed in the same directory as the D-Bus daemon.
|
||||
|
||||
Optionally, a ``<systemd service>`` can be specified to launch the corresponding
|
||||
systemd service instead of the ``<executable>`` if the D-Bus daemon is started by systemd.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_dbus_service_file(
|
||||
NAME org.kde.kded5
|
||||
EXECUTABLE ${KDE_INSTALL_FULL_BINDIR}/kded5
|
||||
DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR}
|
||||
)
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_dbus_service_file(
|
||||
NAME org.kde.kded5
|
||||
EXECUTABLE ${KDE_INSTALL_FULL_BINDIR}/kded5
|
||||
SYSTEMD_SERVICE plasma-kded.service
|
||||
DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR}
|
||||
RENAME org.kde.daemon.service
|
||||
)
|
||||
|
||||
Since 5.73.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ecm_generate_dbus_service_file)
|
||||
set(options)
|
||||
set(oneValueArgs EXECUTABLE NAME SYSTEMD_SERVICE DESTINATION RENAME)
|
||||
set(multiValueArgs)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_generate_dbus_service_file: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ARG_NAME)
|
||||
message(FATAL_ERROR "Missing NAME argument for ecm_generate_dbus_service_file")
|
||||
endif()
|
||||
if(NOT ARG_EXECUTABLE)
|
||||
message(FATAL_ERROR "Missing EXECUTABLE argument for ecm_generate_dbus_service_file")
|
||||
endif()
|
||||
if(NOT ARG_DESTINATION)
|
||||
message(FATAL_ERROR "Missing DESTINATION argument for ecm_generate_dbus_service_file")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
get_filename_component(_exec "${ARG_EXECUTABLE}" NAME)
|
||||
else()
|
||||
if (NOT IS_ABSOLUTE ${ARG_EXECUTABLE})
|
||||
message(FATAL_ERROR "EXECUTABLE must be an absolute path in ecm_generate_dbus_service_file")
|
||||
else()
|
||||
set(_exec ${ARG_EXECUTABLE})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(_service_file ${CMAKE_CURRENT_BINARY_DIR}/${ARG_NAME}.service)
|
||||
|
||||
file(WRITE ${_service_file}
|
||||
"[D-BUS Service]
|
||||
Name=${ARG_NAME}
|
||||
Exec=${_exec}
|
||||
")
|
||||
|
||||
if (ARG_SYSTEMD_SERVICE)
|
||||
file(APPEND ${_service_file} "SystemdService=${ARG_SYSTEMD_SERVICE}\n")
|
||||
endif()
|
||||
|
||||
if (ARG_RENAME)
|
||||
install(FILES ${_service_file} DESTINATION ${ARG_DESTINATION} RENAME ${ARG_RENAME})
|
||||
else()
|
||||
install(FILES ${_service_file} DESTINATION ${ARG_DESTINATION})
|
||||
endif()
|
||||
endfunction()
|
||||
+838
@@ -0,0 +1,838 @@
|
||||
# SPDX-FileCopyrightText: 2019 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGenerateExportHeader
|
||||
-----------------------
|
||||
|
||||
This module provides the ``ecm_generate_export_header`` function for
|
||||
generating export macros for libraries with version-based control over
|
||||
visibility of and compiler warnings for deprecated API for the library user,
|
||||
as well as over excluding deprecated API and their implementation when
|
||||
building the library itself.
|
||||
|
||||
For preparing some values useful in the context it also provides a function
|
||||
``ecm_export_header_format_version``.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_export_header(<library_target_name>
|
||||
VERSION <version>
|
||||
[BASE_NAME <base_name>]
|
||||
[GROUP_BASE_NAME <group_base_name>]
|
||||
[EXPORT_MACRO_NAME <export_macro_name>]
|
||||
[EXPORT_FILE_NAME <export_file_name>]
|
||||
[DEPRECATED_MACRO_NAME <deprecated_macro_name>]
|
||||
[NO_EXPORT_MACRO_NAME <no_export_macro_name>]
|
||||
[INCLUDE_GUARD_NAME <include_guard_name>]
|
||||
[STATIC_DEFINE <static_define>]
|
||||
[PREFIX_NAME <prefix_name>]
|
||||
[DEPRECATED_BASE_VERSION <deprecated_base_version>]
|
||||
[DEPRECATION_VERSIONS <deprecation_version> [<deprecation_version2> [...]]]
|
||||
[EXCLUDE_DEPRECATED_BEFORE_AND_AT <exclude_deprecated_before_and_at_version>]
|
||||
[NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE]
|
||||
[NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE]
|
||||
[USE_VERSION_HEADER [<version_file_name>]] # Since 5.106
|
||||
[VERSION_BASE_NAME <version_base_name>] # Since 5.106
|
||||
[VERSION_MACRO_NAME <version_macro_name>] # Since 5.106
|
||||
[CUSTOM_CONTENT_FROM_VARIABLE <variable>]
|
||||
)
|
||||
|
||||
``VERSION`` specifies the version of the library, given in the format
|
||||
"<major>.<minor>.<patchlevel>".
|
||||
|
||||
``GROUP_BASE_NAME`` specifies the name to use for the macros defining
|
||||
library group default values. If set, this will generate code supporting
|
||||
``<group_base_name>_NO_DEPRECATED_WARNINGS``,
|
||||
``<group_base_name>_DISABLE_DEPRECATED_BEFORE_AND_AT``,
|
||||
``<group_base_name>_DEPRECATED_WARNINGS_SINCE`` and
|
||||
``<group_base_name>_NO_DEPRECATED`` (see below).
|
||||
If not set, the generated code will ignore any such macros.
|
||||
|
||||
``DEPRECATED_BASE_VERSION`` specifies the default version before and at which
|
||||
deprecated API is disabled. Possible values are "0", "CURRENT" (which
|
||||
resolves to <version>) and a version string in the format
|
||||
"<major>.<minor>.<patchlevel>". The default is the value of
|
||||
"<exclude_deprecated_before_and_at_version>" if set, or "<major>.0.0", with
|
||||
<major> taken from <version>.
|
||||
|
||||
``DEPRECATION_VERSIONS`` specifies versions in "<major>.<minor>" format in
|
||||
which API was declared deprecated. Any version used with the generated
|
||||
macro ``<prefix_name><base_name>_DEPRECATED_VERSION(major, minor, text)``
|
||||
or ``<prefix_name><base_name>_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)``
|
||||
needs to be listed here, otherwise the macro will fail to work.
|
||||
|
||||
``EXCLUDE_DEPRECATED_BEFORE_AND_AT`` specifies the version for which all API
|
||||
deprecated before and at should be excluded from the build completely.
|
||||
Possible values are "0" (default), "CURRENT" (which resolves to <version>)
|
||||
and a version string in the format "<major>.<minor>.<patchlevel>".
|
||||
|
||||
``NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE`` specifies that the definition
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCE`` will
|
||||
not be set for the library inside its own build, and thus will be defined
|
||||
by either explicit definition in the build system configuration or by the
|
||||
default value mechanism (see below).
|
||||
The default is that it is set for the build, to the version specified by
|
||||
``EXCLUDE_DEPRECATED_BEFORE_AND_AT``, so no deprecation warnings are
|
||||
done for any own deprecated API used in the library implementation itself.
|
||||
|
||||
``NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE`` specifies that the definition
|
||||
``<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_AT`` will
|
||||
not be set in the public interface of the library inside its own build, and
|
||||
the same for the definition
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCE`` (if not
|
||||
disabled by ``NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE`` already).
|
||||
The default is that they are set, to the version specified by
|
||||
``EXCLUDE_DEPRECATED_BEFORE_AND_AT``, so e.g. test and examples part of the
|
||||
project automatically build against the full API included in the build and
|
||||
without any deprecation warnings for it.
|
||||
|
||||
``USE_VERSION_HEADER`` defines whether a given header file
|
||||
``<version_file_name>`` providing macros specifying the library version should
|
||||
be included in the generated header file. By default angle-brackets are used
|
||||
for the include statement. To generate includes with double quotes, add
|
||||
double quotes to the argument string (needs escaping), e.g. ``\"version.h\"``.
|
||||
The macro from the included version header holding the library version is
|
||||
given as ``<version_macro_name>`` by the argument ``VERSION_MACRO_NAME`` and
|
||||
used in the generated code for calculating defaults. If not specified, the
|
||||
defaults for the version file name and the version macro are derived from
|
||||
``<version_base_name>`` as passed with ``VERSION_BASE_NAME``, which again
|
||||
defaults to ``<base_name>`` or otherwise ``<library_target_name>``.
|
||||
The macro name defaults to ``<uppercase_version_base_name>_VERSION``,
|
||||
the version file name to ``<lowercase_version_base_name>_version.h``.
|
||||
Since 5.106.
|
||||
|
||||
``CUSTOM_CONTENT_FROM_VARIABLE`` specifies the name of a variable whose
|
||||
content will be appended at the end of the generated file, before any
|
||||
final inclusion guard closing. Note that before 5.98 this was broken and
|
||||
would only append the string passed as argument value.
|
||||
|
||||
The function ``ecm_generate_export_header`` defines C++ preprocessor macros
|
||||
in the generated export header, some for use in the sources of the library
|
||||
the header is generated for, other for use by projects linking agsinst the
|
||||
library.
|
||||
|
||||
The macros for use in the library C++ sources are these, next to those also
|
||||
defined by `GenerateExportHeader
|
||||
<https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html>`_:
|
||||
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED_VERSION(major, minor, text)``
|
||||
to use to conditionally set a
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED`` macro for a class, struct
|
||||
or function (other elements to be supported in future versions), depending
|
||||
on the visibility macro flags set (see below)
|
||||
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)``
|
||||
to use to conditionally set a
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED`` macro for a class, struct
|
||||
or function (other elements to be supported in future versions), depending
|
||||
on the visibility macro flags set (see below), with ``major`` & ``minor``
|
||||
applied for the logic and ``textmajor`` & ``textminor`` for the warnings message.
|
||||
Useful for retroactive tagging of API for the compiler without injecting the
|
||||
API into the compiler warning conditions of already released versions.
|
||||
Since 5.71.
|
||||
|
||||
``<prefix_name><uppercase_base_name>_ENUMERATOR_DEPRECATED_VERSION(major, minor, text)``
|
||||
to use to conditionally set a
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED`` macro for an enumerator, depending
|
||||
on the warnings macro flags set (see below). In builds using C++14 standard or earlier,
|
||||
where enumerator attributes are not yet supported, the macro will always yield an empty string.
|
||||
With MSVC it is also always an empty string for now.
|
||||
Since 5.82.
|
||||
|
||||
``<prefix_name><uppercase_base_name>_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)``
|
||||
to use to conditionally set a
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED`` macro for an enumerator, depending
|
||||
on the warnings macro flags set (see below), with ``major`` & ``minor``
|
||||
applied for the logic and ``textmajor`` & ``textminor`` for the warnings message.
|
||||
In builds using C++14 standard or earlier, where enumerator attributes are not yet supported,
|
||||
the macro will always yield an empty string.
|
||||
Useful for retroactive tagging of API for the compiler without injecting the
|
||||
API into the compiler warning conditions of already released versions.
|
||||
With MSVC it is also always an empty string for now.
|
||||
Since 5.82.
|
||||
|
||||
``<prefix_name><uppercase_base_name>_ENABLE_DEPRECATED_SINCE(major, minor)``
|
||||
evaluates to ``TRUE`` or ``FALSE`` depending on the visibility macro flags
|
||||
set (see below). To be used mainly with ``#if``/``#endif`` to mark sections
|
||||
of code which should be included depending on the visibility requested.
|
||||
|
||||
``<prefix_name><uppercase_base_name>_BUILD_DEPRECATED_SINCE(major, minor)``
|
||||
evaluates to ``TRUE`` or ``FALSE`` depending on the value of
|
||||
``EXCLUDE_DEPRECATED_BEFORE_AND_AT``. To be used mainly with
|
||||
``#if``/``#endif`` to mark sections of two types of code: implementation
|
||||
code for deprecated API and declaration code of deprecated API which only
|
||||
may be disabled at build time of the library for BC reasons (e.g. virtual
|
||||
methods, see notes below).
|
||||
|
||||
``<prefix_name><uppercase_base_name>_EXCLUDE_DEPRECATED_BEFORE_AND_AT``
|
||||
holds the version used to exclude deprecated API at build time of the
|
||||
library.
|
||||
|
||||
The macros used to control visibility when building against the library are:
|
||||
|
||||
``<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_AT``
|
||||
definition to set to a value in single hex number version notation
|
||||
(``0x<major><minor><patchlevel>``).
|
||||
|
||||
``<prefix_name><uppercase_base_name>_NO_DEPRECATED``
|
||||
flag to define to disable all deprecated API, being a shortcut for
|
||||
settings ``<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_AT``
|
||||
to the current version. If both are set, this flag overrules.
|
||||
|
||||
``<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCE``
|
||||
definition to set to a value in single hex number version notation
|
||||
(``0x<major><minor><patchlevel>``). Warnings will be only activated for
|
||||
API deprecated up to and including the version. If
|
||||
``<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_AT``
|
||||
is set (directly or via the group default), it will default to that
|
||||
version, resulting in no warnings. Otherwise the default is the current
|
||||
version, resulting in warnings for all deprecated API.
|
||||
|
||||
``<prefix_name><uppercase_base_name>_NO_DEPRECATED_WARNINGS``
|
||||
flag to define to disable all deprecation warnings, being a shortcut for
|
||||
setting ``<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCE``
|
||||
to "0". If both are set, this flag overrules.
|
||||
|
||||
When the ``GROUP_BASE_NAME`` has been used, the same macros but with the
|
||||
given ``<group_base_name>`` prefix are available to define the defaults of
|
||||
these macros, if not explicitly set.
|
||||
|
||||
.. warning::
|
||||
The tricks applied here for hiding deprecated API to the compiler
|
||||
when building against a library do not work for all deprecated API:
|
||||
|
||||
* virtual methods need to stay visible to the compiler to build proper
|
||||
virtual method tables for subclasses
|
||||
* enumerators from enums cannot be simply removed, as this changes
|
||||
auto values of following enumerators, also can poke holes in enumerator
|
||||
series used as index into tables
|
||||
|
||||
In such cases the API can be only "hidden" at build time of the library,
|
||||
itself, by generated hard coded macro settings, using
|
||||
``<prefix_name><uppercase_base_name>_BUILD_DEPRECATED_SINCE(major, minor)``.
|
||||
|
||||
Examples:
|
||||
|
||||
Preparing a library "Foo" created by target "foo", which is part of a group
|
||||
of libraries "Bar", where some API of "Foo" got deprecated at versions
|
||||
5.0 & 5.12:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_export_header(foo
|
||||
GROUP_BASE_NAME BAR
|
||||
VERSION ${FOO_VERSION}
|
||||
DEPRECATION_VERSIONS 5.0 5.12
|
||||
)
|
||||
|
||||
In the library "Foo" sources in the headers the API would be prepared like
|
||||
this, using the generated macros ``FOO_ENABLE_DEPRECATED_SINCE`` and
|
||||
``FOO_DEPRECATED_VERSION``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <foo_export.h>
|
||||
|
||||
#if FOO_ENABLE_DEPRECATED_SINCE(5, 0)
|
||||
/**
|
||||
* @deprecated Since 5.0
|
||||
*/
|
||||
FOO_EXPORT
|
||||
FOO_DEPRECATED_VERSION(5, 0, "Use doFoo2()")
|
||||
void doFoo();
|
||||
#endif
|
||||
|
||||
#if FOO_ENABLE_DEPRECATED_SINCE(5, 12)
|
||||
/**
|
||||
* @deprecated Since 5.12
|
||||
*/
|
||||
FOO_EXPORT
|
||||
FOO_DEPRECATED_VERSION(5, 12, "Use doBar2()")
|
||||
void doBar();
|
||||
#endif
|
||||
|
||||
Projects linking against the "Foo" library can control which part of its
|
||||
deprecated API should be hidden to the compiler by adding a definition
|
||||
using the ``FOO_DISABLE_DEPRECATED_BEFORE_AND_AT`` macro variable set to the
|
||||
desired value (in version hex number notation):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_definitions(-DFOO_DISABLE_DEPRECATED_BEFORE_AND_AT=0x050000)
|
||||
|
||||
Or using the macro variable of the group:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_definitions(-DBAR_DISABLE_DEPRECATED_BEFORE_AND_AT=0x050000)
|
||||
|
||||
If both are specified, ``FOO_DISABLE_DEPRECATED_BEFORE_AND_AT`` will take
|
||||
precedence.
|
||||
|
||||
To build a variant of a library with some deprecated API completely left
|
||||
out from the build, not only optionally invisible to consumers, one uses the
|
||||
``EXCLUDE_DEPRECATED_BEFORE_AND_AT`` parameter. This is best combined with a
|
||||
cached CMake variable.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||
|
||||
ecm_generate_export_header(foo
|
||||
VERSION ${FOO_VERSION}
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
DEPRECATION_VERSIONS 5.0 5.12
|
||||
)
|
||||
|
||||
The macros used in the headers for library consumers are reused for
|
||||
disabling the API excluded in the build of the library. For disabling the
|
||||
implementation of that API as well as for disabling deprecated API which
|
||||
only can be disabled at build time of the library for BC reasons, one
|
||||
uses the generated macro ``FOO_BUILD_DEPRECATED_SINCE``, like this:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <foo_export.h>
|
||||
|
||||
enum Bars {
|
||||
One,
|
||||
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
|
||||
Two FOO_ENUMERATOR_DEPRECATED_VERSION(5, 0, "Use Three"), // macro available since 5.82
|
||||
#endif
|
||||
Three,
|
||||
};
|
||||
|
||||
#if FOO_ENABLE_DEPRECATED_SINCE(5, 0)
|
||||
/**
|
||||
* @deprecated Since 5.0
|
||||
*/
|
||||
FOO_EXPORT
|
||||
FOO_DEPRECATED_VERSION(5, 0, "Use doFoo2()")
|
||||
void doFoo();
|
||||
#endif
|
||||
|
||||
#if FOO_ENABLE_DEPRECATED_SINCE(5, 12)
|
||||
/**
|
||||
* @deprecated Since 5.12
|
||||
*/
|
||||
FOO_EXPORT
|
||||
FOO_DEPRECATED_VERSION(5, 12, "Use doBar2()")
|
||||
void doBar();
|
||||
#endif
|
||||
|
||||
class FOO_EXPORT Foo {
|
||||
public:
|
||||
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
|
||||
/**
|
||||
* @deprecated Since 5.0
|
||||
*/
|
||||
FOO_DEPRECATED_VERSION(5, 0, "Feature removed")
|
||||
virtual void doWhat();
|
||||
#endif
|
||||
};
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
|
||||
void doFoo()
|
||||
{
|
||||
// [...]
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FOO_BUILD_DEPRECATED_SINCE(5, 12)
|
||||
void doBar()
|
||||
{
|
||||
// [...]
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
|
||||
void Foo::doWhat()
|
||||
{
|
||||
// [...]
|
||||
}
|
||||
#endif
|
||||
|
||||
So e.g. if ``EXCLUDE_DEPRECATED_BEFORE_AND_AT`` is set to "5.0.0", the
|
||||
enumerator ``Two`` as well as the methods ``::doFoo()`` and ``Foo::doWhat()``
|
||||
will be not available to library consumers. The methods will not have been
|
||||
compiled into the library binary, and the declarations will be hidden to the
|
||||
compiler, ``FOO_DISABLE_DEPRECATED_BEFORE_AND_AT`` also cannot be used to
|
||||
reactivate them.
|
||||
|
||||
When using the ``NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE`` and the project
|
||||
for the "Foo" library includes also tests and examples linking against the
|
||||
library and using deprecated API (like tests covering it), one better
|
||||
explicitly sets ``FOO_DISABLE_DEPRECATED_BEFORE_AND_AT`` for those targets
|
||||
to the version before and at which all deprecated API has been excluded from
|
||||
the build.
|
||||
Even more when building against other libraries from the same group "Bar" and
|
||||
disabling some deprecated API of those libraries using the group macro
|
||||
``BAR_DISABLE_DEPRECATED_BEFORE_AND_AT``, which also works as default for
|
||||
``FOO_DISABLE_DEPRECATED_BEFORE_AND_AT``.
|
||||
|
||||
To get the hex number style value the helper macro
|
||||
``ecm_export_header_format_version()`` will be used:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control what part of deprecated API is excluded from build [default=0].")
|
||||
|
||||
ecm_generate_export_header(foo
|
||||
VERSION ${FOO_VERSION}
|
||||
GROUP_BASE_NAME BAR
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE
|
||||
DEPRECATION_VERSIONS 5.0 5.12
|
||||
)
|
||||
|
||||
ecm_export_header_format_version(${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
CURRENT_VERSION ${FOO_VERSION}
|
||||
HEXNUMBER_VAR foo_no_deprecated_before_and_at
|
||||
)
|
||||
|
||||
# disable all deprecated API up to 5.9.0 from all other libs of group "BAR" that we use ourselves
|
||||
add_definitions(-DBAR_DISABLE_DEPRECATED_BEFORE_AND_AT=0x050900)
|
||||
|
||||
add_executable(app app.cpp)
|
||||
target_link_libraries(app foo)
|
||||
target_compile_definitions(app
|
||||
PRIVATE "FOO_DISABLE_DEPRECATED_BEFORE_AND_AT=${foo_no_deprecated_before_and_at}")
|
||||
|
||||
Since 5.64.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(GenerateExportHeader)
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(SET CMP0057 NEW) # if IN_LIST
|
||||
|
||||
# helper method
|
||||
function(_ecm_geh_generate_hex_number _var_name _version)
|
||||
set(_hexnumber 0)
|
||||
|
||||
set(version_regex "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
|
||||
string(REGEX REPLACE ${version_regex} "\\1" _version_major "${_version}")
|
||||
string(REGEX REPLACE ${version_regex} "\\2" _version_minor "${_version}")
|
||||
string(REGEX REPLACE ${version_regex} "\\3" _version_patch "${_version}")
|
||||
set(_outputformat)
|
||||
set(_outputformat OUTPUT_FORMAT HEXADECIMAL)
|
||||
math(EXPR _hexnumber "${_version_major}*65536 + ${_version_minor}*256 + ${_version_patch}" ${_outputformat})
|
||||
set(${_var_name} ${_hexnumber} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(ecm_export_header_format_version _version)
|
||||
set(options
|
||||
)
|
||||
set(oneValueArgs
|
||||
CURRENT_VERSION
|
||||
STRING_VAR
|
||||
HEXNUMBER_VAR
|
||||
)
|
||||
set(multiValueArgs
|
||||
)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if (NOT ARGS_STRING_VAR AND NOT ARGS_HEXNUMBER_VAR)
|
||||
message(FATAL_ERROR "No STRING_VAR or HEXNUMBER_VAR passed when calling ecm_export_header_format_version().")
|
||||
endif()
|
||||
|
||||
if(_version STREQUAL "CURRENT")
|
||||
if (NOT ARGS_CURRENT_VERSION )
|
||||
message(FATAL_ERROR "\"CURRENT\" as version value not supported when CURRENT_VERSION not passed on calling ecm_export_header_format_version().")
|
||||
endif()
|
||||
set(_version ${ARGS_CURRENT_VERSION})
|
||||
endif()
|
||||
|
||||
if (ARGS_STRING_VAR)
|
||||
set(${ARGS_STRING_VAR} ${_version} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if (ARGS_HEXNUMBER_VAR)
|
||||
set(_hexnumber 0) # by default build all deprecated API
|
||||
if(_version)
|
||||
_ecm_geh_generate_hex_number(_hexnumber ${_version})
|
||||
endif()
|
||||
set(${ARGS_HEXNUMBER_VAR} ${_hexnumber} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_generate_export_header target)
|
||||
set(options
|
||||
NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE
|
||||
NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE
|
||||
)
|
||||
set(oneValueArgs
|
||||
BASE_NAME
|
||||
GROUP_BASE_NAME
|
||||
EXPORT_FILE_NAME
|
||||
DEPRECATED_BASE_VERSION
|
||||
VERSION
|
||||
VERSION_BASE_NAME
|
||||
VERSION_MACRO_NAME
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT
|
||||
EXPORT_MACRO_NAME
|
||||
DEPRECATED_MACRO_NAME
|
||||
NO_EXPORT_MACRO_NAME
|
||||
INCLUDE_GUARD_NAME
|
||||
STATIC_DEFINE
|
||||
PREFIX_NAME
|
||||
CUSTOM_CONTENT_FROM_VARIABLE
|
||||
)
|
||||
set(multiValueArgs
|
||||
DEPRECATION_VERSIONS
|
||||
USE_VERSION_HEADER
|
||||
)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# helper string
|
||||
set(_version_triple_regexp "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
|
||||
# args sanity check
|
||||
if (NOT ARGS_VERSION)
|
||||
message(FATAL_ERROR "No VERSION passed when calling ecm_generate_export_header().")
|
||||
elseif(NOT ARGS_VERSION MATCHES ${_version_triple_regexp})
|
||||
message(FATAL_ERROR "VERSION expected to be in x.y.z format when calling ecm_generate_export_header().")
|
||||
endif()
|
||||
if (NOT ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT)
|
||||
set(ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT 0)
|
||||
elseif(ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT STREQUAL "CURRENT")
|
||||
set(ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT ${ARGS_VERSION})
|
||||
elseif(NOT ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT MATCHES ${_version_triple_regexp} AND
|
||||
NOT ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT STREQUAL "0")
|
||||
message(FATAL_ERROR "EXCLUDE_DEPRECATED_BEFORE_AND_AT expected to be in \"x.y.z\" format, \"0\" or \"CURRENT\" when calling ecm_generate_export_header().")
|
||||
endif()
|
||||
if (NOT DEFINED ARGS_DEPRECATED_BASE_VERSION)
|
||||
if (ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT)
|
||||
set(ARGS_DEPRECATED_BASE_VERSION "${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT}")
|
||||
else()
|
||||
string(REGEX REPLACE ${_version_triple_regexp} "\\1" _version_major "${ARGS_VERSION}")
|
||||
set(ARGS_DEPRECATED_BASE_VERSION "${_version_major}.0.0")
|
||||
endif()
|
||||
else()
|
||||
if(ARGS_DEPRECATED_BASE_VERSION STREQUAL "CURRENT")
|
||||
set(ARGS_DEPRECATED_BASE_VERSION ${ARGS_VERSION})
|
||||
elseif(NOT ARGS_DEPRECATED_BASE_VERSION MATCHES ${_version_triple_regexp} AND
|
||||
NOT ARGS_DEPRECATED_BASE_VERSION STREQUAL "0")
|
||||
message(FATAL_ERROR "DEPRECATED_BASE_VERSION expected to be in \"x.y.z\" format, \"0\" or \"CURRENT\" when calling ecm_generate_export_header().")
|
||||
endif()
|
||||
if (ARGS_DEPRECATED_BASE_VERSION VERSION_LESS ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT)
|
||||
message(STATUS "DEPRECATED_BASE_VERSION (${ARGS_DEPRECATED_BASE_VERSION}) was lower than EXCLUDE_DEPRECATED_BEFORE_AND_AT (${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT}) when calling ecm_generate_export_header(), raising to that.")
|
||||
set(ARGS_DEPRECATED_BASE_VERSION "${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT ARGS_BASE_NAME)
|
||||
set(ARGS_BASE_NAME "${target}")
|
||||
endif()
|
||||
if(NOT ARGS_VERSION_BASE_NAME)
|
||||
set(ARGS_VERSION_BASE_NAME "${ARGS_BASE_NAME}")
|
||||
endif()
|
||||
string(TOUPPER "${ARGS_BASE_NAME}" _upper_base_name)
|
||||
string(TOLOWER "${ARGS_BASE_NAME}" _lower_base_name)
|
||||
set(_version_header)
|
||||
if (USE_VERSION_HEADER IN_LIST ARGS_KEYWORDS_MISSING_VALUES)
|
||||
string(TOLOWER "${ARGS_VERSION_BASE_NAME}" _lower_version_base_name)
|
||||
set(_version_header "${_lower_version_base_name}_version.h")
|
||||
elseif (DEFINED ARGS_USE_VERSION_HEADER)
|
||||
list(LENGTH ARGS_USE_VERSION_HEADER _arg_count)
|
||||
if (_arg_count GREATER 1)
|
||||
message(FATAL_ERROR "USE_VERSION_HEADER only takes 1 or no arg when calling ecm_generate_export_header().")
|
||||
endif()
|
||||
set(_version_header ${ARGS_USE_VERSION_HEADER})
|
||||
endif()
|
||||
if(_version_header)
|
||||
if(ARGS_VERSION_MACRO_NAME)
|
||||
set(_version_hexnumber "${ARGS_VERSION_MACRO_NAME}")
|
||||
else()
|
||||
string(TOUPPER "${ARGS_VERSION_BASE_NAME}" _upper_version_base_name)
|
||||
set(_version_hexnumber "${_upper_version_base_name}_VERSION")
|
||||
endif()
|
||||
else()
|
||||
_ecm_geh_generate_hex_number(_version_hexnumber "${ARGS_VERSION}")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_EXPORT_FILE_NAME)
|
||||
set(ARGS_EXPORT_FILE_NAME "${_lower_base_name}_export.h")
|
||||
endif()
|
||||
if (ARGS_DEPRECATED_BASE_VERSION STREQUAL "0")
|
||||
set(_default_disabled_deprecated_version_hexnumber "0")
|
||||
else()
|
||||
_ecm_geh_generate_hex_number(_default_disabled_deprecated_version_hexnumber "${ARGS_DEPRECATED_BASE_VERSION}")
|
||||
endif()
|
||||
|
||||
set(_macro_base_name "${ARGS_PREFIX_NAME}${_upper_base_name}")
|
||||
if (ARGS_EXPORT_MACRO_NAME)
|
||||
set(_export_macro_name "${ARGS_EXPORT_MACRO_NAME}")
|
||||
else()
|
||||
set(_export_macro_name "${_macro_base_name}_EXPORT")
|
||||
endif()
|
||||
if (ARGS_NO_EXPORT_MACRO_NAME)
|
||||
set(_no_export_macro_name "${ARGS_NO_EXPORT_MACRO_NAME}")
|
||||
else()
|
||||
set(_no_export_macro_name "${_macro_base_name}_NO_EXPORT")
|
||||
endif()
|
||||
if (ARGS_DEPRECATED_MACRO_NAME)
|
||||
set(_deprecated_macro_name "${ARGS_DEPRECATED_MACRO_NAME}")
|
||||
else()
|
||||
set(_deprecated_macro_name "${_macro_base_name}_DEPRECATED")
|
||||
endif()
|
||||
|
||||
if(NOT IS_ABSOLUTE ${ARGS_EXPORT_FILE_NAME})
|
||||
set(ARGS_EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${ARGS_EXPORT_FILE_NAME}")
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${target} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "${ARGS_EXPORT_FILE_NAME}")
|
||||
# build with all the API not excluded, ensure all deprecated API is visible in the build itself
|
||||
_ecm_geh_generate_hex_number(_hexnumber ${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT})
|
||||
set(_disabling_visibility_definition "${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT=${_hexnumber}")
|
||||
target_compile_definitions(${target} PRIVATE "${_disabling_visibility_definition}")
|
||||
if (NOT ARGS_NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE)
|
||||
target_compile_definitions(${target} INTERFACE "$<BUILD_INTERFACE:${_disabling_visibility_definition}>")
|
||||
endif()
|
||||
if(NOT ARGS_NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE)
|
||||
set(_enabling_warnings_definition "${_macro_base_name}_DEPRECATED_WARNINGS_SINCE=${_hexnumber}")
|
||||
target_compile_definitions(${target} PRIVATE "${_enabling_warnings_definition}")
|
||||
if (NOT ARGS_NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE)
|
||||
target_compile_definitions(${target} INTERFACE "$<BUILD_INTERFACE:${_enabling_warnings_definition}>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# for the set of compiler versions supported by ECM/KF we can assume those attributes supported
|
||||
# KF6: with C++17 as minimum standard planned, switch to always use [[deprecated(text)]]
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set(_decl_deprecated_text_definition "__attribute__ ((__deprecated__(text)))")
|
||||
elseif(MSVC)
|
||||
set(_decl_deprecated_text_definition "__declspec(deprecated(text))")
|
||||
else()
|
||||
set(_decl_deprecated_text_definition "${_macro_base_name}_DECL_DEPRECATED")
|
||||
endif()
|
||||
# generate header file
|
||||
set(_output)
|
||||
if(_version_header)
|
||||
if (_version_header MATCHES "^\".+\"$")
|
||||
string(APPEND _output "#include ${_version_header}\n")
|
||||
else()
|
||||
string(APPEND _output "#include <${_version_header}>\n")
|
||||
endif()
|
||||
endif()
|
||||
string(APPEND _output "
|
||||
#define ${_macro_base_name}_DECL_DEPRECATED_TEXT(text) ${_decl_deprecated_text_definition}
|
||||
"
|
||||
)
|
||||
if (ARGS_GROUP_BASE_NAME)
|
||||
string(TOUPPER "${ARGS_GROUP_BASE_NAME}" _upper_group_name)
|
||||
string(APPEND _output "
|
||||
/* Take any defaults from group settings */
|
||||
#if !defined(${_macro_base_name}_NO_DEPRECATED) && !defined(${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT)
|
||||
# ifdef ${_upper_group_name}_NO_DEPRECATED
|
||||
# define ${_macro_base_name}_NO_DEPRECATED
|
||||
# elif defined(${_upper_group_name}_DISABLE_DEPRECATED_BEFORE_AND_AT)
|
||||
# define ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT ${_upper_group_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT) && defined(${_upper_group_name}_DISABLE_DEPRECATED_BEFORE_AND_AT)
|
||||
# define ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT ${_upper_group_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
#endif
|
||||
|
||||
#if !defined(${_macro_base_name}_NO_DEPRECATED_WARNINGS) && !defined(${_macro_base_name}_DEPRECATED_WARNINGS_SINCE)
|
||||
# ifdef ${_upper_group_name}_NO_DEPRECATED_WARNINGS
|
||||
# define ${_macro_base_name}_NO_DEPRECATED_WARNINGS
|
||||
# elif defined(${_upper_group_name}_DEPRECATED_WARNINGS_SINCE)
|
||||
# define ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE ${_upper_group_name}_DEPRECATED_WARNINGS_SINCE
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(${_macro_base_name}_DEPRECATED_WARNINGS_SINCE) && defined(${_upper_group_name}_DEPRECATED_WARNINGS_SINCE)
|
||||
# define ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE ${_upper_group_name}_DEPRECATED_WARNINGS_SINCE
|
||||
#endif
|
||||
"
|
||||
)
|
||||
endif()
|
||||
string(APPEND _output "
|
||||
#if defined(${_macro_base_name}_NO_DEPRECATED)
|
||||
# undef ${_deprecated_macro_name}
|
||||
# define ${_deprecated_macro_name}_EXPORT ${_export_macro_name}
|
||||
# define ${_deprecated_macro_name}_NO_EXPORT ${_no_export_macro_name}
|
||||
#elif defined(${_macro_base_name}_NO_DEPRECATED_WARNINGS)
|
||||
# define ${_deprecated_macro_name}
|
||||
# define ${_deprecated_macro_name}_EXPORT ${_export_macro_name}
|
||||
# define ${_deprecated_macro_name}_NO_EXPORT ${_no_export_macro_name}
|
||||
#else
|
||||
# define ${_deprecated_macro_name} ${_macro_base_name}_DECL_DEPRECATED
|
||||
# define ${_deprecated_macro_name}_EXPORT ${_macro_base_name}_DECL_DEPRECATED_EXPORT
|
||||
# define ${_deprecated_macro_name}_NO_EXPORT ${_macro_base_name}_DECL_DEPRECATED_NO_EXPORT
|
||||
#endif
|
||||
"
|
||||
)
|
||||
if (ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT)
|
||||
message(STATUS "Excluding from build all API deprecated before and at: ${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT}")
|
||||
# TODO: the generated code ideally would emit a warning if some consumer used a value
|
||||
# smaller then what the the build was done with
|
||||
_ecm_geh_generate_hex_number(_excluded_before_and_at_version_hexnumber "${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT}")
|
||||
string(APPEND _output "
|
||||
/* Build was done with the API removed deprecated before: ${ARGS_EXCLUDE_DEPRECATED_BEFORE_AND_AT} */
|
||||
#define ${_macro_base_name}_EXCLUDE_DEPRECATED_BEFORE_AND_AT ${_excluded_before_and_at_version_hexnumber}
|
||||
|
||||
#ifdef ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
# if ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT < ${_macro_base_name}_EXCLUDE_DEPRECATED_BEFORE_AND_AT
|
||||
# undef ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
# define ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT ${_macro_base_name}_EXCLUDE_DEPRECATED_BEFORE_AND_AT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define ${_macro_base_name}_BUILD_DEPRECATED_SINCE(major, minor) (((major<<16)|(minor<<8)) > ${_macro_base_name}_EXCLUDE_DEPRECATED_BEFORE_AND_AT)
|
||||
"
|
||||
)
|
||||
else()
|
||||
string(APPEND _output "
|
||||
/* No deprecated API had been removed from build */
|
||||
#define ${_macro_base_name}_EXCLUDE_DEPRECATED_BEFORE_AND_AT 0
|
||||
|
||||
#define ${_macro_base_name}_BUILD_DEPRECATED_SINCE(major, minor) 1
|
||||
"
|
||||
)
|
||||
endif()
|
||||
string(APPEND _output "
|
||||
#ifdef ${_macro_base_name}_NO_DEPRECATED
|
||||
# define ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT ${_version_hexnumber}
|
||||
#endif
|
||||
#ifdef ${_macro_base_name}_NO_DEPRECATED_WARNINGS
|
||||
# define ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE 0
|
||||
#endif
|
||||
|
||||
#ifndef ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE
|
||||
# ifdef ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
# define ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
# else
|
||||
# define ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE ${_version_hexnumber}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT
|
||||
# define ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT ${_default_disabled_deprecated_version_hexnumber}
|
||||
#endif
|
||||
|
||||
#ifdef ${_deprecated_macro_name}
|
||||
# define ${_macro_base_name}_ENABLE_DEPRECATED_SINCE(major, minor) (((major<<16)|(minor<<8)) > ${_macro_base_name}_DISABLE_DEPRECATED_BEFORE_AND_AT)
|
||||
#else
|
||||
# define ${_macro_base_name}_ENABLE_DEPRECATED_SINCE(major, minor) 0
|
||||
#endif
|
||||
"
|
||||
)
|
||||
if (DEFINED ARGS_DEPRECATION_VERSIONS)
|
||||
set(_major_versions)
|
||||
foreach(_version ${ARGS_DEPRECATION_VERSIONS})
|
||||
_ecm_geh_generate_hex_number(_version_hexnumber "${_version}.0")
|
||||
string(REPLACE "." "_" _underscored_version "${_version}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)$" "\\1"
|
||||
_version_major "${_version}")
|
||||
# IN_LIST only since cmake 3.3
|
||||
set(_in_list FALSE)
|
||||
foreach(_v ${_major_versions})
|
||||
if (_v STREQUAL _version_major)
|
||||
set(_in_list TRUE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT _in_list)
|
||||
list(APPEND _major_versions ${_version_major})
|
||||
endif()
|
||||
|
||||
string(APPEND _output "
|
||||
#if ${_macro_base_name}_DEPRECATED_WARNINGS_SINCE >= ${_version_hexnumber}
|
||||
# define ${_macro_base_name}_DEPRECATED_VERSION_${_underscored_version}(text) ${_macro_base_name}_DECL_DEPRECATED_TEXT(text)
|
||||
#else
|
||||
# define ${_macro_base_name}_DEPRECATED_VERSION_${_underscored_version}(text)
|
||||
#endif
|
||||
"
|
||||
)
|
||||
endforeach()
|
||||
foreach(_major_version ${_major_versions})
|
||||
string(APPEND _output
|
||||
"#define ${_macro_base_name}_DEPRECATED_VERSION_${_major_version}(minor, text) ${_macro_base_name}_DEPRECATED_VERSION_${_major_version}_##minor(text)
|
||||
"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
string(APPEND _output
|
||||
"#define ${_macro_base_name}_DEPRECATED_VERSION(major, minor, text) ${_macro_base_name}_DEPRECATED_VERSION_##major(minor, \"Since \"#major\".\"#minor\". \" text)
|
||||
"
|
||||
)
|
||||
string(APPEND _output
|
||||
"#define ${_macro_base_name}_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) ${_macro_base_name}_DEPRECATED_VERSION_##major(minor, \"Since \"#textmajor\".\"#textminor\". \" text)
|
||||
"
|
||||
)
|
||||
# reusing the existing version-controlled deprecation macros for enumerator deprecation macros
|
||||
# to avoid having to repeat all the explicit version variants
|
||||
# TODO: MSVC seems to have issues with __declspec(deprecated) being used as enumerator attribute
|
||||
# and deals only with standard [[deprecated(text)]].
|
||||
# But for now we have to keep the deprecation macros using the compiler-specific attributes,
|
||||
# because CMake's GenerateExportHeader uses the latter for the export macros and
|
||||
# at least GCC does not support both being used mixed e.g. on the same class or method.
|
||||
# Possibly needs to be solved by forking GenerateExportHeader to get complete control.
|
||||
if(NOT MSVC)
|
||||
string(APPEND _output
|
||||
"#if defined(__cpp_enumerator_attributes) && __cpp_enumerator_attributes >= 201411
|
||||
# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION(major, minor, text) ${_macro_base_name}_DEPRECATED_VERSION(major, minor, text)
|
||||
# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) ${_macro_base_name}_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)
|
||||
#else
|
||||
# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION(major, minor, text)
|
||||
# define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)
|
||||
#endif
|
||||
"
|
||||
)
|
||||
else()
|
||||
string(APPEND _output
|
||||
"// Not yet implemented for MSVC
|
||||
#define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION(major, minor, text)
|
||||
#define ${_macro_base_name}_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)
|
||||
"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
if (ARGS_CUSTOM_CONTENT_FROM_VARIABLE)
|
||||
if(DEFINED "${ARGS_CUSTOM_CONTENT_FROM_VARIABLE}")
|
||||
string(APPEND _output "${${ARGS_CUSTOM_CONTENT_FROM_VARIABLE}}\n")
|
||||
else()
|
||||
message(DEPRECATION "Passing a value instead of a variable name with CUSTOM_CONTENT_FROM_VARIABLE. Since 5.98, use the name of a variable.")
|
||||
string(APPEND _output "${ARGS_CUSTOM_CONTENT_FROM_VARIABLE}\n")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# prepare optional arguments to pass through to generate_export_header
|
||||
set(_include_guard_name_args)
|
||||
if (ARGS_INCLUDE_GUARD_NAME)
|
||||
set(_include_guard_name_args INCLUDE_GUARD_NAME "${ARGS_INCLUDE_GUARD_NAME}")
|
||||
endif()
|
||||
set(_export_macro_name_args)
|
||||
if (ARGS_EXPORT_MACRO_NAME)
|
||||
set(_export_macro_name_args EXPORT_MACRO_NAME "${ARGS_EXPORT_MACRO_NAME}")
|
||||
endif()
|
||||
set(_no_export_macro_name_args)
|
||||
if (ARGS_NO_EXPORT_MACRO_NAME)
|
||||
set(_no_export_macro_name_args ARGS_NO_EXPORT_MACRO_NAME "${ARGS_NO_EXPORT_MACRO_NAME}")
|
||||
endif()
|
||||
set(_prefix_name_args)
|
||||
if (ARGS_PREFIX_NAME)
|
||||
set(_prefix_name_args PREFIX_NAME "${ARGS_PREFIX_NAME}")
|
||||
endif()
|
||||
set(_static_define_args)
|
||||
if (ARGS_STATIC_DEFINE)
|
||||
set(_static_define_args STATIC_DEFINE "${ARGS_STATIC_DEFINE}")
|
||||
endif()
|
||||
generate_export_header(${target}
|
||||
BASE_NAME ${ARGS_BASE_NAME}
|
||||
DEPRECATED_MACRO_NAME "${_macro_base_name}_DECL_DEPRECATED"
|
||||
${_prefix_name_args}
|
||||
${_export_macro_name_args}
|
||||
${_no_export_macro_name_args}
|
||||
${_static_define_args}
|
||||
EXPORT_FILE_NAME "${ARGS_EXPORT_FILE_NAME}"
|
||||
${_include_guard_name_args}
|
||||
CUSTOM_CONTENT_FROM_VARIABLE _output
|
||||
)
|
||||
endfunction()
|
||||
|
||||
cmake_policy(POP)
|
||||
@@ -0,0 +1,223 @@
|
||||
# SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kdemail.net>
|
||||
# SPDX-FileCopyrightText: 2015 Patrick Spendrin <patrick.spendrin@kdab.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGenerateHeaders
|
||||
------------------
|
||||
|
||||
Generate C/C++ CamelCase forwarding headers.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_headers(<camelcase_forwarding_headers_var>
|
||||
HEADER_NAMES <CamelCaseName> [<CamelCaseName> [...]]
|
||||
[ORIGINAL <CAMELCASE|LOWERCASE>]
|
||||
[HEADER_EXTENSION <header_extension>]
|
||||
[OUTPUT_DIR <output_dir>]
|
||||
[PREFIX <prefix>]
|
||||
[REQUIRED_HEADERS <variable>]
|
||||
[COMMON_HEADER <HeaderName>]
|
||||
[RELATIVE <relative_path>])
|
||||
|
||||
For each CamelCase header name passed to ``HEADER_NAMES``, a file of that name
|
||||
will be generated that will include a version with ``.h`` or, if set,
|
||||
``.<header_extension>`` appended.
|
||||
For example, the generated header ``ClassA`` will include ``classa.h`` (or
|
||||
``ClassA.h``, see ``ORIGINAL``).
|
||||
If a CamelCaseName consists of multiple comma-separated files, e.g.
|
||||
``ClassA,ClassB,ClassC``, then multiple camelcase header files will be
|
||||
generated which are redirects to the first header file.
|
||||
The file locations of these generated headers will be stored in
|
||||
<camelcase_forwarding_headers_var>.
|
||||
|
||||
``ORIGINAL`` specifies how the name of the original header is written: lowercased
|
||||
or also camelcased. The default is "LOWERCASE". Since 1.8.0.
|
||||
|
||||
``HEADER_EXTENSION`` specifies what file name extension is used for the header
|
||||
files. The default is "h". Since 5.48.0.
|
||||
|
||||
``PREFIX`` places the generated headers in subdirectories. This should be a
|
||||
CamelCase name like ``KParts``, which will cause the CamelCase forwarding
|
||||
headers to be placed in the ``KParts`` directory (e.g. ``KParts/Part``). It
|
||||
will also, for the convenience of code in the source distribution, generate
|
||||
forwarding headers based on the original names (e.g. ``kparts/part.h``). This
|
||||
allows includes like ``"#include <kparts/part.h>"`` to be used before
|
||||
installation, as long as the include_directories are set appropriately.
|
||||
|
||||
``OUTPUT_DIR`` specifies where the files will be generated; this should be within
|
||||
the build directory. By default, ``${CMAKE_CURRENT_BINARY_DIR}`` will be used.
|
||||
This option can be used to avoid file conflicts.
|
||||
|
||||
``REQUIRED_HEADERS`` specifies an output variable name where all the required
|
||||
headers will be appended so that they can be installed together with the
|
||||
generated ones. This is mostly intended as a convenience so that adding a new
|
||||
header to a project only requires specifying the CamelCase variant in the
|
||||
CMakeLists.txt file; the original variant will then be added to this
|
||||
variable.
|
||||
|
||||
``COMMON_HEADER`` generates an additional convenience header which includes all
|
||||
other header files.
|
||||
|
||||
The ``RELATIVE`` argument indicates where the original headers can be found
|
||||
relative to ``CMAKE_CURRENT_SOURCE_DIR``. It does not affect the generated
|
||||
CamelCase forwarding files, but ``ecm_generate_headers()`` uses it when checking
|
||||
that the original header exists, and to generate originally named forwarding
|
||||
headers when ``PREFIX`` is set.
|
||||
|
||||
To allow other parts of the source distribution (eg: tests) to use the
|
||||
generated headers before installation, it may be desirable to set the
|
||||
``INCLUDE_DIRECTORIES`` property for the library target to output_dir. For
|
||||
example, if ``OUTPUT_DIR`` is ``CMAKE_CURRENT_BINARY_DIR`` (the default), you could do
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
target_include_directories(MyLib PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>")
|
||||
|
||||
Example usage (without ``PREFIX``):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_headers(
|
||||
MyLib_FORWARDING_HEADERS
|
||||
HEADERS
|
||||
MLFoo
|
||||
MLBar
|
||||
# etc
|
||||
REQUIRED_HEADERS MyLib_HEADERS
|
||||
COMMON_HEADER MLGeneral
|
||||
)
|
||||
install(FILES ${MyLib_FORWARDING_HEADERS} ${MyLib_HEADERS}
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/include
|
||||
COMPONENT Devel)
|
||||
|
||||
Example usage (with ``PREFIX``):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_headers(
|
||||
MyLib_FORWARDING_HEADERS
|
||||
HEADERS
|
||||
Foo
|
||||
# several classes are contained in bar.h, so generate
|
||||
# additional files
|
||||
Bar,BarList
|
||||
# etc
|
||||
PREFIX MyLib
|
||||
REQUIRED_HEADERS MyLib_HEADERS
|
||||
)
|
||||
install(FILES ${MyLib_FORWARDING_HEADERS}
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/MyLib
|
||||
COMPONENT Devel)
|
||||
install(FILES ${MyLib_HEADERS}
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/mylib
|
||||
COMPONENT Devel)
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ECM_GENERATE_HEADERS camelcase_forwarding_headers_var)
|
||||
set(options)
|
||||
set(oneValueArgs ORIGINAL HEADER_EXTENSION OUTPUT_DIR PREFIX REQUIRED_HEADERS COMMON_HEADER RELATIVE)
|
||||
set(multiValueArgs HEADER_NAMES)
|
||||
cmake_parse_arguments(EGH "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if (EGH_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ECM_GENERATE_HEADERS: ${EGH_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(NOT EGH_HEADER_NAMES)
|
||||
message(FATAL_ERROR "Missing header_names argument to ECM_GENERATE_HEADERS")
|
||||
endif()
|
||||
|
||||
if(NOT EGH_ORIGINAL)
|
||||
# default
|
||||
set(EGH_ORIGINAL "LOWERCASE")
|
||||
endif()
|
||||
if(NOT EGH_ORIGINAL STREQUAL "LOWERCASE" AND NOT EGH_ORIGINAL STREQUAL "CAMELCASE")
|
||||
message(FATAL_ERROR "Unexpected value for original argument to ECM_GENERATE_HEADERS: ${EGH_ORIGINAL}")
|
||||
endif()
|
||||
|
||||
if(NOT EGH_HEADER_EXTENSION)
|
||||
set(EGH_HEADER_EXTENSION "h")
|
||||
endif()
|
||||
|
||||
if(NOT EGH_OUTPUT_DIR)
|
||||
set(EGH_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
# Make sure EGH_RELATIVE is /-terminated when it's not empty
|
||||
if (EGH_RELATIVE AND NOT "${EGH_RELATIVE}" MATCHES "^.*/$")
|
||||
set(EGH_RELATIVE "${EGH_RELATIVE}/")
|
||||
endif()
|
||||
|
||||
set(originalprefix)
|
||||
if (EGH_PREFIX)
|
||||
if (NOT "${EGH_PREFIX}" MATCHES "^.*/$")
|
||||
set(EGH_PREFIX "${EGH_PREFIX}/")
|
||||
endif()
|
||||
if (EGH_ORIGINAL STREQUAL "CAMELCASE")
|
||||
set(originalprefix "${EGH_PREFIX}")
|
||||
else()
|
||||
string(TOLOWER "${EGH_PREFIX}" originalprefix)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach(_classnameentry ${EGH_HEADER_NAMES})
|
||||
string(REPLACE "," ";" _classnames ${_classnameentry})
|
||||
list(GET _classnames 0 _baseclass)
|
||||
|
||||
if (EGH_ORIGINAL STREQUAL "CAMELCASE")
|
||||
set(originalbasename "${_baseclass}")
|
||||
else()
|
||||
string(TOLOWER "${_baseclass}" originalbasename)
|
||||
endif()
|
||||
|
||||
set(_actualheader "${CMAKE_CURRENT_SOURCE_DIR}/${EGH_RELATIVE}${originalbasename}.${EGH_HEADER_EXTENSION}")
|
||||
get_source_file_property(_generated "${_actualheader}" GENERATED)
|
||||
if (NOT _generated AND NOT EXISTS ${_actualheader})
|
||||
message(FATAL_ERROR "Could not find \"${_actualheader}\"")
|
||||
endif()
|
||||
|
||||
foreach(_CLASSNAME ${_classnames})
|
||||
set(FANCY_HEADER_FILE "${EGH_OUTPUT_DIR}/${EGH_PREFIX}${_CLASSNAME}")
|
||||
if (NOT EXISTS ${FANCY_HEADER_FILE})
|
||||
file(WRITE ${FANCY_HEADER_FILE} "#include \"${originalprefix}${originalbasename}.${EGH_HEADER_EXTENSION}\" // IWYU pragma: export\n")
|
||||
endif()
|
||||
list(APPEND ${camelcase_forwarding_headers_var} "${FANCY_HEADER_FILE}")
|
||||
if (EGH_PREFIX)
|
||||
# Local forwarding header, for namespaced headers, e.g. kparts/part.h
|
||||
if(EGH_ORIGINAL STREQUAL "CAMELCASE")
|
||||
set(originalclassname "${_CLASSNAME}")
|
||||
else()
|
||||
string(TOLOWER "${_CLASSNAME}" originalclassname)
|
||||
endif()
|
||||
set(REGULAR_HEADER_NAME ${EGH_OUTPUT_DIR}/${originalprefix}${originalclassname}.${EGH_HEADER_EXTENSION})
|
||||
if (NOT EXISTS ${REGULAR_HEADER_NAME})
|
||||
file(RELATIVE_PATH _actualheader_relative "${EGH_OUTPUT_DIR}/${originalprefix}" "${_actualheader}")
|
||||
file(WRITE ${REGULAR_HEADER_NAME} "#include \"${_actualheader_relative}\" // IWYU pragma: export\n")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(APPEND _REQUIRED_HEADERS "${_actualheader}")
|
||||
endforeach()
|
||||
|
||||
if(EGH_COMMON_HEADER)
|
||||
#combine required headers into 1 big convenience header
|
||||
set(COMMON_HEADER ${EGH_OUTPUT_DIR}/${EGH_PREFIX}${EGH_COMMON_HEADER})
|
||||
file(WRITE ${COMMON_HEADER} "// convenience header\n")
|
||||
foreach(_header ${_REQUIRED_HEADERS})
|
||||
get_filename_component(_base ${_header} NAME)
|
||||
file(APPEND ${COMMON_HEADER} "#include \"${_base}\"\n")
|
||||
endforeach()
|
||||
list(APPEND ${camelcase_forwarding_headers_var} "${COMMON_HEADER}")
|
||||
endif()
|
||||
|
||||
set(${camelcase_forwarding_headers_var} ${${camelcase_forwarding_headers_var}} PARENT_SCOPE)
|
||||
if (EGH_REQUIRED_HEADERS)
|
||||
set(${EGH_REQUIRED_HEADERS} ${${EGH_REQUIRED_HEADERS}} ${_REQUIRED_HEADERS} PARENT_SCOPE)
|
||||
endif ()
|
||||
endfunction()
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
# SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGeneratePkgConfigFile
|
||||
------------------------
|
||||
|
||||
Generate a `pkg-config <https://www.freedesktop.org/wiki/Software/pkg-config/>`_
|
||||
file for the benefit of
|
||||
`autotools <https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html>`_-based
|
||||
projects.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_pkgconfig_file(BASE_NAME <baseName>
|
||||
[LIB_NAME <libName>]
|
||||
[DEPS [PRIVATE|PUBLIC] <dep> [[PRIVATE|PUBLIC] <dep> [...]]]
|
||||
[FILENAME_VAR <filename_variable>]
|
||||
[INCLUDE_INSTALL_DIR <dir>]
|
||||
[LIB_INSTALL_DIR <dir>]
|
||||
[DEFINES -D<variable=value>...]
|
||||
[DESCRIPTION <library description>] # since 5.41.0
|
||||
[URL <url>] # since 5.89.0
|
||||
[INSTALL])
|
||||
|
||||
``BASE_NAME`` is the name of the module. It's the name projects will use to
|
||||
find the module.
|
||||
|
||||
``LIB_NAME`` is the name of the library that is being exported. If undefined,
|
||||
it will default to the ``BASE_NAME``. That means the ``LIB_NAME`` will be set
|
||||
as the name field as well as the library to link to.
|
||||
|
||||
``DEPS`` is the list of libraries required by this library. Libraries that are
|
||||
not exposed to applications should be marked with ``PRIVATE``. The default
|
||||
is ``PUBLIC``, but note that according to the
|
||||
`Guide to pkg-config <https://people.freedesktop.org/~dbn/pkg-config-guide.html>`_
|
||||
marking dependencies as private is usually preferred. The ``PUBLIC`` and
|
||||
``PRIVATE`` keywords are supported since 5.89.0.
|
||||
|
||||
``FILENAME_VAR`` is specified with a variable name. This variable will
|
||||
receive the location of the generated file will be set, within the build
|
||||
directory. This way it can be used in case some processing is required. See
|
||||
also ``INSTALL``.
|
||||
|
||||
``INCLUDE_INSTALL_DIR`` specifies where the includes will be installed. If
|
||||
it's not specified, it will default to ``INSTALL_INCLUDEDIR``,
|
||||
``CMAKE_INSTALL_INCLUDEDIR`` or just "include/" in case they are specified,
|
||||
with the ``BASE_NAME`` postfixed.
|
||||
|
||||
``LIB_INSTALL_DIR`` specifies where the library is being installed. If it's
|
||||
not specified, it will default to ``LIB_INSTALL_DIR``,
|
||||
``CMAKE_INSTALL_LIBDIR`` or just "lib/" in case they are specified.
|
||||
|
||||
``DEFINES`` is a list of preprocessor defines that it is recommended users of
|
||||
the library pass to the compiler when using it.
|
||||
|
||||
``DESCRIPTION`` describes what this library is. If it's not specified, CMake
|
||||
will first try to get the description from the metainfo.yaml file or will
|
||||
create one based on ``LIB_NAME``. Since 5.41.0.
|
||||
|
||||
``URL`` An URL where people can get more information about and download the
|
||||
package. Defaults to "https://www.kde.org/". Since 5.89.0.
|
||||
|
||||
``INSTALL`` will cause the module to be installed to the ``pkgconfig``
|
||||
subdirectory of ``LIB_INSTALL_DIR``, unless the ``ECM_PKGCONFIG_INSTALL_DIR``
|
||||
cache variable is set to something different.
|
||||
|
||||
.. note::
|
||||
The first call to ``ecm_generate_pkgconfig_file()`` with the ``INSTALL``
|
||||
argument will cause ``ECM_PKGCONFIG_INSTALL_DIR`` to be set to the cache,
|
||||
and will be used in any subsequent calls.
|
||||
|
||||
To properly use this macro a version needs to be set. To retrieve it,
|
||||
``ECM_PKGCONFIG_INSTALL_DIR`` uses ``PROJECT_VERSION``. To set it, use the
|
||||
``project()`` command or the ``ecm_setup_version()`` macro
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_pkgconfig_file(
|
||||
BASE_NAME KF5Archive
|
||||
DEPS Qt5Core
|
||||
FILENAME_VAR pkgconfig_filename
|
||||
INSTALL
|
||||
)
|
||||
|
||||
Since 1.3.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ECM_GENERATE_PKGCONFIG_FILE)
|
||||
set(options INSTALL)
|
||||
set(oneValueArgs BASE_NAME LIB_NAME FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR DESCRIPTION URL)
|
||||
set(multiValueArgs DEPS DEFINES)
|
||||
|
||||
cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(EGPF_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PKGCONFIG_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT EGPF_BASE_NAME)
|
||||
message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PKGCONFIG_FILE() call")
|
||||
endif()
|
||||
if(NOT PROJECT_VERSION)
|
||||
message(FATAL_ERROR "Required variable PROJECT_VERSION not set before ECM_GENERATE_PKGCONFIG_FILE() call. Did you call ecm_setup_version or project with the VERSION argument?")
|
||||
endif()
|
||||
if(NOT EGPF_LIB_NAME)
|
||||
set(EGPF_LIB_NAME ${EGPF_BASE_NAME})
|
||||
endif()
|
||||
if(NOT EGPF_INCLUDE_INSTALL_DIR)
|
||||
if(INCLUDE_INSTALL_DIR)
|
||||
set(EGPF_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}")
|
||||
elseif(CMAKE_INSTALL_INCLUDEDIR)
|
||||
set(EGPF_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}")
|
||||
else()
|
||||
set(EGPF_INCLUDE_INSTALL_DIR "include/${EGPF_BASE_NAME}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT EGPF_LIB_INSTALL_DIR)
|
||||
if(LIB_INSTALL_DIR)
|
||||
set(EGPF_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}")
|
||||
elseif(CMAKE_INSTALL_LIBDIR)
|
||||
set(EGPF_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(EGPF_LIB_INSTALL_DIR "lib")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT EGPF_DESCRIPTION)
|
||||
if(EXISTS ${CMAKE_SOURCE_DIR}/metainfo.yaml)
|
||||
file(STRINGS "${CMAKE_SOURCE_DIR}/metainfo.yaml" _EGPF_METAINFO_DESCRIPTION_STRING REGEX "^description:.*$")
|
||||
if(_EGPF_METAINFO_DESCRIPTION_STRING)
|
||||
string(REGEX REPLACE "^description:[ ]*(.*)" "\\1" EGPF_DESCRIPTION ${_EGPF_METAINFO_DESCRIPTION_STRING})
|
||||
endif()
|
||||
endif()
|
||||
if("${EGPF_DESCRIPTION}" STREQUAL "")
|
||||
set(EGPF_DESCRIPTION "${EGPF_LIB_NAME} library.")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT EGPF_URL)
|
||||
set(EGPF_URL "https://www.kde.org/")
|
||||
endif()
|
||||
|
||||
set(PKGCONFIG_TARGET_BASENAME ${EGPF_BASE_NAME})
|
||||
set(PKGCONFIG_TARGET_LIBNAME ${EGPF_LIB_NAME})
|
||||
if (DEFINED EGPF_DEPS)
|
||||
# convert the dependencies to a list
|
||||
string(REPLACE " " ";" EGPF_DEPS "${EGPF_DEPS}")
|
||||
foreach(EGPF_DEP ${EGPF_DEPS})
|
||||
if("${EGPF_DEP}" STREQUAL "")
|
||||
elseif("${EGPF_DEP}" STREQUAL "PRIVATE")
|
||||
set(private_deps ON)
|
||||
elseif("${EGPF_DEP}" STREQUAL "PUBLIC")
|
||||
unset(private_deps)
|
||||
else()
|
||||
if(private_deps)
|
||||
list(APPEND PKGCONFIG_TARGET_DEPS_PRIVATE "${EGPF_DEP}")
|
||||
else()
|
||||
list(APPEND PKGCONFIG_TARGET_DEPS "${EGPF_DEP}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
list(JOIN PKGCONFIG_TARGET_DEPS " " PKGCONFIG_TARGET_DEPS)
|
||||
list(JOIN PKGCONFIG_TARGET_DEPS_PRIVATE " " PKGCONFIG_TARGET_DEPS_PRIVATE)
|
||||
endif ()
|
||||
if(TARGET ${EGPF_LIB_NAME})
|
||||
# Generator expression cannot be evaluated when creating the pkgconfig file, we need to convert the public include directories
|
||||
# into something pkgconfig can understand
|
||||
get_target_property(__EGPF_TARGET_INCLUDE_DIRS ${EGPF_LIB_NAME} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
|
||||
if(__EGPF_TARGET_INCLUDE_DIRS)
|
||||
set(_EGPF_TARGET_INCLUDE_DIRS "${__EGPF_TARGET_INCLUDE_DIRS}")
|
||||
# INTERFACE_INCLUDE_DIRS can contain semicolon separated locations. Since CMake still doesn't accept different separators,
|
||||
# We need to convert _EGPF_TARGET_INCLUDE_DIRS to a string, extract the locations and convert it back to a list
|
||||
string(REPLACE ";" "|" _EGPF_TARGET_INCLUDE_DIRS "${_EGPF_TARGET_INCLUDE_DIRS}")
|
||||
list(TRANSFORM _EGPF_TARGET_INCLUDE_DIRS REPLACE "\\$<INSTALL_INTERFACE:([^,>]+)>" "\\1")
|
||||
string(REPLACE "|" ";" _EGPF_TARGET_INCLUDE_DIRS "${_EGPF_TARGET_INCLUDE_DIRS}")
|
||||
|
||||
# Remove any other generator expression.
|
||||
string(GENEX_STRIP "${_EGPF_TARGET_INCLUDE_DIRS}" _EGPF_TARGET_INCLUDE_DIRS)
|
||||
|
||||
# Remove possible duplicate entries a first time
|
||||
list(REMOVE_DUPLICATES _EGPF_TARGET_INCLUDE_DIRS)
|
||||
|
||||
foreach(EGPF_INCLUDE_DIR IN LISTS _EGPF_TARGET_INCLUDE_DIRS)
|
||||
# if the path is not absolute (that would be the case for KDEInstallDirs variables), append \${prefix} before each entry
|
||||
if(NOT IS_ABSOLUTE "${EGPF_INCLUDE_DIR}")
|
||||
list(TRANSFORM _EGPF_TARGET_INCLUDE_DIRS REPLACE "${EGPF_INCLUDE_DIR}" "\${prefix}/${EGPF_INCLUDE_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}")
|
||||
list(APPEND PKGCONFIG_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}")
|
||||
else()
|
||||
list(APPEND PKGCONFIG_TARGET_INCLUDES "\${prefix}/${EGPF_INCLUDE_INSTALL_DIR}")
|
||||
endif()
|
||||
list(APPEND _EGPF_TARGET_INCLUDE_DIRS "${PKGCONFIG_TARGET_INCLUDES}")
|
||||
|
||||
# Strip trailing '/' if present anywhere
|
||||
list(TRANSFORM _EGPF_TARGET_INCLUDE_DIRS REPLACE "(.*)/$" "\\1")
|
||||
|
||||
# Deduplicate the list a second time, append -I before each entry and convert it to a string
|
||||
list(REMOVE_DUPLICATES _EGPF_TARGET_INCLUDE_DIRS)
|
||||
list(TRANSFORM _EGPF_TARGET_INCLUDE_DIRS PREPEND "-I")
|
||||
string(REPLACE ";" " " PKGCONFIG_CFLAGS_INCLUDES "${_EGPF_TARGET_INCLUDE_DIRS}")
|
||||
|
||||
if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}")
|
||||
set(PKGCONFIG_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}")
|
||||
else()
|
||||
set(PKGCONFIG_TARGET_LIBS "\${prefix}/${EGPF_LIB_INSTALL_DIR}")
|
||||
endif()
|
||||
set(PKGCONFIG_TARGET_DESCRIPTION "${EGPF_DESCRIPTION}")
|
||||
set(PKGCONFIG_TARGET_URL "${EGPF_URL}")
|
||||
set(PKGCONFIG_TARGET_DEFINES "")
|
||||
if(EGPF_DEFINES)
|
||||
# Transform the list to a string without semicolon
|
||||
string(REPLACE ";" " " EGPF_DEFINES "${EGPF_DEFINES}")
|
||||
set(PKGCONFIG_TARGET_DEFINES "${EGPF_DEFINES}")
|
||||
endif()
|
||||
|
||||
set(PKGCONFIG_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/${PKGCONFIG_TARGET_BASENAME}.pc)
|
||||
if (EGPF_FILENAME_VAR)
|
||||
set(${EGPF_FILENAME_VAR} ${PKGCONFIG_FILENAME} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
set(PKGCONFIG_CONTENT
|
||||
"
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=\${prefix}
|
||||
libdir=${PKGCONFIG_TARGET_LIBS}
|
||||
includedir=${PKGCONFIG_TARGET_INCLUDES}
|
||||
|
||||
Name: ${PKGCONFIG_TARGET_LIBNAME}
|
||||
Description: ${PKGCONFIG_TARGET_DESCRIPTION}
|
||||
URL: ${PKGCONFIG_TARGET_URL}
|
||||
Version: ${PROJECT_VERSION}
|
||||
Libs: -L${PKGCONFIG_TARGET_LIBS} -l${PKGCONFIG_TARGET_LIBNAME}
|
||||
Cflags: ${PKGCONFIG_CFLAGS_INCLUDES} ${PKGCONFIG_TARGET_DEFINES}
|
||||
Requires: ${PKGCONFIG_TARGET_DEPS}
|
||||
"
|
||||
)
|
||||
if(PKGCONFIG_TARGET_DEPS_PRIVATE)
|
||||
set(PKGCONFIG_CONTENT
|
||||
"${PKGCONFIG_CONTENT}Requires.private: ${PKGCONFIG_TARGET_DEPS_PRIVATE}
|
||||
"
|
||||
)
|
||||
endif()
|
||||
file(WRITE ${PKGCONFIG_FILENAME} "${PKGCONFIG_CONTENT}")
|
||||
|
||||
if(EGPF_INSTALL)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
set(ECM_PKGCONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig" CACHE PATH "The directory where pkgconfig will be installed to.")
|
||||
else()
|
||||
set(ECM_PKGCONFIG_INSTALL_DIR "${EGPF_LIB_INSTALL_DIR}/pkgconfig" CACHE PATH "The directory where pkgconfig will be installed to.")
|
||||
endif()
|
||||
install(FILES ${PKGCONFIG_FILENAME} DESTINATION ${ECM_PKGCONFIG_INSTALL_DIR})
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,256 @@
|
||||
# SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGeneratePriFile
|
||||
------------------
|
||||
|
||||
Generate a ``.pri`` file for the benefit of qmake-based projects.
|
||||
|
||||
As well as the function below, this module creates the cache variable
|
||||
``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``.
|
||||
This assumes Qt and the current project are both installed to the same
|
||||
non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will
|
||||
certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like
|
||||
``share/qt5/mkspecs/modules``.
|
||||
|
||||
The main thing is that this should be the ``modules`` subdirectory of either
|
||||
the default qmake ``mkspecs`` directory or of a directory that will be in the
|
||||
``$QMAKEPATH`` environment variable when qmake is run.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_pri_file(BASE_NAME <baseName>
|
||||
LIB_NAME <libName>
|
||||
[VERSION <version>] # since 5.83
|
||||
[DEPS "<dep> [<dep> [...]]"]
|
||||
[FILENAME_VAR <filename_variable>]
|
||||
[INCLUDE_INSTALL_DIRS <dir> [<dir> [...]]] # since 5.92
|
||||
[INCLUDE_INSTALL_DIR <dir>] # deprecated since 5.92
|
||||
[LIB_INSTALL_DIR <dir>])
|
||||
|
||||
If your CMake project produces a Qt-based library, you may expect there to be
|
||||
applications that wish to use it that use a qmake-based build system, rather
|
||||
than a CMake-based one. Creating a ``.pri`` file will make use of your
|
||||
library convenient for them, in much the same way that CMake config files make
|
||||
things convenient for CMake-based applications. ``ecm_generate_pri_file()``
|
||||
generates just such a file.
|
||||
|
||||
``VERSION`` specifies the version of the library the ``.pri`` file describes. If
|
||||
not set, the value is taken from the context variable ``PROJECT_VERSION``.
|
||||
This variable is usually set by the ``project(... VERSION ...)`` command or,
|
||||
if CMake policy CMP0048 is not ``NEW``, by :module:`ECMSetupVersion`.
|
||||
For backward-compatibility with older ECM versions the
|
||||
``PROJECT_VERSION_STRING`` variable as set by :module:`ECMSetupVersion`
|
||||
will be preferred over ``PROJECT_VERSION`` if set, unless the minimum
|
||||
required version of ECM is 5.83 and newer. Since 5.83.
|
||||
|
||||
``BASE_NAME`` specifies the name qmake project (.pro) files should use to refer to
|
||||
the library (eg: KArchive). ``LIB_NAME`` is the name of the actual library to
|
||||
link to (ie: the first argument to add_library()). ``DEPS`` is a space-separated
|
||||
list of the base names of other libraries (for Qt libraries, use the same
|
||||
names you use with the ``QT`` variable in a qmake project file, such as "core"
|
||||
for QtCore). ``FILENAME_VAR`` specifies the name of a variable to store the path
|
||||
to the generated file in.
|
||||
|
||||
``INCLUDE_INSTALL_DIRS`` are the paths (relative to ``CMAKE_INSTALL_PREFIX``) that
|
||||
include files will be installed to. It defaults to
|
||||
``${INCLUDE_INSTALL_DIR}/<baseName>`` if the ``INCLUDE_INSTALL_DIR`` variable
|
||||
is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable
|
||||
is used instead, and if neither are set ``include`` is used. ``LIB_INSTALL_DIR``
|
||||
operates similarly for the installation location for libraries; it defaults to
|
||||
``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order.
|
||||
|
||||
``INCLUDE_INSTALL_DIR`` is the old variant of ``INCLUDE_INSTALL_DIRS``, taking only one
|
||||
directory.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_generate_pri_file(
|
||||
BASE_NAME KArchive
|
||||
LIB_NAME KF5KArchive
|
||||
DEPS "core"
|
||||
FILENAME_VAR pri_filename
|
||||
VERSION 4.2.0
|
||||
)
|
||||
install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
|
||||
|
||||
A qmake-based project that wished to use this would then do::
|
||||
|
||||
QT += KArchive
|
||||
|
||||
in their ``.pro`` file.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
# Replicate the logic from KDEInstallDirs.cmake as we can't depend on it
|
||||
# Ask qmake if we're using the same prefix as Qt
|
||||
set(_should_query_qt OFF)
|
||||
if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS)
|
||||
include(ECMQueryQt)
|
||||
ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX TRY)
|
||||
if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}")
|
||||
set(_should_query_qt ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(KDE_INSTALL_USE_QT_SYS_PATHS OR _should_query_qt)
|
||||
include(ECMQueryQt)
|
||||
ecm_query_qt(qt_install_prefix_dir QT_INSTALL_PREFIX)
|
||||
ecm_query_qt(qt_host_data_dir QT_HOST_DATA)
|
||||
if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}")
|
||||
file(RELATIVE_PATH qt_host_data_dir ${qt_install_prefix_dir} ${qt_host_data_dir})
|
||||
endif()
|
||||
if(qt_host_data_dir STREQUAL "")
|
||||
set(mkspecs_install_dir mkspecs/modules)
|
||||
else()
|
||||
set(mkspecs_install_dir ${qt_host_data_dir}/mkspecs/modules)
|
||||
endif()
|
||||
set(ECM_MKSPECS_INSTALL_DIR ${mkspecs_install_dir} CACHE PATH "The directory where mkspecs will be installed to.")
|
||||
else()
|
||||
set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.")
|
||||
endif()
|
||||
|
||||
function(ECM_GENERATE_PRI_FILE)
|
||||
set(options )
|
||||
set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR VERSION)
|
||||
set(multiValueArgs INCLUDE_INSTALL_DIRS)
|
||||
|
||||
cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(EGPF_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PRI_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(ECM_GLOBAL_FIND_VERSION VERSION_LESS 5.83.0)
|
||||
set(_support_backward_compat_version_string_var TRUE)
|
||||
else()
|
||||
set(_support_backward_compat_version_string_var FALSE)
|
||||
endif()
|
||||
|
||||
if(NOT EGPF_BASE_NAME)
|
||||
message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call")
|
||||
endif()
|
||||
if(NOT EGPF_LIB_NAME)
|
||||
message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call")
|
||||
endif()
|
||||
if(NOT EGPF_VERSION)
|
||||
if(_support_backward_compat_version_string_var)
|
||||
if(NOT PROJECT_VERSION_STRING AND NOT PROJECT_VERSION)
|
||||
message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING or PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?")
|
||||
endif()
|
||||
else()
|
||||
if(NOT PROJECT_VERSION)
|
||||
message(FATAL_ERROR "Required variable PROJECT_VERSION not set before ECM_GENERATE_PRI_FILE() call. Missing call of ecm_setup_version() or project(VERSION)?")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(EGPF_INCLUDE_INSTALL_DIR)
|
||||
if(EGPF_INCLUDE_INSTALL_DIRS)
|
||||
message(FATAL_ERROR "Only one argument of INCLUDE_INSTALL_DIR & INCLUDE_INSTALL_DIRS can be used in ECM_GENERATE_PRI_FILE() call")
|
||||
endif()
|
||||
set(EGPF_INCLUDE_INSTALL_DIRS ${EGPF_INCLUDE_INSTALL_DIR})
|
||||
endif()
|
||||
if(NOT EGPF_INCLUDE_INSTALL_DIRS)
|
||||
if(INCLUDE_INSTALL_DIR)
|
||||
set(EGPF_INCLUDE_INSTALL_DIRS "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}")
|
||||
elseif(CMAKE_INSTALL_INCLUDEDIR)
|
||||
set(EGPF_INCLUDE_INSTALL_DIRS "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}")
|
||||
else()
|
||||
set(EGPF_INCLUDE_INSTALL_DIRS "include/${EGPF_BASE_NAME}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT EGPF_LIB_INSTALL_DIR)
|
||||
if(LIB_INSTALL_DIR)
|
||||
set(EGPF_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}")
|
||||
elseif(CMAKE_INSTALL_LIBDIR)
|
||||
set(EGPF_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(EGPF_LIB_INSTALL_DIR "lib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(EGPF_VERSION)
|
||||
set(PRI_VERSION "${EGPF_VERSION}")
|
||||
else()
|
||||
if(_support_backward_compat_version_string_var AND PROJECT_VERSION_STRING)
|
||||
set(PRI_VERSION "${PROJECT_VERSION_STRING}")
|
||||
if(NOT PROJECT_VERSION_STRING STREQUAL PROJECT_VERSION)
|
||||
message(DEPRECATION "ECM_GENERATE_PRI_FILE() will no longer support PROJECT_VERSION_STRING when the required minimum version of ECM is 5.83 or newer. Set VERSION parameter or use PROJECT_VERSION instead.")
|
||||
endif()
|
||||
else()
|
||||
set(PRI_VERSION "${PROJECT_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PRI_VERSION_MAJOR "${PRI_VERSION}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PRI_VERSION_MINOR "${PRI_VERSION}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PRI_VERSION_PATCH "${PRI_VERSION}")
|
||||
|
||||
# Prepare the right number of "../.." to go from ECM_MKSPECS_INSTALL_DIR to the install prefix
|
||||
# This allows to make the generated pri files relocatable (no absolute paths)
|
||||
if (IS_ABSOLUTE ${ECM_MKSPECS_INSTALL_DIR})
|
||||
set(BASEPATH ${CMAKE_INSTALL_PREFIX})
|
||||
else()
|
||||
string(REGEX REPLACE "[^/]+" ".." PRI_ROOT_RELATIVE_TO_MKSPECS ${ECM_MKSPECS_INSTALL_DIR})
|
||||
set(BASEPATH "$$PWD/${PRI_ROOT_RELATIVE_TO_MKSPECS}")
|
||||
endif()
|
||||
|
||||
set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME})
|
||||
set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME})
|
||||
set(PRI_TARGET_QTDEPS ${EGPF_DEPS})
|
||||
set(PRI_TARGET_INCLUDES)
|
||||
foreach(_dir ${EGPF_INCLUDE_INSTALL_DIRS})
|
||||
# separate list entries with space
|
||||
if(IS_ABSOLUTE "${_dir}")
|
||||
string(APPEND PRI_TARGET_INCLUDES " ${_dir}")
|
||||
else()
|
||||
string(APPEND PRI_TARGET_INCLUDES " ${BASEPATH}/${_dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}")
|
||||
set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}")
|
||||
else()
|
||||
set(PRI_TARGET_LIBS "${BASEPATH}/${EGPF_LIB_INSTALL_DIR}")
|
||||
endif()
|
||||
set(PRI_TARGET_DEFINES "")
|
||||
|
||||
set(PRI_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/qt_${PRI_TARGET_BASENAME}.pri)
|
||||
if (EGPF_FILENAME_VAR)
|
||||
set(${EGPF_FILENAME_VAR} ${PRI_FILENAME} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
set(PRI_TARGET_MODULE_CONFIG "")
|
||||
# backward compat: it was not obvious LIB_NAME needs to be a target name,
|
||||
# and some projects where the target name was not the actual library output name
|
||||
# passed the output name for LIB_NAME, so .name & .module prperties are correctly set.
|
||||
# TODO: improve API dox, allow control over module name if target name != output name
|
||||
if(TARGET ${EGPF_LIB_NAME})
|
||||
get_target_property(target_type ${EGPF_LIB_NAME} TYPE)
|
||||
if (target_type STREQUAL "STATIC_LIBRARY")
|
||||
set(PRI_TARGET_MODULE_CONFIG "staticlib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT ${PRI_FILENAME}
|
||||
CONTENT
|
||||
"QT.${PRI_TARGET_BASENAME}.VERSION = ${PRI_VERSION}
|
||||
QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PRI_VERSION_MAJOR}
|
||||
QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PRI_VERSION_MINOR}
|
||||
QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PRI_VERSION_PATCH}
|
||||
QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME}
|
||||
QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME}
|
||||
QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES}
|
||||
QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES}
|
||||
QT.${PRI_TARGET_BASENAME}.private_includes =
|
||||
QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS}
|
||||
QT.${PRI_TARGET_BASENAME}.depends = ${PRI_TARGET_QTDEPS}
|
||||
QT.${PRI_TARGET_BASENAME}.module_config = ${PRI_TARGET_MODULE_CONFIG}
|
||||
"
|
||||
)
|
||||
endfunction()
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Qt Company Ltd.
|
||||
# SPDX-FileCopyrightText: 2024 Manuel Alcaraz Zambrano <manuelalcarazzam@gmail.com>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Based on https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/widgetbinding/CMakeLists.txt
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGeneratePythonBindings
|
||||
-------------------------
|
||||
|
||||
This module is experimental and internal. The interface will likely
|
||||
change in the coming releases.
|
||||
|
||||
Generate Python bindings using Shiboken.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_python_bindings(PACKAGE_NAME <pythonlibrary>
|
||||
VERSION <version>
|
||||
WRAPPED_HEADER <filename>
|
||||
TYPESYSTEM <filename>
|
||||
GENERATED_SOURCES <filename> [<filename> [...]]
|
||||
DEPENDENCIES <target> [<target> [...]]
|
||||
QT_VERSION <version>
|
||||
HOMEPAGE_URL <url>
|
||||
ISSUES_URL <url>
|
||||
AUTHOR <string>
|
||||
README <filename> )
|
||||
|
||||
``<pythonlibrary>`` is the name of the Python library that will be created.
|
||||
|
||||
``VERSION`` is the version of the library.
|
||||
|
||||
``WRAPPED_HEADER`` is a C++ header that contains all the required includes
|
||||
for the library.
|
||||
|
||||
``TYPESYSTEM`` is the XML file where the bindings are defined.
|
||||
|
||||
``GENERATED_SOURCES`` is the list of generated C++ source files by Shiboken
|
||||
that will be used to build the shared library.
|
||||
|
||||
``QT_VERSION`` is the minimum required Qt version of the library.
|
||||
|
||||
``DEPENDENCIES`` is the list of dependencies that the bindings uses.
|
||||
|
||||
``HOMEPAGE_URL`` is a URL to the proyect homepage.
|
||||
|
||||
``ISSUES_URL` is a URL where users can report bugs and feature requests.
|
||||
|
||||
``AUTHOR`` is a string with the author of the library.
|
||||
|
||||
``README`` is a Markdown file that will be used as the project's
|
||||
description on the Python Package Index.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
set(MODULES_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
function(ecm_generate_python_bindings)
|
||||
set(options )
|
||||
set(oneValueArgs PACKAGE_NAME WRAPPED_HEADER TYPESYSTEM VERSION QT_VERSION HOMEPAGE_URL ISSUES_URL AUTHOR README)
|
||||
set(multiValueArgs GENERATED_SOURCES DEPENDENCIES)
|
||||
|
||||
cmake_parse_arguments(PB "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Ugly hacks because PySide6::pyside6 only includes /usr/includes/PySide6 and none of the sub directory
|
||||
# Qt bugreport: PYSIDE-2882
|
||||
get_property(PYSIDE_INCLUDE_DIRS TARGET "PySide6::pyside6" PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
foreach(PYSIDE_INCLUDE_DIR ${PYSIDE_INCLUDE_DIRS})
|
||||
file(GLOB PYSIDE_SUBDIRS LIST_DIRECTORIES true "${PYSIDE_INCLUDE_DIR}/*")
|
||||
foreach (PYSIDE_SUBDIR ${PYSIDE_SUBDIRS})
|
||||
if (IS_DIRECTORY ${PYSIDE_SUBDIR})
|
||||
set_property(TARGET PySide6::pyside6
|
||||
APPEND
|
||||
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
|
||||
${PYSIDE_SUBDIR}
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
list(APPEND PB_DEPENDENCIES PySide6::pyside6)
|
||||
list(APPEND PB_DEPENDENCIES Shiboken6::libshiboken)
|
||||
|
||||
# Get the relevant include dirs, to pass them on to shiboken.
|
||||
set(INCLUDES "")
|
||||
|
||||
if(WIN32)
|
||||
set(PATH_SEP "\;")
|
||||
else()
|
||||
set(PATH_SEP ":")
|
||||
endif()
|
||||
|
||||
macro(make_path varname)
|
||||
# accepts any number of path variables
|
||||
string(REPLACE ";" "${PATH_SEP}" ${varname} "${ARGN}")
|
||||
endmacro()
|
||||
|
||||
foreach(_dependency ${PB_DEPENDENCIES})
|
||||
get_property(DEPENDENCY_INCLUDE_DIRS TARGET "${_dependency}" PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
|
||||
|
||||
make_path(_include_dirs $<JOIN:$<TARGET_PROPERTY:${_dependency},INTERFACE_INCLUDE_DIRECTORIES>,${PATH_SEP}>)
|
||||
list(APPEND INCLUDES "--include-paths=${_include_dirs}")
|
||||
endforeach()
|
||||
|
||||
# Set up the options to pass to shiboken.
|
||||
set(shiboken_options --enable-pyside-extensions
|
||||
${INCLUDES}
|
||||
--include-paths=${CMAKE_SOURCE_DIR}
|
||||
--typesystem-paths=${CMAKE_SOURCE_DIR}
|
||||
--typesystem-paths=${PYSIDE_TYPESYSTEMS}
|
||||
--output-directory=${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(generated_sources_dependencies ${PB_WRAPPED_HEADER} ${PB_TYPESYSTEM})
|
||||
|
||||
# Add custom target to run shiboken to generate the binding cpp files.
|
||||
add_custom_command(
|
||||
OUTPUT ${PB_GENERATED_SOURCES}
|
||||
COMMAND shiboken6 ${shiboken_options} ${PB_WRAPPED_HEADER} ${PB_TYPESYSTEM}
|
||||
DEPENDS ${generated_sources_dependencies}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Running generator for ${PB_TYPESYSTEM}"
|
||||
)
|
||||
|
||||
# Set the cpp files which will be used for the bindings library.
|
||||
set(${PB_PACKAGE_NAME}_sources ${PB_GENERATED_SOURCES})
|
||||
|
||||
# PySide6 uses deprecated code.
|
||||
get_property(_defs DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS)
|
||||
list(FILTER _defs EXCLUDE REGEX [[^QT_DISABLE_DEPRECATED_BEFORE=]])
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS ${_defs})
|
||||
get_property(_defs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS)
|
||||
list(FILTER _defs EXCLUDE REGEX [[^QT_DISABLE_DEPRECATED_BEFORE=]])
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS ${_defs})
|
||||
|
||||
# Define and build the bindings library.
|
||||
add_library(${PB_PACKAGE_NAME} SHARED ${${PB_PACKAGE_NAME}_sources})
|
||||
|
||||
target_link_libraries(${PB_PACKAGE_NAME} PRIVATE
|
||||
PySide6::pyside6
|
||||
Shiboken6::libshiboken
|
||||
${Python3_LIBRARIES}
|
||||
)
|
||||
|
||||
# Apply relevant include and link flags.
|
||||
target_include_directories(${PB_PACKAGE_NAME} PRIVATE
|
||||
${PYSIDE_PYTHONPATH}/include
|
||||
${SHIBOKEN_PYTHON_INCLUDE_DIRS}
|
||||
$<TARGET_PROPERTY:PySide6::pyside6,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:Shiboken6::libshiboken,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
)
|
||||
|
||||
# Hide noisy warnings
|
||||
target_compile_options(${PB_PACKAGE_NAME} PRIVATE -Wno-cast-function-type -Wno-missing-include-dirs)
|
||||
|
||||
# Adjust the name of generated module.
|
||||
set_property(TARGET ${PB_PACKAGE_NAME} PROPERTY PREFIX "")
|
||||
set_property(TARGET ${PB_PACKAGE_NAME} PROPERTY LIBRARY_OUTPUT_NAME "${PB_PACKAGE_NAME}.${Python3_SOABI}")
|
||||
set_property(TARGET ${PB_PACKAGE_NAME} PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${PB_PACKAGE_NAME}/build/lib)
|
||||
|
||||
# Build Python Wheel
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PB_PACKAGE_NAME}/${PB_PACKAGE_NAME}")
|
||||
configure_file("${MODULES_DIR}/ECMGeneratePythonBindings.toml.in" "${CMAKE_CURRENT_BINARY_DIR}/${PB_PACKAGE_NAME}/pyproject.toml")
|
||||
configure_file(${PB_README} "${CMAKE_CURRENT_BINARY_DIR}/${PB_PACKAGE_NAME}/README.md" COPYONLY)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${PB_PACKAGE_NAME}
|
||||
POST_BUILD
|
||||
COMMAND Python3::Interpreter -m build --wheel --no-isolation
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PB_PACKAGE_NAME}"
|
||||
COMMENT "Building Python Wheel"
|
||||
)
|
||||
|
||||
endfunction()
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
[project]
|
||||
name = "${PB_PACKAGE_NAME}"
|
||||
version = "${PB_VERSION}"
|
||||
authors = [
|
||||
{ name="${PB_AUTHOR}" },
|
||||
]
|
||||
description = "Python bindings for ${PB_PACKAGE_NAME}"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"PySide6>=${PB_QT_VERSION},<7",
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: X11 Applications :: KDE",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "${PB_HOMEPAGE_URL}"
|
||||
Issues = "${PB_ISSUES_URL}"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2024 Manuel Alcaraz Zambrano <manuelalcarazzam@gmail.com>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
@@ -0,0 +1,60 @@
|
||||
# SPDX-FileCopyrightText: 2017 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMGenerateQmlTypes
|
||||
-------------------
|
||||
|
||||
Generates plugins.qmltypes files for QML plugins.
|
||||
|
||||
::
|
||||
|
||||
ecm_generate_qmltypes(<org.kde.pluginname> 1.3
|
||||
DESTINATION <${KDE_INSTALL_QMLDIR}/org/kde/pluginname>)
|
||||
|
||||
Makes it possible to generate plugins.qmltypes files for the QML plugins that
|
||||
our project offers. These files offer introspection upon our plugin and are
|
||||
useful for integrating with IDE language support of our plugin. It offers
|
||||
information about the objects its methods and their argument types.
|
||||
|
||||
The developer will be in charge of making sure that these files are up to date.
|
||||
The plugin.qmltypes file will sit in the source directory. This function will
|
||||
include the code that installs the file in the right place and a small unit
|
||||
test named qmltypes-pluginname-version that makes sure that it doesn't need updating.
|
||||
|
||||
|
||||
Since 5.33.0
|
||||
#]=======================================================================]
|
||||
|
||||
function(ecm_generate_qmltypes)
|
||||
if (NOT TARGET qmltypes)
|
||||
add_custom_target(qmltypes)
|
||||
endif ()
|
||||
|
||||
set(options)
|
||||
set(oneValueArgs DESTINATION TEST_ENABLED)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "" ${ARGN})
|
||||
|
||||
set(targetname "qmltypes-${ARG_UNPARSED_ARGUMENTS}")
|
||||
string(REPLACE ";" - targetname "${targetname}")
|
||||
|
||||
set(generatedFile ${CMAKE_CURRENT_SOURCE_DIR}/plugins.qmltypes)
|
||||
add_custom_target(${targetname}
|
||||
BYPRODUCTS ${generatedFile}
|
||||
COMMAND qmlplugindump -nonrelocatable ${ARG_UNPARSED_ARGUMENTS} ${KDE_INSTALL_QMLDIR} > ${generatedFile}
|
||||
)
|
||||
add_dependencies(qmltypes ${targetname})
|
||||
if (EXISTS ${generatedFile})
|
||||
install(FILES ${generatedFile} DESTINATION "${ARG_DESTINATION}")
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" / processedArgs "${ARG_UNPARSED_ARGUMENTS}")
|
||||
|
||||
# sometimes qmlplugindump output isn't reproducible, we better have it opt in for now
|
||||
if(ARG_TEST_ENABLED)
|
||||
add_test(NAME ${targetname} COMMAND
|
||||
cmake -DARG_UNPARSED_ARGUMENTS=${processedArgs} -DKDE_INSTALL_QMLDIR=${KDE_INSTALL_QMLDIR} -DINPUT=${generatedFile} -P ${ECM_MODULE_DIR}/../test-modules/test_execute_and_compare.cmake
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,274 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2013 David Edmundson <kde@davidedmundson.co.uk>
|
||||
# SPDX-FileCopyrightText: 2008 Chusslove Illich <caslav.ilic@gmx.net>
|
||||
# SPDX-FileCopyrightText: 2006 Alex Neundorf <neundorf@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMInstallIcons
|
||||
---------------
|
||||
|
||||
Installs icons, sorting them into the correct directories according to the
|
||||
FreeDesktop.org icon naming specification.
|
||||
|
||||
::
|
||||
|
||||
ecm_install_icons(ICONS <icon> [<icon> [...]]
|
||||
DESTINATION <icon_install_dir>
|
||||
[LANG <l10n_code>]
|
||||
[THEME <theme>])
|
||||
|
||||
The given icons, whose names must match the pattern::
|
||||
|
||||
<size>-<group>-<name>.<ext>
|
||||
|
||||
will be installed to the appropriate subdirectory of ``DESTINATION`` according to
|
||||
the FreeDesktop.org icon naming scheme. By default, they are installed to the
|
||||
"hicolor" theme, but this can be changed using the ``THEME`` argument. If the
|
||||
icons are localized, the LANG argument can be used to install them in a
|
||||
locale-specific directory.
|
||||
|
||||
``<size>`` is a numeric pixel size (typically 16, 22, 32, 48, 64, 128 or 256)
|
||||
or ``sc`` for scalable (SVG) files, ``<group>`` is one of the standard
|
||||
FreeDesktop.org icon groups (actions, animations, apps, categories, devices,
|
||||
emblems, emotes, intl, mimetypes, places, status) and ``<ext>`` is one of
|
||||
``.png``, ``.mng`` or ``.svgz``.
|
||||
|
||||
The typical installation directory is ``share/icons``.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_install_icons(ICONS 22-actions-menu_new.png
|
||||
DESTINATION share/icons)
|
||||
|
||||
The above code will install the file ``22-actions-menu_new.png`` as
|
||||
``${CMAKE_INSTALL_PREFIX}/share/icons/<theme>/22x22/actions/menu_new.png``
|
||||
|
||||
Users of the :kde-module:`KDEInstallDirs` module would normally use
|
||||
``${KDE_INSTALL_ICONDIR}`` as the DESTINATION, while users of the GNUInstallDirs
|
||||
module should use ``${CMAKE_INSTALL_DATAROOTDIR}/icons``.
|
||||
|
||||
An old form of arguments will also be accepted::
|
||||
|
||||
ecm_install_icons(<icon_install_dir> [<l10n_code>])
|
||||
|
||||
This matches files named like::
|
||||
|
||||
<theme><size>-<group>-<name>.<ext>
|
||||
|
||||
where ``<theme>`` is one of
|
||||
|
||||
* ``hi`` for hicolor
|
||||
* ``lo`` for locolor
|
||||
* ``cr`` for the Crystal icon theme
|
||||
* ``ox`` for the Oxygen icon theme
|
||||
* ``br`` for the Breeze icon theme
|
||||
|
||||
With this syntax, the file ``hi22-actions-menu_new.png`` would be installed
|
||||
into ``<icon_install_dir>/hicolor/22x22/actions/menu_new.png``
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
# A "map" of short type names to the directories.
|
||||
# Unknown names produce a warning.
|
||||
set(_ECM_ICON_GROUP_mimetypes "mimetypes")
|
||||
set(_ECM_ICON_GROUP_places "places")
|
||||
set(_ECM_ICON_GROUP_devices "devices")
|
||||
set(_ECM_ICON_GROUP_apps "apps")
|
||||
set(_ECM_ICON_GROUP_actions "actions")
|
||||
set(_ECM_ICON_GROUP_categories "categories")
|
||||
set(_ECM_ICON_GROUP_status "status")
|
||||
set(_ECM_ICON_GROUP_emblems "emblems")
|
||||
set(_ECM_ICON_GROUP_emotes "emotes")
|
||||
set(_ECM_ICON_GROUP_animations "animations")
|
||||
set(_ECM_ICON_GROUP_intl "intl")
|
||||
|
||||
# For the "compatibility" syntax: a "map" of short theme names to the theme
|
||||
# directory
|
||||
set(_ECM_ICON_THEME_br "breeze")
|
||||
set(_ECM_ICON_THEME_ox "oxygen")
|
||||
set(_ECM_ICON_THEME_cr "crystalsvg")
|
||||
set(_ECM_ICON_THEME_lo "locolor")
|
||||
set(_ECM_ICON_THEME_hi "hicolor")
|
||||
|
||||
macro(_ecm_install_icons_v1 _defaultpath)
|
||||
# the l10n-subdir if language given as second argument (localized icon)
|
||||
set(_lang ${ARGV1})
|
||||
if(_lang)
|
||||
set(_l10n_SUBDIR l10n/${_lang})
|
||||
else()
|
||||
set(_l10n_SUBDIR ".")
|
||||
endif()
|
||||
|
||||
set(_themes)
|
||||
|
||||
# first the png icons
|
||||
file(GLOB _icons *.png)
|
||||
foreach (_current_ICON ${_icons} )
|
||||
# since CMake 2.6 regex matches are stored in special variables CMAKE_MATCH_x, if it didn't match, they are empty
|
||||
string(REGEX MATCH "^.*/([a-zA-Z]+)([0-9]+)\\-([a-z]+)\\-(.+\\.png)$" _dummy "${_current_ICON}")
|
||||
set(_type "${CMAKE_MATCH_1}")
|
||||
set(_size "${CMAKE_MATCH_2}")
|
||||
set(_group "${CMAKE_MATCH_3}")
|
||||
set(_name "${CMAKE_MATCH_4}")
|
||||
|
||||
set(_theme_GROUP ${_ECM_ICON_THEME_${_type}})
|
||||
if( _theme_GROUP)
|
||||
list(APPEND _themes "${_theme_GROUP}")
|
||||
_ECM_ADD_ICON_INSTALL_RULE(${CMAKE_CURRENT_BINARY_DIR}/install_icons.cmake
|
||||
${_defaultpath}/${_theme_GROUP}/${_size}x${_size}
|
||||
${_group} ${_current_ICON} ${_name} ${_l10n_SUBDIR})
|
||||
endif()
|
||||
endforeach (_current_ICON)
|
||||
|
||||
# mng icons
|
||||
file(GLOB _icons *.mng)
|
||||
foreach (_current_ICON ${_icons} )
|
||||
# since CMake 2.6 regex matches are stored in special variables CMAKE_MATCH_x, if it didn't match, they are empty
|
||||
string(REGEX MATCH "^.*/([a-zA-Z]+)([0-9]+)\\-([a-z]+)\\-(.+\\.mng)$" _dummy "${_current_ICON}")
|
||||
set(_type "${CMAKE_MATCH_1}")
|
||||
set(_size "${CMAKE_MATCH_2}")
|
||||
set(_group "${CMAKE_MATCH_3}")
|
||||
set(_name "${CMAKE_MATCH_4}")
|
||||
|
||||
set(_theme_GROUP ${_ECM_ICON_THEME_${_type}})
|
||||
if( _theme_GROUP)
|
||||
list(APPEND _themes "${_theme_GROUP}")
|
||||
_ECM_ADD_ICON_INSTALL_RULE(${CMAKE_CURRENT_BINARY_DIR}/install_icons.cmake
|
||||
${_defaultpath}/${_theme_GROUP}/${_size}x${_size}
|
||||
${_group} ${_current_ICON} ${_name} ${_l10n_SUBDIR})
|
||||
endif()
|
||||
endforeach (_current_ICON)
|
||||
|
||||
# and now the svg icons
|
||||
file(GLOB _icons *.svgz)
|
||||
foreach (_current_ICON ${_icons} )
|
||||
# since CMake 2.6 regex matches are stored in special variables CMAKE_MATCH_x, if it didn't match, they are empty
|
||||
string(REGEX MATCH "^.*/([a-zA-Z]+)sc\\-([a-z]+)\\-(.+\\.svgz)$" _dummy "${_current_ICON}")
|
||||
set(_type "${CMAKE_MATCH_1}")
|
||||
set(_group "${CMAKE_MATCH_2}")
|
||||
set(_name "${CMAKE_MATCH_3}")
|
||||
|
||||
set(_theme_GROUP ${_ECM_ICON_THEME_${_type}})
|
||||
if( _theme_GROUP)
|
||||
list(APPEND _themes "${_theme_GROUP}")
|
||||
_ECM_ADD_ICON_INSTALL_RULE(${CMAKE_CURRENT_BINARY_DIR}/install_icons.cmake
|
||||
${_defaultpath}/${_theme_GROUP}/scalable
|
||||
${_group} ${_current_ICON} ${_name} ${_l10n_SUBDIR})
|
||||
endif()
|
||||
endforeach (_current_ICON)
|
||||
|
||||
if (_themes)
|
||||
list(REMOVE_DUPLICATES _themes)
|
||||
foreach(_theme ${_themes})
|
||||
_ecm_update_iconcache("${_defaultpath}" "${_theme}")
|
||||
endforeach()
|
||||
else()
|
||||
message(AUTHOR_WARNING "No suitably-named icons found")
|
||||
endif()
|
||||
|
||||
endmacro()
|
||||
|
||||
# only used internally by _ecm_install_icons_v1
|
||||
macro(_ecm_add_icon_install_rule _install_SCRIPT _install_PATH _group _orig_NAME _install_NAME _l10n_SUBDIR)
|
||||
|
||||
# if the string doesn't match the pattern, the result is the full string, so all three have the same content
|
||||
if (NOT ${_group} STREQUAL ${_install_NAME} )
|
||||
set(_icon_GROUP ${_ECM_ICON_GROUP_${_group}})
|
||||
if(NOT _icon_GROUP)
|
||||
message(WARNING "Icon ${_install_NAME} uses invalid category ${_group}, setting to 'actions'")
|
||||
set(_icon_GROUP "actions")
|
||||
endif()
|
||||
# message(STATUS "icon: ${_current_ICON} size: ${_size} group: ${_group} name: ${_name} l10n: ${_l10n_SUBDIR}")
|
||||
install(FILES ${_orig_NAME} DESTINATION ${_install_PATH}/${_icon_GROUP}/${_l10n_SUBDIR}/ RENAME ${_install_NAME} )
|
||||
endif (NOT ${_group} STREQUAL ${_install_NAME} )
|
||||
|
||||
endmacro()
|
||||
|
||||
# Updates the mtime of the icon theme directory, so caches that
|
||||
# watch for changes to the directory will know to update.
|
||||
# If present, this also runs gtk-update-icon-cache (which despite the name is also used by Qt).
|
||||
function(_ecm_update_iconcache installdir theme)
|
||||
find_program(GTK_UPDATE_ICON_CACHE_EXECUTABLE NAMES gtk-update-icon-cache)
|
||||
# We don't always have touch command (e.g. on Windows), so instead
|
||||
# create and delete a temporary file in the theme dir.
|
||||
install(CODE "
|
||||
set(DESTDIR_VALUE \"\$ENV{DESTDIR}\")
|
||||
if (NOT DESTDIR_VALUE)
|
||||
execute_process(COMMAND \"${CMAKE_COMMAND}\" -E touch \"${CMAKE_INSTALL_PREFIX}/${installdir}/${theme}\")
|
||||
set(HAVE_GTK_UPDATE_ICON_CACHE_EXEC ${GTK_UPDATE_ICON_CACHE_EXECUTABLE})
|
||||
if (HAVE_GTK_UPDATE_ICON_CACHE_EXEC)
|
||||
execute_process(COMMAND ${GTK_UPDATE_ICON_CACHE_EXECUTABLE} -q -t -i . WORKING_DIRECTORY \"${CMAKE_INSTALL_PREFIX}/${installdir}/${theme}\")
|
||||
endif ()
|
||||
endif (NOT DESTDIR_VALUE)
|
||||
")
|
||||
endfunction()
|
||||
|
||||
function(ecm_install_icons)
|
||||
set(options)
|
||||
set(oneValueArgs DESTINATION LANG THEME)
|
||||
set(multiValueArgs ICONS)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT ARG_ICONS AND NOT ARG_DESTINATION)
|
||||
message(AUTHOR_WARNING "ecm_install_icons() with no ICONS argument is deprecated")
|
||||
_ecm_install_icons_v1(${ARGN})
|
||||
return()
|
||||
endif()
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_install_icons: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ARG_DESTINATION)
|
||||
message(FATAL_ERROR "No DESTINATION argument given to ecm_install_icons")
|
||||
endif()
|
||||
if(NOT ARG_THEME)
|
||||
set(ARG_THEME "hicolor")
|
||||
endif()
|
||||
if(ARG_LANG)
|
||||
set(l10n_subdir "l10n/${ARG_LANG}/")
|
||||
endif()
|
||||
|
||||
foreach(icon ${ARG_ICONS})
|
||||
get_filename_component(filename "${icon}" NAME)
|
||||
string(REGEX MATCH "([0-9sc]+)\\-([a-z]+)\\-([^/]+)\\.([a-z]+)$"
|
||||
complete_match "${filename}")
|
||||
set(size "${CMAKE_MATCH_1}")
|
||||
set(group "${CMAKE_MATCH_2}")
|
||||
set(name "${CMAKE_MATCH_3}")
|
||||
set(ext "${CMAKE_MATCH_4}")
|
||||
if(NOT size OR NOT group OR NOT name OR NOT ext)
|
||||
message(WARNING "${icon} is not named correctly for ecm_install_icons - ignoring")
|
||||
elseif(NOT size STREQUAL "sc" AND NOT size GREATER 0)
|
||||
message(WARNING "${icon} size (${size}) is invalid - ignoring")
|
||||
else()
|
||||
if (NOT complete_match STREQUAL filename)
|
||||
# We can't stop accepting filenames with leading characters,
|
||||
# because that would break existing projects, so just warn
|
||||
# about them instead.
|
||||
message(AUTHOR_WARNING "\"${icon}\" has characters before the size; it should be renamed to \"${size}-${group}-${name}.${ext}\"")
|
||||
endif()
|
||||
if(NOT _ECM_ICON_GROUP_${group})
|
||||
message(WARNING "${icon} group (${group}) is not recognized")
|
||||
endif()
|
||||
if(size STREQUAL "sc")
|
||||
if(NOT ext STREQUAL "svg" AND NOT ext STREQUAL "svgz")
|
||||
message(WARNING "Scalable icon ${icon} is not SVG or SVGZ")
|
||||
endif()
|
||||
set(size_dir "scalable")
|
||||
else()
|
||||
if(NOT ext STREQUAL "png" AND NOT ext STREQUAL "mng" AND NOT ext STREQUAL "svg" AND NOT ext STREQUAL "svgz")
|
||||
message(WARNING "Fixed-size icon ${icon} is not PNG/MNG/SVG/SVGZ")
|
||||
endif()
|
||||
set(size_dir "${size}x${size}")
|
||||
endif()
|
||||
install(
|
||||
FILES "${icon}"
|
||||
DESTINATION "${ARG_DESTINATION}/${ARG_THEME}/${size_dir}/${group}/${l10n_subdir}"
|
||||
RENAME "${name}.${ext}"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
_ecm_update_iconcache("${ARG_DESTINATION}" "${ARG_THEME}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,41 @@
|
||||
# SPDX-FileCopyrightText: 2012 Stephen Kelly <steveire@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2012 Alex Neundorf <neundorf@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMMarkAsTest
|
||||
-------------
|
||||
|
||||
Marks a target as only being required for tests.
|
||||
|
||||
::
|
||||
|
||||
ecm_mark_as_test(<target1> [<target2> [...]])
|
||||
|
||||
This will cause the specified targets to not be built unless either
|
||||
``BUILD_TESTING`` is set to ``ON`` or the user invokes the ``buildtests`` target.
|
||||
|
||||
``BUILD_TESTING`` is created as a cache variable by the CTest module and by the
|
||||
:kde-module:`KDECMakeSettings` module.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
if (NOT BUILD_TESTING)
|
||||
if(NOT TARGET buildtests)
|
||||
add_custom_target(buildtests)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(ecm_mark_as_test)
|
||||
if (NOT BUILD_TESTING)
|
||||
foreach(_target ${ARGN})
|
||||
set_target_properties(${_target}
|
||||
PROPERTIES
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
)
|
||||
add_dependencies(buildtests ${_target})
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2012 Stephen Kelly <steveire@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMMarkNonGuiExecutable
|
||||
-----------------------
|
||||
|
||||
Marks an executable target as not being a GUI application.
|
||||
|
||||
::
|
||||
|
||||
ecm_mark_nongui_executable(<target1> [<target2> [...]])
|
||||
|
||||
This will indicate to CMake that the specified targets should not be included
|
||||
in a MACOSX_BUNDLE and should not be WIN32_EXECUTABLEs. On platforms other
|
||||
than MacOS X or Windows, this will have no effect.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ecm_mark_nongui_executable)
|
||||
foreach(_target ${ARGN})
|
||||
set_target_properties(${_target}
|
||||
PROPERTIES
|
||||
WIN32_EXECUTABLE FALSE
|
||||
MACOSX_BUNDLE FALSE
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# SPDX-FileCopyrightText: 2007 Alexander Neundorf <neundorf@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMOptionalAddSubdirectory
|
||||
--------------------------
|
||||
|
||||
Make subdirectories optional.
|
||||
|
||||
::
|
||||
|
||||
ecm_optional_add_subdirectory(<dir>)
|
||||
|
||||
This behaves like ``add_subdirectory()``, except that it does not complain if the
|
||||
directory does not exist. Additionally, if the directory does exist, it
|
||||
creates an option to allow the user to skip it. The option will be named
|
||||
BUILD_<dir>.
|
||||
|
||||
This is useful for "meta-projects" that combine several mostly-independent
|
||||
sub-projects.
|
||||
|
||||
If the CMake variable ``DISABLE_ALL_OPTIONAL_SUBDIRECTORIES`` is set to ``TRUE`` for
|
||||
the first CMake run on the project, all optional subdirectories will be
|
||||
disabled by default (but can of course be enabled via the respective options).
|
||||
For example, the following will disable all optional subdirectories except the
|
||||
one named "foo":
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
cmake -DDISABLE_ALL_OPTIONAL_SUBDIRECTORIES=TRUE -DBUILD_foo=TRUE myproject
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ECM_OPTIONAL_ADD_SUBDIRECTORY _dir)
|
||||
get_filename_component(_fullPath ${_dir} ABSOLUTE)
|
||||
if(EXISTS ${_fullPath}/CMakeLists.txt)
|
||||
if(DISABLE_ALL_OPTIONAL_SUBDIRECTORIES)
|
||||
set(_DEFAULT_OPTION_VALUE FALSE)
|
||||
else()
|
||||
set(_DEFAULT_OPTION_VALUE TRUE)
|
||||
endif()
|
||||
if(DISABLE_ALL_OPTIONAL_SUBDIRS AND NOT DEFINED BUILD_${_dir})
|
||||
set(_DEFAULT_OPTION_VALUE FALSE)
|
||||
endif()
|
||||
option(BUILD_${_dir} "Build directory ${_dir}" ${_DEFAULT_OPTION_VALUE})
|
||||
if(BUILD_${_dir})
|
||||
add_subdirectory(${_dir})
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kdemail.net>
|
||||
# SPDX-FileCopyrightText: 2013 Stephen Kelly <steveire@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMPackageConfigHelpers
|
||||
-----------------------
|
||||
|
||||
Helper macros for generating CMake package config files.
|
||||
|
||||
``write_basic_package_version_file()`` is the same as the one provided by the
|
||||
`CMakePackageConfigHelpers
|
||||
<https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
|
||||
module in CMake; see that module's documentation for
|
||||
more information.
|
||||
|
||||
::
|
||||
|
||||
ecm_configure_package_config_file(<input> <output>
|
||||
INSTALL_DESTINATION <path>
|
||||
[PATH_VARS <var1> [<var2> [...]]
|
||||
[NO_SET_AND_CHECK_MACRO]
|
||||
[NO_CHECK_REQUIRED_COMPONENTS_MACRO])
|
||||
|
||||
|
||||
This behaves in the same way as ``configure_package_config_file()`` from CMake
|
||||
2.8.12, except that it adds an extra helper macro: ``find_dependency()``. It is
|
||||
highly recommended that you read the `documentation for
|
||||
CMakePackageConfigHelpers
|
||||
<https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
|
||||
for more information, particularly with regard to the ``PATH_VARS`` argument.
|
||||
|
||||
Note that there is no argument that will disable the ``find_dependency()`` macro;
|
||||
if you do not require this macro, you should use
|
||||
``configure_package_config_file`` from the CMakePackageConfigHelpers module.
|
||||
|
||||
CMake 3.0 includes a CMakeFindDependencyMacro module that provides the
|
||||
``find_dependency()`` macro (which you can ``include()`` in your package config
|
||||
file), so this file is only useful for projects wishing to provide config
|
||||
files that will work with CMake 2.8.12.
|
||||
|
||||
Additional Config File Macros
|
||||
=============================
|
||||
|
||||
::
|
||||
|
||||
find_dependency(<dep> [<version> [EXACT]])
|
||||
|
||||
``find_dependency()`` should be used instead of ``find_package()`` to find package
|
||||
dependencies. It forwards the correct parameters for ``EXACT``, ``QUIET`` and
|
||||
``REQUIRED`` which were passed to the original ``find_package()`` call. It also sets
|
||||
an informative diagnostic message if the dependency could not be found.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_ROOT}/Modules/CMakePackageConfigHelpers.cmake)
|
||||
|
||||
set(_ecm_package_config_helpers_included TRUE)
|
||||
|
||||
message(AUTHOR_WARNING "Your project already requires a version of CMake that includes the find_dependency macro via the CMakeFindDependencyMacro module. You should use CMakePackageConfigHelpers instead of ECMPackageConfigHelpers.")
|
||||
|
||||
function(ECM_CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
|
||||
set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||
set(oneValueArgs INSTALL_DESTINATION )
|
||||
set(multiValueArgs PATH_VARS )
|
||||
|
||||
cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(CCF_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT CCF_INSTALL_DESTINATION)
|
||||
message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
|
||||
endif()
|
||||
|
||||
if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
|
||||
set(absInstallDir "${CCF_INSTALL_DESTINATION}")
|
||||
else()
|
||||
set(absInstallDir "${CMAKE_INSTALL_PREFIX}/${CCF_INSTALL_DESTINATION}")
|
||||
endif()
|
||||
|
||||
file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${CMAKE_INSTALL_PREFIX}" )
|
||||
|
||||
foreach(var ${CCF_PATH_VARS})
|
||||
if(NOT DEFINED ${var})
|
||||
message(FATAL_ERROR "Variable ${var} does not exist")
|
||||
else()
|
||||
if(IS_ABSOLUTE "${${var}}")
|
||||
string(REPLACE "${CMAKE_INSTALL_PREFIX}" "\${PACKAGE_PREFIX_DIR}"
|
||||
PACKAGE_${var} "${${var}}")
|
||||
else()
|
||||
set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
get_filename_component(inputFileName "${_inputFile}" NAME)
|
||||
|
||||
set(PACKAGE_INIT "
|
||||
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() (ECM variant) #######
|
||||
####### Any changes to this file will be overwritten by the next CMake run #######
|
||||
####### The input file was ${inputFileName} #######
|
||||
|
||||
get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
|
||||
")
|
||||
|
||||
if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
|
||||
# Handle "/usr move" symlinks created by some Linux distros.
|
||||
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||
# Use original install prefix when loaded through a \"/usr move\"
|
||||
# cross-prefix symbolic link such as /lib -> /usr/lib.
|
||||
get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
|
||||
get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
|
||||
if(_realCurr STREQUAL _realOrig)
|
||||
set(PACKAGE_PREFIX_DIR \"${CMAKE_INSTALL_PREFIX}\")
|
||||
endif()
|
||||
unset(_realOrig)
|
||||
unset(_realCurr)
|
||||
")
|
||||
endif()
|
||||
|
||||
if(NOT CCF_NO_SET_AND_CHECK_MACRO)
|
||||
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||
macro(set_and_check _var _file)
|
||||
set(\${_var} \"\${_file}\")
|
||||
if(NOT EXISTS \"\${_file}\")
|
||||
message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
include(CMakeFindDependencyMacro OPTIONAL RESULT_VARIABLE _CMakeFindDependencyMacro_FOUND)
|
||||
|
||||
if (NOT _CMakeFindDependencyMacro_FOUND)
|
||||
macro(find_dependency dep)
|
||||
if (NOT \${dep}_FOUND)
|
||||
|
||||
set(ecm_fd_version)
|
||||
if (\${ARGC} GREATER 1)
|
||||
set(ecm_fd_version \${ARGV1})
|
||||
endif()
|
||||
set(ecm_fd_exact_arg)
|
||||
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT)
|
||||
set(ecm_fd_exact_arg EXACT)
|
||||
endif()
|
||||
set(ecm_fd_quiet_arg)
|
||||
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
|
||||
set(ecm_fd_quiet_arg QUIET)
|
||||
endif()
|
||||
set(ecm_fd_required_arg)
|
||||
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
||||
set(ecm_fd_required_arg REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(\${dep} \${ecm_fd_version}
|
||||
\${ecm_fd_exact_arg}
|
||||
\${ecm_fd_quiet_arg}
|
||||
\${ecm_fd_required_arg}
|
||||
)
|
||||
|
||||
if (NOT \${dep}_FOUND)
|
||||
set(\${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE \"\${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency \${dep} could not be found.\")
|
||||
set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(ecm_fd_version)
|
||||
set(ecm_fd_required_arg)
|
||||
set(ecm_fd_quiet_arg)
|
||||
set(ecm_fd_exact_arg)
|
||||
endif()
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
")
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||
macro(check_required_components _NAME)
|
||||
foreach(comp \${\${_NAME}_FIND_COMPONENTS})
|
||||
if(NOT \${_NAME}_\${comp}_FOUND)
|
||||
if(\${_NAME}_FIND_REQUIRED_\${comp})
|
||||
set(\${_NAME}_FOUND FALSE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
")
|
||||
endif()
|
||||
|
||||
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||
####################################################################################")
|
||||
|
||||
configure_file("${_inputFile}" "${_outputFile}" @ONLY)
|
||||
|
||||
endfunction()
|
||||
@@ -0,0 +1,253 @@
|
||||
# SPDX-FileCopyrightText: 2007-2009 Kitware, Inc.
|
||||
# SPDX-FileCopyrightText: 2007 Alexander Neundorf <neundorf@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMPoQmTools
|
||||
------------
|
||||
|
||||
This module provides the ``ecm_process_po_files_as_qm`` and
|
||||
``ecm_install_po_files_as_qm`` functions for generating QTranslator (.qm)
|
||||
catalogs from Gettext (.po) catalogs, and the ``ecm_create_qm_loader``
|
||||
function for generating the necessary code to load them in a Qt application
|
||||
or library.
|
||||
|
||||
::
|
||||
|
||||
ecm_process_po_files_as_qm(<lang> [ALL]
|
||||
[INSTALL_DESTINATION <install_destination>]
|
||||
PO_FILES <pofile> [<pofile> [...]])
|
||||
|
||||
Compile .po files into .qm files for the given language.
|
||||
|
||||
If ``INSTALL_DESTINATION`` is given, the .qm files are installed in
|
||||
``<install_destination>/<lang>/LC_MESSAGES``. Typically,
|
||||
``<install_destination>`` is set to ``share/locale``.
|
||||
|
||||
``ecm_process_po_files_as_qm`` creates a "translations" target. This target
|
||||
builds all .po files into .qm files. If ``ALL`` is specified, these rules are
|
||||
added to the "all" target (and so the .qm files will be built by default).
|
||||
|
||||
::
|
||||
|
||||
ecm_create_qm_loader(<sources_var_name(|target (since 5.83))> <catalog_name>)
|
||||
|
||||
Generates C++ code which ensures translations are automatically loaded at
|
||||
startup. The generated files are appended to the variable named
|
||||
``<sources_var_name>`` or, if the first argument is a target (since 5.83), to
|
||||
the ``SOURCES`` property of ``<target>``. Any target must be created with
|
||||
``add_executable()`` or ``add_library()`` and not be an alias.
|
||||
|
||||
It assumes that the .qm file for the language code ``<lang>`` is installed as
|
||||
``<sharedir>/locale/<lang>/LC_MESSAGES/<catalog_name>.qm``, where
|
||||
``<sharedir>`` is one of the directories given by the ``GenericDataLocation``
|
||||
of ``QStandardPaths``.
|
||||
|
||||
Typical usage is like:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set(mylib_SRCS foo.cpp bar.cpp)
|
||||
ecm_create_qm_loader(mylib_SRCS mycatalog)
|
||||
add_library(mylib ${mylib_SRCS})
|
||||
|
||||
# Or, since 5.83:
|
||||
add_library(mylib foo.cpp bar.cpp)
|
||||
ecm_create_qm_loader(mylib mycatalog)
|
||||
|
||||
::
|
||||
|
||||
ecm_install_po_files_as_qm(<podir>)
|
||||
|
||||
Searches for .po files and installs them to the standard location.
|
||||
|
||||
This is a convenience function which relies on all .po files being kept in
|
||||
``<podir>/<lang>/``, where ``<lang>`` is the language the .po files are
|
||||
written in.
|
||||
|
||||
For example, given the following directory structure::
|
||||
|
||||
po/
|
||||
fr/
|
||||
mylib.po
|
||||
|
||||
``ecm_install_po_files_as_qm(po)`` compiles ``mylib.po`` into ``mylib.qm`` and
|
||||
installs it in ``<install_destination>/fr/LC_MESSAGES``.
|
||||
``<install_destination>`` defaults to ``${LOCALE_INSTALL_DIR}`` if defined,
|
||||
otherwise it uses ``${CMAKE_INSTALL_LOCALEDIR}`` if that is defined, otherwise
|
||||
it uses ``share/locale``.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||
option(KF_SKIP_PO_PROCESSING "Skip processing of po files" OFF)
|
||||
|
||||
# Copied from FindGettext.cmake
|
||||
function(_ecm_qm_get_unique_target_name _name _unique_name)
|
||||
set(propertyName "_ECM_QM_UNIQUE_COUNTER_${_name}")
|
||||
get_property(currentCounter GLOBAL PROPERTY "${propertyName}")
|
||||
if(NOT currentCounter)
|
||||
set(currentCounter 1)
|
||||
endif()
|
||||
set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE)
|
||||
math(EXPR currentCounter "${currentCounter} + 1")
|
||||
set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} )
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_create_qm_loader sourcesvar_or_target catalog_name)
|
||||
if (TARGET ${sourcesvar_or_target})
|
||||
get_target_property(target_type ${sourcesvar_or_target} TYPE)
|
||||
set(allowed_types "EXECUTABLE" "STATIC_LIBRARY" "MODULE_LIBRARY" "SHARED_LIBRARY" "OBJECT_LIBRARY" "INTERFACE_LIBRARY")
|
||||
if (NOT target_type IN_LIST allowed_types)
|
||||
message(FATAL_ERROR "Target argument passed to ecm_create_qm_loader is not an executable or a library: ${appsources_or_target}")
|
||||
endif()
|
||||
get_target_property(aliased_target ${sourcesvar_or_target} ALIASED_TARGET)
|
||||
if(aliased_target)
|
||||
message(FATAL_ERROR "Target argument passed to ecm_create_qm_loader must not be an alias: ${sourcesvar_or_target}")
|
||||
endif()
|
||||
endif()
|
||||
set(loader_base ${CMAKE_CURRENT_BINARY_DIR}/ECMQmLoader-${catalog_name})
|
||||
|
||||
set(QM_LOADER_CATALOG_NAME "${catalog_name}")
|
||||
|
||||
set(QM_LOADER_CPP_FILE "${loader_base}.cpp")
|
||||
configure_file(
|
||||
${ECM_MODULE_DIR}/ECMQmLoader.cpp.in
|
||||
${QM_LOADER_CPP_FILE}
|
||||
@ONLY
|
||||
)
|
||||
if (TARGET ${sourcesvar_or_target})
|
||||
target_sources(${sourcesvar_or_target} PRIVATE ${QM_LOADER_CPP_FILE})
|
||||
else()
|
||||
set(${sourcesvar_or_target} "${${sourcesvar_or_target}}" ${QM_LOADER_CPP_FILE} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_process_po_files_as_qm lang)
|
||||
if (KF_SKIP_PO_PROCESSING)
|
||||
return()
|
||||
endif()
|
||||
# Parse arguments
|
||||
set(options ALL)
|
||||
set(oneValueArgs INSTALL_DESTINATION)
|
||||
set(multiValueArgs PO_FILES)
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ARGS_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to ecm_process_po_files_as_qm(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_PO_FILES)
|
||||
message(FATAL_ERROR "ecm_process_po_files_as_qm() must be called with PO_FILES argument")
|
||||
endif()
|
||||
|
||||
# Find lrelease and lconvert
|
||||
if (QT_MAJOR_VERSION EQUAL "5")
|
||||
find_package(Qt5LinguistTools CONFIG REQUIRED)
|
||||
else()
|
||||
find_package(Qt6 COMPONENTS LinguistTools CONFIG REQUIRED)
|
||||
endif()
|
||||
|
||||
if(TARGET Qt${QT_MAJOR_VERSION}::lconvert)
|
||||
set(lconvert_executable Qt${QT_MAJOR_VERSION}::lconvert)
|
||||
else()
|
||||
# Qt < 5.3.1 does not define Qt5::lconvert
|
||||
get_target_property(lrelease_location Qt5::lrelease LOCATION)
|
||||
get_filename_component(lrelease_path ${lrelease_location} PATH)
|
||||
find_program(lconvert_executable
|
||||
NAMES lconvert-qt5 lconvert
|
||||
PATHS ${lrelease_path}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
|
||||
# Create commands to turn po files into qm files
|
||||
set(qm_files)
|
||||
foreach (po_file ${ARGS_PO_FILES})
|
||||
get_filename_component(po_file ${po_file} ABSOLUTE)
|
||||
get_filename_component(filename_base ${po_file} NAME_WE)
|
||||
|
||||
# Use own ECMPoQm/ subfolder for processing the files, to avoid cluttering
|
||||
# the default build dir as well as potential file/dir name clashes from
|
||||
# other build artifacts.
|
||||
# Include ${lang} in build dir because we might be called multiple times
|
||||
# with the same ${filename_base}
|
||||
set(build_dir ${CMAKE_CURRENT_BINARY_DIR}/ECMPoQm/${lang})
|
||||
set(ts_file ${build_dir}/${filename_base}.ts)
|
||||
set(qm_file ${build_dir}/${filename_base}.qm)
|
||||
|
||||
file(MAKE_DIRECTORY ${build_dir})
|
||||
|
||||
# lconvert from .po to .ts, then lrelease from .ts to .qm.
|
||||
add_custom_command(OUTPUT ${qm_file}
|
||||
COMMAND ${lconvert_executable}
|
||||
ARGS -i ${po_file} -o ${ts_file} -target-language ${lang}
|
||||
COMMAND Qt${QT_MAJOR_VERSION}::lrelease
|
||||
ARGS -removeidentical -nounfinished -silent ${ts_file} -qm ${qm_file}
|
||||
DEPENDS ${po_file}
|
||||
)
|
||||
if (ARGS_INSTALL_DESTINATION)
|
||||
install(
|
||||
FILES ${qm_file}
|
||||
DESTINATION ${ARGS_INSTALL_DESTINATION}/${lang}/LC_MESSAGES
|
||||
)
|
||||
endif()
|
||||
list(APPEND qm_files ${qm_file})
|
||||
endforeach()
|
||||
|
||||
# Hook qm files to targets
|
||||
if(NOT TARGET translations)
|
||||
add_custom_target(translations)
|
||||
endif()
|
||||
|
||||
_ecm_qm_get_unique_target_name(translations target_name)
|
||||
|
||||
if (ARGS_ALL)
|
||||
add_custom_target(${target_name} ALL DEPENDS ${qm_files})
|
||||
else()
|
||||
add_custom_target(${target_name} DEPENDS ${qm_files})
|
||||
endif()
|
||||
|
||||
add_dependencies(translations ${target_name})
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_install_po_files_as_qm podir)
|
||||
if (LOCALE_INSTALL_DIR)
|
||||
set(install_destination "${LOCALE_INSTALL_DIR}")
|
||||
elseif (CMAKE_INSTALL_LOCALEDIR)
|
||||
set(install_destination "${CMAKE_INSTALL_LOCALEDIR}")
|
||||
else()
|
||||
set(install_destination share/locale)
|
||||
endif()
|
||||
|
||||
get_filename_component(absolute_podir ${podir} ABSOLUTE)
|
||||
|
||||
# we try to find the po directory in the binary directory, in case it was downloaded
|
||||
# using ECM
|
||||
if (NOT (EXISTS "${absolute_podir}" AND IS_DIRECTORY "${absolute_podir}"))
|
||||
get_filename_component(absolute_podir ${CMAKE_BINARY_DIR}/${podir} ABSOLUTE)
|
||||
endif()
|
||||
|
||||
if (NOT (EXISTS "${absolute_podir}" AND IS_DIRECTORY "${absolute_podir}"))
|
||||
# Nothing to do if there's no podir and it would create an empty
|
||||
# LOCALE_INSTALL_DIR in that case.
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB po_files "${absolute_podir}/*/*.po")
|
||||
foreach(po_file ${po_files})
|
||||
get_filename_component(po_dir ${po_file} DIRECTORY)
|
||||
get_filename_component(lang ${po_dir} NAME)
|
||||
ecm_process_po_files_as_qm(
|
||||
${lang} ALL
|
||||
PO_FILES ${po_file}
|
||||
INSTALL_DESTINATION ${install_destination}
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if (${ECM_GLOBAL_FIND_VERSION} VERSION_GREATER_EQUAL 5.88)
|
||||
message(DEPRECATION "ECMQMLModules.cmake is deprecated since 5.88.0, please use ECMFindQmlModule.cmake instead")
|
||||
endif()
|
||||
|
||||
include(ECMFindQmlModule)
|
||||
@@ -0,0 +1,251 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
PROJECT_NAME = @ECM_QCH_DOXYGEN_PROJECTNAME@
|
||||
PROJECT_NUMBER = @ECM_QCH_DOXYGEN_PROJECTVERSION@
|
||||
OUTPUT_DIRECTORY = @ECM_QCH_DOXYGEN_OUTPUTDIR@
|
||||
GENERATE_TAGFILE = @ECM_QCH_DOXYGEN_TAGFILE@
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF = "The \$name class" \
|
||||
"The \$name widget" \
|
||||
"The \$name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
ALWAYS_DETAILED_SEC = YES
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = NO
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
# Do not require explicitly @brief command for brief description
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 8
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
SUBGROUPING = YES
|
||||
LAYOUT_FILE = @ECM_QCH_DOXYGEN_LAYOUTFILE@
|
||||
# Dark colors won't look nice in Qt Assistant
|
||||
HTML_COLORSTYLE = LIGHT
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
EXTRACT_ALL = NO
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = NO
|
||||
EXTRACT_LOCAL_METHODS = NO
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
# Require classes to be documented to appear in apidox, but always document all
|
||||
# public and protected members (even if static)
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = NO
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
HIDE_COMPOUND_REFERENCE = YES
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_MEMBERS_CTORS_1ST = YES
|
||||
SORT_BRIEF_DOCS = YES
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
GENERATE_TODOLIST = NO
|
||||
GENERATE_TESTLIST = NO
|
||||
GENERATE_BUGLIST = NO
|
||||
GENERATE_DEPRECATEDLIST = YES
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = NO
|
||||
SHOW_FILES = YES
|
||||
FILE_VERSION_FILTER =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = @ECM_QCH_DOXYGEN_QUIET@
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = YES
|
||||
WARN_FORMAT = "\$file:\$line: \$text"
|
||||
WARN_LOGFILE = @ECM_QCH_DOXYGEN_WARN_LOGFILE@
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = @ECM_QCH_DOXYGEN_INPUT@
|
||||
FILE_PATTERNS = @ECM_QCH_DOXYGEN_FILE_PATTERNS@
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS = */.svn/* \
|
||||
*/.git/* \
|
||||
*/cmake/* \
|
||||
*.moc.* \
|
||||
moc* \
|
||||
*.all_cpp.* \
|
||||
*unload.* \
|
||||
*/test/* \
|
||||
*/tests/* \
|
||||
*/autotests/* \
|
||||
*_p.cpp \
|
||||
*_p.h
|
||||
# Symbols from Qt that show up occassionlly and we don't want to see
|
||||
EXCLUDE_SYMBOLS = iterator const_iterator
|
||||
EXAMPLE_PATH = @ECM_QCH_DOXYGEN_EXAMPLEDIRS@
|
||||
EXAMPLE_PATTERNS = *
|
||||
EXAMPLE_RECURSIVE = YES
|
||||
IMAGE_PATH = @ECM_QCH_DOXYGEN_IMAGEDIRS@
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
USE_MDFILE_AS_MAINPAGE = @ECM_QCH_DOXYGEN_MAINPAGE_MDFILE@
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# do NOT generate any formats other than qhp
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_MAN = NO
|
||||
GENERATE_RTF = NO
|
||||
GENERATE_XML = NO
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
GENERATE_PERLMOD = NO
|
||||
DISABLE_INDEX = YES
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the qhp output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_QHP = YES
|
||||
QCH_FILE = @ECM_QCH_DOXYGEN_FILEPATH@
|
||||
QHP_NAMESPACE = @ECM_QCH_DOXYGEN_FULLNAMESPACE@
|
||||
QHP_VIRTUAL_FOLDER = @ECM_QCH_DOXYGEN_VIRTUALFOLDER@
|
||||
QHG_LOCATION = @ECM_QCH_DOXYGEN_QHELPGENERATOR_EXECUTABLE@
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH = @ECM_QCH_DOXYGEN_INCLUDE_PATH@
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
TAGFILES = @ECM_QCH_DOXYGEN_TAGFILES@
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = NO
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = NO
|
||||
CLASS_GRAPH = NO
|
||||
COLLABORATION_GRAPH = NO
|
||||
GROUP_GRAPHS = NO
|
||||
UML_LOOK = NO
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = NO
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = NO
|
||||
DIRECTORY_GRAPH = NO
|
||||
GENERATE_LEGEND = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
||||
|
||||
|
||||
### KDE Settings
|
||||
ALIASES = "intern=\par<b>Internal use only.</b>" \
|
||||
"reimp=\par<b>Reimplemented from superclass.</b>" \
|
||||
"obsolete=@deprecated" \
|
||||
"feature=\xrefitem features \"Feature(s)\" \"Features\"" \
|
||||
"unmaintained=\xrefitem unmaintained \"Unmaintained\" \"Unmaintained\"" \
|
||||
"requirement=\xrefitem requirements \"Requirement(s)\" \"Requirements\"" \
|
||||
"faq=\xrefitem FAQ \"F.A.Q.\" \"F.A.Q.\"" \
|
||||
"authors=\xrefitem authors \"Author(s)\" \"Authors\"" \
|
||||
"maintainers=\xrefitem maintainers \"Maintainer(s)\" \"Maintainers\"" \
|
||||
"glossary=\xrefitem glossary \"Glossary\" \"Glossary\"" \
|
||||
"acronym=\b " \
|
||||
"licenses=\xrefitem licenses \"License(s)\" \"Licenses\"" \
|
||||
"FIXME=\xrefitem fixme \"Fixme\" \"Fixme\"" \
|
||||
"bc=\xrefitem bc \"Binary Compatible\" \"Binary Compatible\"" \
|
||||
"threadsafe=\xrefitem threadsafe \"Threadsafe\" \"Threadsafe\"" \
|
||||
"artistic=<a href=\"https://opensource.org/licenses/artistic-license.php\">Artistic</a>" \
|
||||
"bsd=<a href=\"https://www.xfree86.org/3.3.6/COPYRIGHT2.html#5\">BSD</a>" \
|
||||
"x11=<a href=\"https://www.xfree86.org/3.3.6/COPYRIGHT2.html#3\">X11</a>" \
|
||||
"gpl=<a href=\"https://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC1\">GPLv2</a>" \
|
||||
"lgpl=<a href=\"https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html#SEC1\">LGPLv2</a>" \
|
||||
"mit=<a href=\"https://www.opensource.org/licenses/mit-license.php\">MIT</a>" \
|
||||
"qpl=<a href=\"https://opensource.org/licenses/QPL-1.0\">QPL</a>"
|
||||
|
||||
# K_DOXYGEN set to have preprocessor macros know that kapidox/doxygen processes them
|
||||
# DOXYGEN_SHOULD_SKIP_THIS is the deprecated variant (remove for KF6)
|
||||
PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \
|
||||
K_DOXYGEN \
|
||||
\
|
||||
Q_WS_X11= \
|
||||
Q_WS_WIN= \
|
||||
Q_WS_MAC= \
|
||||
Q_WS_QWS= \
|
||||
Q_WS_MAEMO_5= \
|
||||
Q_OS_LINUX= \
|
||||
Q_OS_UNIX= \
|
||||
Q_OS_WIN= \
|
||||
Q_OS_MAC= \
|
||||
Q_OS_MACX= \
|
||||
Q_OS_DARWIN= \
|
||||
Q_OS_FREEBSD= \
|
||||
Q_OS_NETBSD= \
|
||||
Q_OS_OPENBSD= \
|
||||
Q_OS_BSD4= \
|
||||
Q_OS_SOLARIS= \
|
||||
Q_OS_IRIX= \
|
||||
\
|
||||
Q_SLOTS=slots \
|
||||
Q_SIGNALS=signals \
|
||||
"Q_DECLARE_FLAGS(Flags, Enum)=typedef QFlags<Enum> Flags;" \
|
||||
Q_DECL_CONSTEXPR=constexpr \
|
||||
Q_DECL_RELAXED_CONSTEXPR= \
|
||||
Q_DECL_OVERRIDE=override \
|
||||
Q_DECL_FINAL=final \
|
||||
"Q_DECL_EQ_DEFAULT= = default" \
|
||||
"Q_DECL_EQ_DELETE= = delete" \
|
||||
Q_DECL_NOEXCEPT= \
|
||||
Q_DECL_DEPRECATED= \
|
||||
Q_DECL_UNUSED_MEMBER= \
|
||||
Q_DECL_VARIABLE_DEPRECATED= \
|
||||
Q_DECL_EXPORT= \
|
||||
Q_DECL_IMPORT= \
|
||||
Q_DECL_HIDDEN= \
|
||||
Q_DECL_NULLPTR=nullptr \
|
||||
Q_REQUIRED_RESULT= \
|
||||
Q_SCRIPTABLE= \
|
||||
Q_INVOKABLE= \
|
||||
@ECM_QCH_DOXYGEN_PREDEFINED_MACROS@ \
|
||||
@ECM_QCH_DOXYGEN_BLANK_MACROS@
|
||||
@@ -0,0 +1,194 @@
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.8.7 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes" title="" intro=""/>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<properties title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
<protectedstaticmethods title=""/>
|
||||
<protectedattributes title=""/>
|
||||
<protectedstaticattributes title=""/>
|
||||
<packagetypes title=""/>
|
||||
<packagemethods title=""/>
|
||||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
<privatemethods title=""/>
|
||||
<privatestaticmethods title=""/>
|
||||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
<friends title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<properties title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<constructors title=""/>
|
||||
<functions title=""/>
|
||||
<related title=""/>
|
||||
<variables title=""/>
|
||||
<events title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="no"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<sourcelink visible="no"/>
|
||||
<memberdecl>
|
||||
<classes visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
@@ -0,0 +1,138 @@
|
||||
/* This file was generated by ecm_create_qm_loader(). DO NOT EDIT!
|
||||
*
|
||||
* Building this file in a library ensures translations are automatically loaded
|
||||
* when an application makes use of the library.
|
||||
*
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||
* SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
|
||||
* SPDX-FileCopyrightText: 2023 Ingo Klöcker <kloecker@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <QCoreApplication>
|
||||
#include <QLocale>
|
||||
#include <QStandardPaths>
|
||||
#include <QThread>
|
||||
#include <QTranslator>
|
||||
#include <QDir>
|
||||
|
||||
namespace {
|
||||
|
||||
static QLocale getSystemLocale()
|
||||
{
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
// On Windows and Apple OSs, we cannot use QLocale::system() if an application-specific
|
||||
// language was set by kxmlgui because Qt ignores LANGUAGE on Windows and Apple OSs.
|
||||
// The following code is a simplified variant of QSystemLocale::fallbackUiLocale()
|
||||
// (in qlocale_unix.cpp) ignoring LC_ALL, LC_MESSAGES, and LANG.
|
||||
QString language = qEnvironmentVariable("LANGUAGE");
|
||||
if (!language.isEmpty()) {
|
||||
language = language.split(QLatin1Char{':'}).constFirst();
|
||||
if (!language.isEmpty()) {
|
||||
return QLocale{language};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return QLocale::system();
|
||||
}
|
||||
|
||||
enum class LoadOptions { CreateWatcher, DoNotCreateWatcher };
|
||||
|
||||
void load(LoadOptions options);
|
||||
|
||||
class LanguageChangeWatcher : public QObject
|
||||
{
|
||||
public:
|
||||
LanguageChangeWatcher(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_loadedLocale = getSystemLocale().name();
|
||||
QCoreApplication::instance()->installEventFilter(this);
|
||||
}
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override
|
||||
{
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
const auto systemLocaleName = getSystemLocale().name();
|
||||
if (m_loadedLocale != systemLocaleName) {
|
||||
m_loadedLocale = systemLocaleName;
|
||||
load(LoadOptions::DoNotCreateWatcher);
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
|
||||
QString m_loadedLocale;
|
||||
};
|
||||
|
||||
bool loadTranslation(const QString &localeDirName)
|
||||
{
|
||||
QString subPath = QStringLiteral("locale/") + localeDirName + QStringLiteral("/LC_MESSAGES/@QM_LOADER_CATALOG_NAME@.qm");
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
const QString fullPath = QStringLiteral("assets:/share/") + subPath;
|
||||
if (!QFile::exists(fullPath)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
const QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath);
|
||||
if (fullPath.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
QTranslator *translator = new QTranslator(QCoreApplication::instance());
|
||||
if (!translator->load(fullPath)) {
|
||||
delete translator;
|
||||
return false;
|
||||
}
|
||||
QCoreApplication::instance()->installTranslator(translator);
|
||||
return true;
|
||||
}
|
||||
|
||||
void load(LoadOptions options)
|
||||
{
|
||||
// The way Qt translation system handles plural forms makes it necessary to
|
||||
// have a translation file which contains only plural forms for `en`. That's
|
||||
// why we load the `en` translation unconditionally, then load the
|
||||
// translation for the current locale to overload it.
|
||||
loadTranslation(QStringLiteral("en"));
|
||||
|
||||
auto langs = getSystemLocale().uiLanguages();
|
||||
for (auto it = langs.begin(); it != langs.end(); ++it) {
|
||||
(*it).replace(QLatin1Char('-'), QLatin1Char('_'));
|
||||
const auto idx = (*it).indexOf(QLatin1Char('_'));
|
||||
if (idx > 0) {
|
||||
const QString genericLang = (*it).left(idx);
|
||||
it = langs.insert(++it, genericLang);
|
||||
}
|
||||
}
|
||||
langs.removeDuplicates();
|
||||
for (const auto &lang : langs) {
|
||||
if (lang == QLatin1String("en") || loadTranslation(lang)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (options == LoadOptions::CreateWatcher) {
|
||||
new LanguageChangeWatcher(QCoreApplication::instance());
|
||||
}
|
||||
}
|
||||
|
||||
void loadOnMainThread()
|
||||
{
|
||||
// If this library is loaded after the QCoreApplication instance is
|
||||
// created (eg: because it is brought in by a plugin), there is no
|
||||
// guarantee this function will be called on the main thread.
|
||||
// QCoreApplication::installTranslator needs to be called on the main
|
||||
// thread, because it uses QCoreApplication::sendEvent.
|
||||
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
|
||||
load(LoadOptions::CreateWatcher);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), [] { load(LoadOptions::CreateWatcher); }, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_COREAPP_STARTUP_FUNCTION(loadOnMainThread)
|
||||
@@ -0,0 +1,189 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[========================================================================[.rst:
|
||||
ECMQmlModule
|
||||
------------
|
||||
|
||||
This file contains helper functions to make it easier to create QML modules. It
|
||||
takes care of a number of things that often need to be repeated. It also takes
|
||||
care of special handling of QML modules between shared and static builds. When
|
||||
building a static version of a QML module, the relevant QML source files are
|
||||
bundled into the static library. When using a shared build, the QML plugin and
|
||||
relevant QML files are copied to the target's ``RUNTIME_OUTPUT_DIRECTORY`` to make
|
||||
it easier to run things directly from the build directory.
|
||||
|
||||
Since 6.0.0, when using Qt 6, most functionality of this module has been
|
||||
implemented by upstream Qt. Most of the functions here will now forward to the
|
||||
similar Qt functions.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_add_qml_module(ExampleModule URI "org.example.Example")
|
||||
|
||||
target_sources(ExampleModule PRIVATE ExamplePlugin.cpp)
|
||||
target_link_libraries(ExampleModule PRIVATE Qt::Quick)
|
||||
|
||||
ecm_target_qml_sources(ExampleModule SOURCES ExampleItem.qml) # This will have 1.0 as the default version
|
||||
ecm_target_qml_sources(ExampleModule SOURCES AnotherExampleItem.qml VERSION 1.5)
|
||||
|
||||
ecm_finalize_qml_module(ExampleModule DESTINATION ${KDE_INSTALL_QMLDIR})
|
||||
|
||||
|
||||
::
|
||||
|
||||
ecm_add_qml_module(<target name>
|
||||
URI <module uri>
|
||||
[VERSION <module version>]
|
||||
[NO_PLUGIN] # Deprecated since 6.0.0 when using Qt 6
|
||||
[CLASSNAME <class name>] # Deprecated since 6.0.0 when using Qt 6, use CLASS_NAME instead
|
||||
[QT_NO_PLUGIN] # Since 6.0.0, when using Qt 6
|
||||
[GENERATE_PLUGIN_SOURCE] # Since 6.0.0, when using Qt 6
|
||||
)
|
||||
|
||||
This will declare a new CMake target called ``<target name>``. The ``URI``
|
||||
argument is required and should be a proper QML module URI. The ``URI`` is used,
|
||||
among others, to generate a subdirectory where the module will be installed to.
|
||||
|
||||
If the ``VERSION`` argument is specified, it is used to initialize the default
|
||||
version that is used by ``ecm_target_qml_sources`` when adding QML files. If it
|
||||
is not specified, a default of 1.0 is used. Additionally, if a version greater
|
||||
than or equal to 2.0 is specified, the major version is appended to the
|
||||
Qt5 installation path of the module.
|
||||
In case you don't specify and version, but specify a version for the individual sources, the latest
|
||||
will be set as the resulting version for this plugin. This will be used in the ECMFindQmlModule module.
|
||||
|
||||
If the option ``NO_PLUGIN`` is set, a target is declared that is not expected to
|
||||
contain any C++ QML plugin.
|
||||
|
||||
If the optional ``CLASSNAME`` argument is supplied, it will be used as class
|
||||
name in the generated QMLDIR file. If it is not specified, the target name will
|
||||
be used instead.
|
||||
|
||||
You can add C++ and QML source files to the target using ``target_sources`` and
|
||||
``ecm_target_qml_sources``, respectively.
|
||||
|
||||
Since 5.91.0
|
||||
|
||||
Since 6.0.0, when used with Qt 6, this will forward to ``qt_add_qml_module``. Any extra arguments will
|
||||
be forwarded as well. The ``NO_PLUGIN`` argument is deprecated and implies ``GENERATE_PLUGIN_SOURCE``,
|
||||
since modules in Qt 6 always require a plugin or backing target. If you want to use Qt's behaviour for
|
||||
``NO_PLUGIN``, use ``QT_NO_PLUGIN`` instead. Additionally, to maintain backward compatibility, by
|
||||
default we pass ``NO_GENERATE_PLUGIN_SOURCE`` to ``qt_add_qml_module``. To have Qt generate the plugin
|
||||
sources, pass ``GENERATE_PLUGIN_SOURCE``.
|
||||
|
||||
::
|
||||
|
||||
ecm_add_qml_module_dependencies(<target> DEPENDS <module string> [<module string> ...])
|
||||
|
||||
Add the list of dependencies specified by the ``DEPENDS`` argument to be listed
|
||||
as dependencies in the generated QMLDIR file of ``<target>``.
|
||||
|
||||
Since 5.91.0
|
||||
|
||||
Since 6.0.0, this is deprecated and ignored when using Qt 6, instead use the
|
||||
``DEPENDENCIES`` and ``IMPORTS`` arguments to ``ecm_add_qml_module``.
|
||||
|
||||
::
|
||||
|
||||
ecm_target_qml_sources(<target> SOURCES <source.qml> [<source.qml> ...] [VERSION <version>] [PATH <path>] [PRIVATE])
|
||||
|
||||
Add the list of QML files specified by the ``SOURCES`` argument as source files
|
||||
to the QML module target ``<target>``.
|
||||
|
||||
If the optional ``VERSION`` argument is specified, all QML files will be added
|
||||
with the specified version. If it is not specified, they will use the version of
|
||||
the QML module target.
|
||||
|
||||
If the optional ``PRIVATE`` argument is specified, the QML files will be
|
||||
included in the target but not in the generated qmldir file. Any version
|
||||
argument will be ignored.
|
||||
|
||||
The optional ``PATH`` argument declares a subdirectory of the module where the
|
||||
files should be copied to. By default, files will be copied to the module root.
|
||||
|
||||
This function will fail if ``<target>`` is not a QML module target or any of the
|
||||
specified files do not exist.
|
||||
|
||||
Since 5.91.0
|
||||
|
||||
Since 6.0.0, when used with Qt 6, this will forward to ``qt_target_qml_sources()``.
|
||||
The ``SOURCES`` argument will be translated to ``QML_SOURCES``. ``VERSION`` and
|
||||
``PRIVATE`` will set Qt's ``QT_QML_SOURCE_VERSIONS`` and ``QT_QML_INTERNAL_TYPE``
|
||||
properties on ``SOURCES`` before calling ``qt_target_qml_sources()``. Since Qt
|
||||
includes the path relative to the current source dir, for each source file a
|
||||
resource alias will be generated with the path stripped. If the ``PATH`` argument
|
||||
is set, it will be prefixed to the alias. Any additional arguments will be passed
|
||||
to ``qt_target_qml_sources()``.
|
||||
|
||||
::
|
||||
|
||||
ecm_finalize_qml_module(<target>
|
||||
[DESTINATION <QML install destination>] # Optional since 6.0
|
||||
[VERSION <Project Version>] # Added for 6.0 when using Qt 6
|
||||
[EXPORT <export-set>] # Added for 6.8 when using Qt 6
|
||||
)
|
||||
|
||||
Finalize the specified QML module target. This must be called after all other
|
||||
setup (like adding sources) on the target has been done. It will perform a
|
||||
number of tasks:
|
||||
|
||||
- It will generate a qmldir file from the QML files added to the target. If the
|
||||
module has a C++ plugin, this will also be included in the qmldir file.
|
||||
- If ``BUILD_SHARED_LIBS`` is off, a QRC file is generated from the QML files
|
||||
added to the target. This QRC file will be included when compiling the C++ QML
|
||||
module. The built static library will be installed in a subdirection of
|
||||
``DESTINATION`` based on the QML module's uri. If this value is not set, KDE_INSTALL_QMLDIR will be used.
|
||||
Note that if ``NO_PLUGIN`` is set, a C++ QML plugin will be generated to include the QRC files.
|
||||
- If ``BUILD_SHARED_LIBS`` in on, all generated files, QML sources and the C++
|
||||
plugin will be installed in a subdirectory of ``DESTINATION`` based upon the
|
||||
QML module's uri. In addition, these files will also be copied to the target's
|
||||
``RUNTIME_OUTPUT_DIRECTORY`` in a similar subdirectory.
|
||||
- If ``BUILD_SHARED_LIBS`` is off, ``EXPORT`` allows to specify a CMake export set
|
||||
all installed targets should be added to.
|
||||
|
||||
This function will fail if ``<target>`` is not a QML module target.
|
||||
|
||||
Since 5.91.0
|
||||
|
||||
Since 6.0.0, when using Qt 6, this will instead install the files generated
|
||||
by ``qt_add_qml_module``. The optional ``VERSION`` argument was added that will
|
||||
default to ``PROJECT_VERSION`` and which will write a file that is used by
|
||||
``ECMFindQmlModule`` to detect the version of the QML module.
|
||||
|
||||
Since 6.1.0
|
||||
|
||||
Enabling the option ``VERBOSE_QML_COMPILER`` will activate verbose output for qmlcachegen.
|
||||
|
||||
#]========================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||
|
||||
# This is also used by ECMFindQmlModule, so needs to be available for both
|
||||
# Qt 5 and Qt 6.
|
||||
macro(_ecm_qmlmodule_uri_to_path ARG_OUTPUT ARG_PATH ARG_VERSION)
|
||||
string(REPLACE "." "/" _output "${ARG_PATH}")
|
||||
|
||||
# If the major version of the module is >2.0, Qt expects a ".MajorVersion" suffix on the directory
|
||||
# However, we only need to do this in Qt5
|
||||
if ("${QT_MAJOR_VERSION}" STREQUAL "5" AND "${ARG_VERSION}" VERSION_GREATER_EQUAL 2.0)
|
||||
string(REGEX MATCH "^([0-9]+)" _version_major ${ARG_VERSION})
|
||||
set("${ARG_OUTPUT}" "${_output}.${_version_major}")
|
||||
else()
|
||||
set("${ARG_OUTPUT}" "${_output}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# The implementation for this has diverged significantly between Qt 5 and Qt 6
|
||||
# so it has been split to separate files.
|
||||
if ("${QT_MAJOR_VERSION}" STREQUAL "6")
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule6.cmake)
|
||||
elseif("${QT_MAJOR_VERSION}" STREQUAL "5")
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule5.cmake)
|
||||
else()
|
||||
message(FATAL_ERROR "Could not determine Qt major version")
|
||||
endif()
|
||||
@@ -0,0 +1,10 @@
|
||||
// This file is autogenerated by ECMQmlModule to support static QML-only plugins.
|
||||
|
||||
#include "QmlModule.h"
|
||||
|
||||
#include <QQmlEngine>
|
||||
|
||||
void QmlModule::registerTypes(const char* uri)
|
||||
{
|
||||
Q_ASSERT(QLatin1String(uri) == QLatin1String("@_qml_uri@"));
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
@@ -0,0 +1,17 @@
|
||||
// This file is autogenerated by ECMQmlModule to support static QML-only plugins.
|
||||
|
||||
#ifndef QMLMODULE_H
|
||||
#define QMLMODULE_H
|
||||
|
||||
#include <QQmlExtensionPlugin>
|
||||
|
||||
class QmlModule : public QQmlExtensionPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
|
||||
|
||||
public:
|
||||
void registerTypes(const char* uri) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
@@ -0,0 +1,339 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Qt 5 implementation of ECMQmlModule
|
||||
|
||||
set(_ECM_QMLMODULE_STATIC_QMLONLY_H "${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule.h.in")
|
||||
set(_ECM_QMLMODULE_STATIC_QMLONLY_CPP "${CMAKE_CURRENT_LIST_DIR}/ECMQmlModule.cpp.in")
|
||||
|
||||
set(_ECM_QMLMODULE_PROPERTY_URI "_ecm_qml_uri")
|
||||
set(_ECM_QMLMODULE_PROPERTY_QMLDIR "_ecm_qmldir_file")
|
||||
set(_ECM_QMLMODULE_PROPERTY_FILES "_ecm_qml_files")
|
||||
set(_ECM_QMLMODULE_PROPERTY_QMLONLY "_ecm_qml_only")
|
||||
set(_ECM_QMLMODULE_PROPERTY_VERSION "_ecm_qml_version")
|
||||
set(_ECM_QMLMODULE_PROPERTY_PRIVATE "_ecm_qml_private")
|
||||
set(_ECM_QMLMODULE_PROPERTY_PATH "_ecm_qml_path")
|
||||
set(_ECM_QMLMODULE_PROPERTY_CLASSNAME "_ecm_qml_classname")
|
||||
set(_ECM_QMLMODULE_PROPERTY_DEPENDS "_ecm_qml_depends")
|
||||
|
||||
macro(_ecm_qmlmodule_verify_qml_target ARG_TARGET)
|
||||
if (NOT TARGET ${ARG_TARGET})
|
||||
message(FATAL_ERROR "Target ${ARG_TARGET} does not exist")
|
||||
endif()
|
||||
get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI})
|
||||
if ("${_qml_uri}" STREQUAL "" OR "${_qml_uri}" STREQUAL "${_ECM_QMLMODULE_PROPERTY_URI}-NOTFOUND")
|
||||
message(FATAL_ERROR "Target ${ARG_TARGET} is not a QML plugin target")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(_ecm_qmlmodule_generate_qmldir ARG_TARGET)
|
||||
get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI})
|
||||
get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES})
|
||||
get_target_property(_qml_only ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLONLY})
|
||||
get_target_property(_qml_classname ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_CLASSNAME})
|
||||
|
||||
set(_qmldir_template "# This file was automatically generated by ECMQmlModule and should not be modified")
|
||||
|
||||
string(APPEND _qmldir_template "\nmodule ${_qml_uri}")
|
||||
|
||||
if (NOT ${_qml_only})
|
||||
string(APPEND _qmldir_template "\nplugin ${ARG_TARGET}")
|
||||
|
||||
if ("${_qml_classname}" STREQUAL "")
|
||||
string(APPEND _qmldir_template "\nclassname ${ARG_TARGET}")
|
||||
else()
|
||||
string(APPEND _qmldir_template "\nclassname ${_qml_classname}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_target_property(_qml_depends ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_DEPENDS})
|
||||
if (NOT "${_qml_depends}" STREQUAL "")
|
||||
foreach(_depends ${_qml_depends})
|
||||
string(APPEND _qmldir_template "\ndepends ${_depends}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_file ${_qml_files})
|
||||
get_filename_component(_filename ${_file} NAME)
|
||||
get_filename_component(_classname ${_file} NAME_WE)
|
||||
get_property(_version SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_VERSION})
|
||||
get_property(_private SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PRIVATE})
|
||||
get_property(_singleton SOURCE ${_file} PROPERTY "QT_QML_SINGLETON_TYPE")
|
||||
get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH})
|
||||
cmake_path(SET _actual_path "${_path}")
|
||||
cmake_path(APPEND _actual_path "${_filename}")
|
||||
if (_singleton)
|
||||
string(APPEND _qmldir_template "\nsingleton ${_classname} ${_version} ${_actual_path}")
|
||||
else()
|
||||
if (NOT _private)
|
||||
string(APPEND _qmldir_template "\n${_classname} ${_version} ${_actual_path}")
|
||||
else()
|
||||
string(APPEND _qmldir_template "\ninternal ${_classname} ${_actual_path}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
get_target_property(_qml_version ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_VERSION})
|
||||
string(APPEND _qmldir_template "\n# KDE-qmldir-Version: ${_qml_version}")
|
||||
|
||||
string(APPEND _qmldir_template "\n")
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}_qmldir" "${_qmldir_template}")
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES _ecm_qmldir_file "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}_qmldir")
|
||||
endfunction()
|
||||
|
||||
function(_ecm_qmlmodule_generate_qrc ARG_TARGET)
|
||||
get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI})
|
||||
get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES})
|
||||
get_target_property(_qmldir_file ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLDIR})
|
||||
get_target_property(_qml_version ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_VERSION})
|
||||
|
||||
_ecm_qmlmodule_uri_to_path(_qml_prefix "${_qml_uri}" "${_qml_version}")
|
||||
|
||||
set(_qrc_template "<!-- This file was automatically generated by ECMQmlModule and should not be modified -->")
|
||||
|
||||
string(APPEND _qrc_template "\n<RCC>\n<qresource prefix=\"/qt-project.org/imports/${_qml_prefix}\">")
|
||||
string(APPEND _qrc_template "\n<file alias=\"qmldir\">${_qmldir_file}</file>")
|
||||
|
||||
foreach(_file ${_qml_files})
|
||||
get_filename_component(_filename ${_file} NAME)
|
||||
get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH})
|
||||
|
||||
set(_file_path "${_filename}")
|
||||
if (NOT "${_path}" STREQUAL "")
|
||||
set(_file_path "${_path}/${_filename}")
|
||||
endif()
|
||||
|
||||
string(APPEND _qrc_template "\n<file alias=\"${_file_path}\">${_file}</file>")
|
||||
endforeach()
|
||||
|
||||
string(APPEND _qrc_template "\n</qresource>\n</RCC>\n")
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.qrc" "${_qrc_template}")
|
||||
qt_add_resources(_qrc_output "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}.qrc")
|
||||
|
||||
target_sources(${ARG_TARGET} PRIVATE ${_qrc_output})
|
||||
endfunction()
|
||||
|
||||
function(ecm_add_qml_module ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "NO_PLUGIN" "URI;VERSION;CLASSNAME" "")
|
||||
|
||||
if ("${ARG_TARGET}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_add_qml_module called without a valid target name.")
|
||||
endif()
|
||||
|
||||
if ("${ARG_URI}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.")
|
||||
endif()
|
||||
|
||||
string(FIND "${ARG_URI}" " " "_space")
|
||||
if (${_space} GREATER_EQUAL 0)
|
||||
message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.")
|
||||
endif()
|
||||
|
||||
if (${BUILD_SHARED_LIBS} AND ${ARG_NO_PLUGIN})
|
||||
# CMake doesn't like library targets without sources, so if we have no
|
||||
# C++ sources, use a plain target that we can add all the install stuff
|
||||
# to.
|
||||
add_custom_target(${ARG_TARGET} ALL)
|
||||
else()
|
||||
add_library(${ARG_TARGET})
|
||||
endif()
|
||||
|
||||
if (NOT ARG_VERSION)
|
||||
set(ARG_VERSION "1.0")
|
||||
endif()
|
||||
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES
|
||||
${_ECM_QMLMODULE_PROPERTY_URI} "${ARG_URI}"
|
||||
${_ECM_QMLMODULE_PROPERTY_FILES} ""
|
||||
${_ECM_QMLMODULE_PROPERTY_QMLONLY} "${ARG_NO_PLUGIN}"
|
||||
${_ECM_QMLMODULE_PROPERTY_VERSION} "${ARG_VERSION}"
|
||||
${_ECM_QMLMODULE_PROPERTY_CLASSNAME} "${ARG_CLASSNAME}"
|
||||
${_ECM_QMLMODULE_PROPERTY_DEPENDS} ""
|
||||
)
|
||||
|
||||
# QQmlImportDatabase::resolvePlugin doesn't accept lib prefixes under
|
||||
# Windows, causing to fail to import when using as a dynamic plugin.
|
||||
if (MINGW)
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES PREFIX "")
|
||||
endif()
|
||||
|
||||
# -Muri is required for static QML plugins to work properly, see
|
||||
# https://bugreports.qt.io/browse/QTBUG-82598
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES AUTOMOC_MOC_OPTIONS -Muri=${ARG_URI})
|
||||
endfunction()
|
||||
|
||||
function(ecm_add_qml_module_dependencies ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "DEPENDS")
|
||||
|
||||
_ecm_qmlmodule_verify_qml_target(${ARG_TARGET})
|
||||
|
||||
if ("${ARG_DEPENDS}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_add_qml_module_dependencies called without required argument DEPENDS")
|
||||
endif()
|
||||
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES ${_ECM_QMLMODULE_PROPERTY_DEPENDS} "${ARG_DEPENDS}")
|
||||
endfunction()
|
||||
|
||||
function(ecm_target_qml_sources ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "PRIVATE" "VERSION;PATH" "SOURCES")
|
||||
|
||||
_ecm_qmlmodule_verify_qml_target(${ARG_TARGET})
|
||||
|
||||
if ("${ARG_SOURCES}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_target_qml_sources called without required argument SOURCES")
|
||||
endif()
|
||||
|
||||
if ("${ARG_VERSION}" STREQUAL "")
|
||||
get_target_property(ARG_VERSION ${ARG_TARGET} "_ecm_qml_version")
|
||||
endif()
|
||||
# In case we have not specified a version in add_qml_module, we use 1.0 as a default for the individual QML files
|
||||
if (NOT ARG_VERSION)
|
||||
set(ARG_VERSION "1.0")
|
||||
endif()
|
||||
|
||||
foreach(_file ${ARG_SOURCES})
|
||||
# Check if a given file exists, but only for files that are not
|
||||
# automatically generated.
|
||||
set(_path "${_file}")
|
||||
get_source_file_property(_generated ${_file} GENERATED)
|
||||
if (NOT _generated)
|
||||
if (IS_ABSOLUTE ${_path})
|
||||
# Assume absolute paths are generated, which may not always be
|
||||
# true but is fairly likely.
|
||||
set(_generated TRUE)
|
||||
else()
|
||||
string(FIND ${_file} ${CMAKE_BINARY_DIR} _in_binary_dir)
|
||||
if (${_in_binary_dir} GREATER_EQUAL 0)
|
||||
# Assume anything in binary dir is generated.
|
||||
set(_generated TRUE)
|
||||
else()
|
||||
set(_path "${CMAKE_CURRENT_SOURCE_DIR}/${_file}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT ${_generated} AND NOT EXISTS ${_path})
|
||||
message(FATAL_ERROR "ecm_target_qml_sources called with nonexistent file ${_file}")
|
||||
endif()
|
||||
|
||||
set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_VERSION} "${ARG_VERSION}")
|
||||
if (ARG_PRIVATE)
|
||||
set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PRIVATE} TRUE)
|
||||
endif()
|
||||
set_property(SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH} "${ARG_PATH}")
|
||||
set_property(TARGET ${ARG_TARGET}
|
||||
APPEND PROPERTY ${_ECM_QMLMODULE_PROPERTY_FILES} ${_path}
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(ecm_finalize_qml_module ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "DESTINATION" "")
|
||||
|
||||
_ecm_qmlmodule_verify_qml_target(${ARG_TARGET})
|
||||
|
||||
if (NOT ARG_DESTINATION)
|
||||
set(ARG_DESTINATION "${KDE_INSTALL_QMLDIR}")
|
||||
endif()
|
||||
if ("${ARG_DESTINATION}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_finalize_qml_module called without argument DESTINATION and KDE_INSTALL_QMLDIR is not set")
|
||||
endif()
|
||||
|
||||
get_target_property(_qml_uri ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_URI})
|
||||
get_target_property(_version ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_VERSION})
|
||||
|
||||
_ecm_qmlmodule_generate_qmldir(${ARG_TARGET})
|
||||
_ecm_qmlmodule_uri_to_path(_plugin_path "${_qml_uri}" "${_version}")
|
||||
|
||||
get_target_property(_qml_only ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLONLY})
|
||||
|
||||
if (NOT BUILD_SHARED_LIBS)
|
||||
_ecm_qmlmodule_generate_qrc(${ARG_TARGET})
|
||||
set(CPP_RESOURCE_INIT "#include <qglobal.h> \n#include <QDebug> \n void initQmlResource${ARG_TARGET}() {Q_INIT_RESOURCE(${ARG_TARGET}); qWarning()<<Q_FUNC_INFO;};")
|
||||
file(WRITE ${ARG_TARGET}_init.cpp "${CPP_RESOURCE_INIT}")
|
||||
target_sources(${ARG_TARGET} PRIVATE ${ARG_TARGET}_init.cpp)
|
||||
target_compile_definitions(${ARG_TARGET} PRIVATE -DQT_PLUGIN_RESOURCE_INIT_FUNCTION=initQmlResource${ARG_TARGET} -DQT_STATICPLUGIN=1)
|
||||
|
||||
if (${_qml_only})
|
||||
# If we do not have any C++ sources for the target, we need a way to
|
||||
# compile the generated QRC file. So generate a very plain C++ QML
|
||||
# plugin that we include in the target.
|
||||
configure_file(${_ECM_QMLMODULE_STATIC_QMLONLY_H} ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.h @ONLY)
|
||||
configure_file(${_ECM_QMLMODULE_STATIC_QMLONLY_CPP} ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.cpp @ONLY)
|
||||
|
||||
target_sources(${ARG_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/QmlModule.cpp)
|
||||
if (TARGET Qt::Qml)
|
||||
target_link_libraries(${ARG_TARGET} PRIVATE Qt::Qml)
|
||||
else()
|
||||
target_link_libraries(${ARG_TARGET} PRIVATE Qt5::Qml)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS ${ARG_TARGET} DESTINATION ${ARG_DESTINATION}/${_plugin_path})
|
||||
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(_runtime_output_dir ${ARG_TARGET} RUNTIME_OUTPUT_DIRECTORY)
|
||||
if (NOT ${_runtime_output_dir})
|
||||
if ("${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" STREQUAL "")
|
||||
set(_runtime_output_dir ${CMAKE_CURRENT_BINARY_DIR})
|
||||
else()
|
||||
set(_runtime_output_dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${ARG_TARGET}
|
||||
POST_BUILD
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${_runtime_output_dir}/${_plugin_path}
|
||||
BYPRODUCTS ${_runtime_output_dir}/${_plugin_path}
|
||||
)
|
||||
|
||||
get_target_property(_qmldir_file ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_QMLDIR})
|
||||
install(FILES ${_qmldir_file} DESTINATION ${ARG_DESTINATION}/${_plugin_path} RENAME "qmldir")
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${ARG_TARGET}
|
||||
POST_BUILD
|
||||
WORKING_DIRECTORY ${_runtime_output_dir}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${_qmldir_file} ${_plugin_path}/qmldir
|
||||
)
|
||||
|
||||
if (NOT ${_qml_only})
|
||||
install(TARGETS ${ARG_TARGET} DESTINATION ${ARG_DESTINATION}/${_plugin_path})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${ARG_TARGET}
|
||||
POST_BUILD
|
||||
WORKING_DIRECTORY ${_runtime_output_dir}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${ARG_TARGET}> ${_plugin_path}
|
||||
)
|
||||
endif()
|
||||
|
||||
get_target_property(_qml_files ${ARG_TARGET} ${_ECM_QMLMODULE_PROPERTY_FILES})
|
||||
foreach(_file ${_qml_files})
|
||||
get_filename_component(_filename ${_file} NAME)
|
||||
get_property(_path SOURCE ${_file} PROPERTY ${_ECM_QMLMODULE_PROPERTY_PATH})
|
||||
|
||||
set(_file_path "${_plugin_path}/")
|
||||
if (NOT "${_path}" STREQUAL "")
|
||||
set(_file_path "${_plugin_path}/${_path}/")
|
||||
endif()
|
||||
|
||||
install(FILES ${_file} DESTINATION ${ARG_DESTINATION}/${_file_path})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${ARG_TARGET}
|
||||
POST_BUILD
|
||||
WORKING_DIRECTORY ${_runtime_output_dir}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_path}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${_file} ${_file_path}
|
||||
BYPRODUCTS ${_file_path}/${_filename}
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,258 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiemstra@heimr.nl>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Qt 6 implementation of ECMQmlModule
|
||||
|
||||
set(QT_NO_CREATE_VERSIONLESS_FUNCTIONS ON)
|
||||
find_package(Qt6 COMPONENTS Core Qml CONFIG)
|
||||
unset(QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
|
||||
|
||||
if (NOT TARGET Qt6::Qml)
|
||||
message(WARNING "Target Qt6::Qml was not found. ECMQmlModule requires the QML module when building with Qt 6")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Match KDECMakeSettings' RUNTIME_OUTPUT_DIRECTORY so that we can load plugins from
|
||||
# the build directory when running tests. But only if we don't yet have an output directory.
|
||||
if (NOT QT_QML_OUTPUT_DIRECTORY)
|
||||
set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
endif()
|
||||
|
||||
# Stop warning about a changed import prefix.
|
||||
qt6_policy(SET QTP0001 NEW)
|
||||
|
||||
function(ecm_add_qml_module ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "NO_PLUGIN;QT_NO_PLUGIN;GENERATE_PLUGIN_SOURCE" "URI;VERSION;CLASSNAME;OUTPUT_TARGETS" "")
|
||||
|
||||
if ("${ARG_TARGET}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_add_qml_module called without a valid target name.")
|
||||
endif()
|
||||
|
||||
if ("${ARG_URI}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.")
|
||||
endif()
|
||||
|
||||
string(FIND "${ARG_URI}" " " "_space")
|
||||
if (${_space} GREATER_EQUAL 0)
|
||||
message(FATAL_ERROR "ecm_add_qml_module called without a valid module URI.")
|
||||
endif()
|
||||
|
||||
set(_arguments ${ARG_TARGET} URI ${ARG_URI})
|
||||
|
||||
if (ARG_VERSION)
|
||||
list(APPEND _arguments VERSION ${ARG_VERSION})
|
||||
endif()
|
||||
|
||||
if (ARG_CLASSNAME)
|
||||
message(AUTHOR_WARNING "The CLASSNAME argument to ecm_add_qml_module is deprecated for Qt 6; Use CLASS_NAME instead.")
|
||||
list(APPEND _arguments CLASS_NAME ${ARG_CLASSNAME})
|
||||
endif()
|
||||
|
||||
if (ARG_NO_PLUGIN)
|
||||
message(AUTHOR_WARNING "The NO_PLUGIN argument to ecm_add_qml_module is deprecated for Qt 6; For Qt 6 QML modules always require a plugin library. Use GENERATE_PLUGIN_SOURCE instead. If you wish to use the NO_PLUGIN feature from qt_add_qml_module, use QT_NO_PLUGIN.")
|
||||
set(ARG_GENERATE_PLUGIN_SOURCE TRUE)
|
||||
endif()
|
||||
|
||||
if (ARG_QT_NO_PLUGIN)
|
||||
list(APPEND _arguments NO_PLUGIN)
|
||||
endif()
|
||||
|
||||
if (NOT ARG_GENERATE_PLUGIN_SOURCE)
|
||||
list(APPEND _arguments NO_GENERATE_PLUGIN_SOURCE)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET ${ARG_TARGET})
|
||||
list(APPEND _arguments PLUGIN_TARGET ${ARG_TARGET})
|
||||
if (BUILD_SHARED_LIBS)
|
||||
list(APPEND _arguments SHARED)
|
||||
else()
|
||||
list(APPEND _arguments STATIC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# make qmlimportscanner also find QML modules installed
|
||||
# outside of the Qt prefix
|
||||
if(IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_QMLDIR}")
|
||||
list(APPEND _import_paths "${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_QMLDIR}")
|
||||
endif()
|
||||
foreach(_prefix ${CMAKE_PREFIX_PATH})
|
||||
if(IS_DIRECTORY "${_prefix}/${KDE_INSTALL_QMLDIR}")
|
||||
list(APPEND _import_paths "${_prefix}/${KDE_INSTALL_QMLDIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
list(APPEND _arguments IMPORT_PATH ${_import_paths})
|
||||
|
||||
list(APPEND _arguments ${ARG_UNPARSED_ARGUMENTS})
|
||||
|
||||
qt6_add_qml_module(${_arguments} OUTPUT_TARGETS _out_targets)
|
||||
if (ARG_OUTPUT_TARGETS)
|
||||
set(${ARG_OUTPUT_TARGETS} ${_out_targets} PARENT_SCOPE)
|
||||
endif()
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES _ecm_output_targets "${_out_targets}")
|
||||
|
||||
# KDECMakeSettings sets the prefix of MODULE targets to empty but Qt will
|
||||
# not load a QML plugin without prefix. So we need to force it here.
|
||||
qt6_query_qml_module(${ARG_TARGET} PLUGIN_TARGET _plugin_target)
|
||||
if (NOT WIN32 AND TARGET ${_plugin_target})
|
||||
set_target_properties(${_plugin_target} PROPERTIES PREFIX "lib")
|
||||
endif()
|
||||
|
||||
option(VERBOSE_QML_COMPILER "Enable verbose output for qmlcachegen" OFF)
|
||||
|
||||
if (VERBOSE_QML_COMPILER)
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES
|
||||
QT_QMLCACHEGEN_ARGUMENTS "--verbose"
|
||||
)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
function(ecm_add_qml_module_dependencies ARG_TARGET)
|
||||
message(AUTHOR_WARNING "ecm_add_qml_module_dependencies is deprecated for Qt 6; use the DEPENDENCIES argument to ecm_add_qml_module() instead.")
|
||||
endfunction()
|
||||
|
||||
function(ecm_target_qml_sources ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "PRIVATE" "VERSION;PATH;OUTPUT_TARGETS" "SOURCES")
|
||||
|
||||
if ("${ARG_SOURCES}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_target_qml_sources called without required argument SOURCES")
|
||||
endif()
|
||||
|
||||
if (ARG_VERSION)
|
||||
set_source_files_properties(${ARG_SOURCES} PROPERTIES QT_QML_SOURCE_VERSIONS "${ARG_VERSION}")
|
||||
endif()
|
||||
|
||||
if (ARG_PRIVATE)
|
||||
set_source_files_properties(${ARG_SOURCES} PROPERTIES QT_QML_INTERNAL_TYPE TRUE)
|
||||
endif()
|
||||
|
||||
set(_QML_FILES)
|
||||
set(_RESOURCES)
|
||||
foreach(_path ${ARG_SOURCES})
|
||||
get_filename_component(_file "${_path}" NAME)
|
||||
get_filename_component(_ext "${_path}" EXT)
|
||||
set(_resource_alias "${_file}")
|
||||
if (ARG_PATH)
|
||||
set(_resource_alias "${ARG_PATH}/${_file}")
|
||||
endif()
|
||||
set_source_files_properties("${_path}" PROPERTIES QT_RESOURCE_ALIAS "${_resource_alias}")
|
||||
if ("${_ext}" MATCHES "(.qml|.js|.mjs)")
|
||||
list(APPEND _QML_FILES "${_path}")
|
||||
else()
|
||||
list(APPEND _RESOURCES "${_path}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(_QML_FILES)
|
||||
qt6_target_qml_sources(${ARG_TARGET} QML_FILES ${_QML_FILES} OUTPUT_TARGETS _out_targets_qml ${ARG_UNPARSED_ARGUMENTS})
|
||||
endif()
|
||||
if(_RESOURCES)
|
||||
qt6_target_qml_sources(${ARG_TARGET} RESOURCES ${_RESOURCES} OUTPUT_TARGETS _out_targets_qrc ${ARG_UNPARSED_ARGUMENTS})
|
||||
endif()
|
||||
list(APPEND _out_targets "${_out_targets_qml}" "${_out_targets_qrc}")
|
||||
if (ARG_OUTPUT_TARGETS)
|
||||
set(${ARG_OUTPUT_TARGETS} ${_out_targets} PARENT_SCOPE)
|
||||
endif()
|
||||
get_target_property(_ecm_output_targets ${ARG_TARGET} _ecm_output_targets)
|
||||
list(APPEND _ecm_output_targets ${_out_targets})
|
||||
set_target_properties(${ARG_TARGET} PROPERTIES _ecm_output_targets "${_ecm_output_targets}")
|
||||
|
||||
endfunction()
|
||||
|
||||
function(ecm_finalize_qml_module ARG_TARGET)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "DESTINATION;VERSION;EXPORT" "")
|
||||
|
||||
if (NOT ARG_DESTINATION)
|
||||
set(ARG_DESTINATION "${KDE_INSTALL_QMLDIR}")
|
||||
endif()
|
||||
|
||||
if ("${ARG_DESTINATION}" STREQUAL "")
|
||||
message(FATAL_ERROR "ecm_finalize_qml_module called without required argument DESTINATION and KDE_INSTALL_QMLDIR is not set")
|
||||
endif()
|
||||
|
||||
if (NOT ARG_VERSION)
|
||||
set(ARG_VERSION "${PROJECT_VERSION}")
|
||||
endif()
|
||||
|
||||
# This is effectively a workaround for missing upstream API, see QTBUG-100102
|
||||
|
||||
qt6_query_qml_module(${ARG_TARGET}
|
||||
URI module_uri
|
||||
VERSION module_version
|
||||
PLUGIN_TARGET module_plugin_target
|
||||
TARGET_PATH module_target_path
|
||||
QMLDIR module_qmldir
|
||||
TYPEINFO module_typeinfo
|
||||
QML_FILES module_qml_files
|
||||
RESOURCES module_resources
|
||||
)
|
||||
|
||||
set(module_dir "${ARG_DESTINATION}/${module_target_path}")
|
||||
|
||||
if (NOT TARGET "${module_plugin_target}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (ARG_EXPORT AND NOT BUILD_SHARED_LIBS)
|
||||
set(_install_targets_args EXPORT "${ARG_EXPORT}")
|
||||
endif()
|
||||
install(TARGETS "${module_plugin_target}"
|
||||
${_install_targets_args}
|
||||
LIBRARY DESTINATION "${module_dir}"
|
||||
RUNTIME DESTINATION "${module_dir}"
|
||||
)
|
||||
|
||||
# Install dependent targets created for static builds
|
||||
get_target_property(_ecm_output_targets ${ARG_TARGET} _ecm_output_targets)
|
||||
if (_ecm_output_targets AND ARG_EXPORT)
|
||||
install(TARGETS ${_ecm_output_targets}
|
||||
EXPORT ${ARG_EXPORT}
|
||||
LIBRARY DESTINATION "${module_dir}"
|
||||
RUNTIME DESTINATION "${module_dir}"
|
||||
OBJECTS DESTINATION "${module_dir}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Install the QML module meta information.
|
||||
install(FILES "${module_qmldir}" DESTINATION "${module_dir}")
|
||||
install(FILES "${module_typeinfo}" DESTINATION "${module_dir}")
|
||||
|
||||
# Install QML files, possibly renamed.
|
||||
# see Kirigami::StyleSelector::resolveFileX, on Android we never use QML files from disk
|
||||
# but rather always the ones from qrc
|
||||
if (NOT ANDROID)
|
||||
list(LENGTH module_qml_files num_files)
|
||||
if (NOT "${module_qml_files}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0)
|
||||
qt6_query_qml_module(${ARG_TARGET} QML_FILES_DEPLOY_PATHS qml_files_deploy_paths)
|
||||
|
||||
math(EXPR last_index "${num_files} - 1")
|
||||
foreach(i RANGE 0 ${last_index})
|
||||
list(GET module_qml_files ${i} src_file)
|
||||
list(GET qml_files_deploy_paths ${i} deploy_path)
|
||||
get_filename_component(dst_name "${deploy_path}" NAME)
|
||||
get_filename_component(dest_dir "${deploy_path}" DIRECTORY)
|
||||
install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Install resources, possibly renamed.
|
||||
list(LENGTH module_resources num_files)
|
||||
if (NOT "${module_resources}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0)
|
||||
qt6_query_qml_module(${ARG_TARGET} RESOURCES_DEPLOY_PATHS resources_deploy_paths)
|
||||
|
||||
math(EXPR last_index "${num_files} - 1")
|
||||
foreach(i RANGE 0 ${last_index})
|
||||
list(GET module_resources ${i} src_file)
|
||||
list(GET resources_deploy_paths ${i} deploy_path)
|
||||
get_filename_component(dst_name "${deploy_path}" NAME)
|
||||
get_filename_component(dest_dir "${deploy_path}" DIRECTORY)
|
||||
install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}-kde-qmlmodule.version" CONTENT "${ARG_VERSION}\n")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${ARG_TARGET}-kde-qmlmodule.version" DESTINATION "${module_dir}" RENAME "kde-qmlmodule.version")
|
||||
endfunction()
|
||||
+421
@@ -0,0 +1,421 @@
|
||||
# SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMQtDeclareLoggingCategory
|
||||
---------------------------
|
||||
|
||||
This module provides the ``ecm_qt_declare_logging_category`` function for
|
||||
generating declarations for logging categories in Qt5, and the
|
||||
``ecm_qt_install_logging_categories`` function for generating and installing
|
||||
a file in KDebugSettings format with the info about all those categories,
|
||||
as well as a file with info about any renamed categories if defined.
|
||||
To include in that file any logging categories that are manually defined
|
||||
also a function ``ecm_qt_export_logging_category`` is provided.
|
||||
|
||||
::
|
||||
|
||||
ecm_qt_declare_logging_category(<sources_var_name(|target (since 5.80))>
|
||||
HEADER <filename>
|
||||
IDENTIFIER <identifier>
|
||||
CATEGORY_NAME <category_name>
|
||||
[OLD_CATEGORY_NAMES <oldest_cat_name> [<second_oldest_cat_name> [...]]]
|
||||
[DEFAULT_SEVERITY <Debug|Info|Warning|Critical|Fatal>]
|
||||
[EXPORT <exportid>]
|
||||
[DESCRIPTION <description>]
|
||||
)
|
||||
|
||||
A header file, ``<filename>``, will be generated along with a corresponding
|
||||
source file. These will provide a QLoggingCategory category that can be referred
|
||||
to from C++ code using ``<identifier>``, and from the logging configuration using
|
||||
``<category_name>``.
|
||||
|
||||
The generated source file will be added to the variable with the name
|
||||
``<sources_var_name>``. If the given argument is a target though, instead both the
|
||||
generated header file and the generated source file will be added to the target as
|
||||
private sources (since 5.80). The target must not be an alias.
|
||||
|
||||
If ``<filename>`` is not absolute, it will be taken relative to the current
|
||||
binary directory.
|
||||
|
||||
``<identifier>`` may include namespaces (eg: ``foo::bar::IDENT``).
|
||||
|
||||
If ``EXPORT`` is passed, the category will be registered for the group id
|
||||
``<exportid>``. Info about the categories of that group can then be
|
||||
generated in a file and installed by that group id with the
|
||||
``ecm_qt_install_logging_categories`` function. In that case also ``DESCRIPTION``
|
||||
will need to be passed, with ``<description>`` being a short single line text.
|
||||
And ``OLD_CATEGORY_NAMES`` can be used to inform about any renamings of the category,
|
||||
so user settings can be migrated. Since 5.68.0.
|
||||
|
||||
Since 5.14.0.
|
||||
|
||||
::
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER <identifier>
|
||||
CATEGORY_NAME <category_name>
|
||||
[OLD_CATEGORY_NAMES <oldest_category_name> [<second_oldest_category_name> [...]]]
|
||||
EXPORT <exportid>
|
||||
DESCRIPTION <description>
|
||||
[DEFAULT_SEVERITY <Debug|Info|Warning|Critical|Fatal>]
|
||||
)
|
||||
|
||||
Registers a logging category for being included in the generated and
|
||||
installed KDebugSettings files. To be used for categories who are declared by
|
||||
manual code or other ways instead of code generated with
|
||||
``ecm_qt_declare_logging_category``.
|
||||
|
||||
``<identifier>`` may include namespaces (eg: ``foo::bar::IDENT``).
|
||||
|
||||
``EXPORT`` specifies the group id with which the category will be registered.
|
||||
Info about the categories of that group can then be generated in a file and
|
||||
installed by that group id with the ``ecm_qt_install_logging_categories`` function.
|
||||
|
||||
``DESCRIPTION`` specifies a short single line text describing the category.
|
||||
|
||||
``OLD_CATEGORY_NAMES`` can be used to inform about any renamings of the category,
|
||||
so user settings can be migrated.
|
||||
|
||||
Since 5.68.0.
|
||||
|
||||
::
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT <exportid>
|
||||
[FILE <filename>]
|
||||
DESTINATION <install_path>
|
||||
[SORT]
|
||||
[COMPONENT <component>]
|
||||
)
|
||||
|
||||
Generates and installs a file in KDebugSettings format with the info about all
|
||||
the categories registered for the group ``<exportid>``, as well as a file with
|
||||
info about any renamed categories, if there are.
|
||||
|
||||
The method call needs to be after the last ``ecm_qt_declare_logging_category``
|
||||
call which uses the same ``<exportid>``. This can be in the same directory, or
|
||||
any subdirectory or parent directory.
|
||||
|
||||
``EXPORT`` specifies the group id of categories whose information should be
|
||||
stored in the file generated and installed.
|
||||
|
||||
``FILE`` specifies the name of the file generated and installed. It will default
|
||||
to lower-cased ``<exportid>.categories``. The name of the file with info about
|
||||
renamed categories will use the same final base name and the suffix
|
||||
``.renamecategories``. Note: Before 5.113, the base name should not have any
|
||||
further ``.`` in the name, as its end would be defined by that.
|
||||
|
||||
``DESTINATION`` specifies where the generated file will be
|
||||
installed.
|
||||
|
||||
IF ``SORT`` is set, entries will be sorted by identifiers.
|
||||
|
||||
``COMPONENT`` specifies the installation component name with which the install
|
||||
rules for the generated file are associated.
|
||||
|
||||
Since 5.85.0 this is a no-op when building for Android, as KDebugSettings is
|
||||
not available on that platform and the logging category files therefore just
|
||||
bloat the APK.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_qt_declare_logging_category(
|
||||
MYPROJECT_SRCS
|
||||
HEADER "myproject_debug.h"
|
||||
IDENTIFIER "MYPROJECT_DEBUG"
|
||||
CATEGORY_NAME "myproject"
|
||||
OLD_CATEGORY_NAMES "myprojectlog"
|
||||
DESCRIPTION "My project"
|
||||
EXPORT MyProject
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER "MYPROJECT_SUBMODULE_DEBUG"
|
||||
CATEGORY_NAME "myproject.submodule"
|
||||
DESCRIPTION "My project - submodule"
|
||||
EXPORT MyProject
|
||||
)
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT MyProject
|
||||
FILE myproject.categories
|
||||
DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}"
|
||||
)
|
||||
|
||||
Since 5.68.0.
|
||||
#]=======================================================================]
|
||||
|
||||
set(_ECM_QT_DECLARE_LOGGING_CATEGORY_TEMPLATE_CPP "${CMAKE_CURRENT_LIST_DIR}/ECMQtDeclareLoggingCategory.cpp.in")
|
||||
set(_ECM_QT_DECLARE_LOGGING_CATEGORY_TEMPLATE_H "${CMAKE_CURRENT_LIST_DIR}/ECMQtDeclareLoggingCategory.h.in")
|
||||
|
||||
function(ecm_qt_export_logging_category)
|
||||
set(options)
|
||||
set(oneValueArgs IDENTIFIER CATEGORY_NAME DEFAULT_SEVERITY EXPORT DESCRIPTION)
|
||||
set(multiValueArgs OLD_CATEGORY_NAMES)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_qt_export_logging_category: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ARG_IDENTIFIER)
|
||||
message(FATAL_ERROR "Missing IDENTIFIER argument for ecm_qt_export_logging_category")
|
||||
endif()
|
||||
if(NOT ARG_CATEGORY_NAME)
|
||||
message(FATAL_ERROR "Missing CATEGORY_NAME argument for ecm_qt_export_logging_category")
|
||||
endif()
|
||||
if(NOT ARG_DEFAULT_SEVERITY)
|
||||
set(ARG_DEFAULT_SEVERITY Info)
|
||||
set(is_explicite_default_severity FALSE)
|
||||
else()
|
||||
set(acceptible_severities Debug Info Warning Critical Fatal)
|
||||
list(FIND acceptible_severities "${ARG_DEFAULT_SEVERITY}" pos)
|
||||
if (pos EQUAL -1)
|
||||
message(FATAL_ERROR "Unknown DEFAULT_SEVERITY ${pos}")
|
||||
endif()
|
||||
set(is_explicite_default_severity TRUE)
|
||||
endif()
|
||||
if(NOT ARG_EXPORT)
|
||||
message(FATAL_ERROR "Missing EXPORT argument for ecm_qt_export_logging_category.")
|
||||
endif()
|
||||
if(NOT ARG_DESCRIPTION)
|
||||
message(FATAL_ERROR "Missing DESCRIPTION argument for ecm_qt_export_logging_category.")
|
||||
endif()
|
||||
|
||||
# note data in global properties
|
||||
set(_propertyprefix "ECM_QT_LOGGING_CATEGORY_${ARG_EXPORT}")
|
||||
set_property(GLOBAL APPEND PROPERTY "${_propertyprefix}_CATEGORIES" ${ARG_CATEGORY_NAME})
|
||||
set_property(GLOBAL PROPERTY "${_propertyprefix}_IDENTIFIER_${ARG_CATEGORY_NAME}" "${ARG_IDENTIFIER}")
|
||||
set_property(GLOBAL PROPERTY "${_propertyprefix}_DESCRIPTION_${ARG_CATEGORY_NAME}" "${ARG_DESCRIPTION}")
|
||||
set_property(GLOBAL PROPERTY "${_propertyprefix}_OLD_NAMES_${ARG_CATEGORY_NAME}" "${ARG_OLD_CATEGORY_NAMES}")
|
||||
if (is_explicite_default_severity)
|
||||
set_property(GLOBAL PROPERTY "${_propertyprefix}_DEFAULT_SEVERITY_${ARG_CATEGORY_NAME}" "${ARG_DEFAULT_SEVERITY}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_qt_declare_logging_category sources_var)
|
||||
set(options)
|
||||
set(oneValueArgs HEADER IDENTIFIER CATEGORY_NAME DEFAULT_SEVERITY EXPORT DESCRIPTION)
|
||||
set(multiValueArgs OLD_CATEGORY_NAMES)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_qt_declare_logging_category: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ARG_HEADER)
|
||||
message(FATAL_ERROR "Missing HEADER argument for ecm_qt_declare_logging_category")
|
||||
endif()
|
||||
if(NOT ARG_IDENTIFIER)
|
||||
message(FATAL_ERROR "Missing IDENTIFIER argument for ecm_qt_declare_logging_category")
|
||||
endif()
|
||||
if(NOT ARG_CATEGORY_NAME)
|
||||
message(FATAL_ERROR "Missing CATEGORY_NAME argument for ecm_qt_declare_logging_category")
|
||||
endif()
|
||||
if(NOT ARG_DEFAULT_SEVERITY)
|
||||
set(ARG_DEFAULT_SEVERITY Info)
|
||||
set(is_explicite_default_severity FALSE)
|
||||
else()
|
||||
set(acceptible_severities Debug Info Warning Critical Fatal)
|
||||
list(FIND acceptible_severities "${ARG_DEFAULT_SEVERITY}" pos)
|
||||
if (pos EQUAL -1)
|
||||
message(FATAL_ERROR "Unknown DEFAULT_SEVERITY ${pos}")
|
||||
endif()
|
||||
set(is_explicite_default_severity TRUE)
|
||||
endif()
|
||||
if(ARG_EXPORT AND NOT ARG_DESCRIPTION)
|
||||
message(FATAL_ERROR "Missing DESCRIPTION argument for ecm_qt_declare_logging_category.")
|
||||
endif()
|
||||
if (TARGET ${sources_var})
|
||||
get_target_property(aliased_target ${sources_var} ALIASED_TARGET)
|
||||
if(aliased_target)
|
||||
message(FATAL_ERROR "Target argument passed to ecm_qt_declare_logging_category must not be an alias: ${sources_var}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT IS_ABSOLUTE "${ARG_HEADER}")
|
||||
set(ARG_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${ARG_HEADER}")
|
||||
endif()
|
||||
|
||||
string(REPLACE "::" ";" namespaces "${ARG_IDENTIFIER}")
|
||||
list(LENGTH namespaces len)
|
||||
math(EXPR last_pos "${len} - 1")
|
||||
list(GET namespaces ${last_pos} IDENTIFIER)
|
||||
list(REMOVE_AT namespaces ${last_pos})
|
||||
|
||||
set(OPEN_NAMESPACES)
|
||||
set(CLOSE_NAMESPACES)
|
||||
foreach(ns ${namespaces})
|
||||
set(OPEN_NAMESPACES "${OPEN_NAMESPACES} namespace ${ns} {")
|
||||
set(CLOSE_NAMESPACES "} ${CLOSE_NAMESPACES}")
|
||||
endforeach()
|
||||
|
||||
string(FIND "${ARG_HEADER}" "." pos REVERSE)
|
||||
if (pos EQUAL -1)
|
||||
set(cpp_filename "${ARG_HEADER}.cpp")
|
||||
else()
|
||||
string(SUBSTRING "${ARG_HEADER}" 0 ${pos} cpp_filename)
|
||||
set(cpp_filename "${cpp_filename}.cpp")
|
||||
endif()
|
||||
|
||||
get_filename_component(HEADER_NAME "${ARG_HEADER}" NAME)
|
||||
|
||||
string(REGEX REPLACE "[^a-zA-Z0-9]" "_" GUARD_NAME "${HEADER_NAME}")
|
||||
string(REPLACE "::" "_" GUARD_PREFIX "ECM_QLOGGINGCATEGORY_${ARG_IDENTIFIER}")
|
||||
string(TOUPPER "${GUARD_PREFIX}_${GUARD_NAME}" GUARD_NAME)
|
||||
|
||||
if (NOT _ECM_QT_DECLARE_LOGGING_CATEGORY_TEMPLATE_CPP)
|
||||
message(FATAL_ERROR "You must include(ECMQtDeclareLoggingCategory) before using ecm_qt_declare_logging_category")
|
||||
endif()
|
||||
|
||||
configure_file("${_ECM_QT_DECLARE_LOGGING_CATEGORY_TEMPLATE_CPP}" "${cpp_filename}")
|
||||
configure_file("${_ECM_QT_DECLARE_LOGGING_CATEGORY_TEMPLATE_H}" "${ARG_HEADER}")
|
||||
|
||||
if(TARGET ${sources_var})
|
||||
target_sources(${sources_var} PRIVATE ${cpp_filename} "${ARG_HEADER}")
|
||||
else()
|
||||
set(sources "${${sources_var}}")
|
||||
list(APPEND sources "${cpp_filename}")
|
||||
set(${sources_var} "${sources}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
# note data in global properties
|
||||
if (ARG_EXPORT)
|
||||
set(_default_severity)
|
||||
if (is_explicite_default_severity)
|
||||
set(_default_severity DEFAULT_SEVERITY ${ARG_DEFAULT_SEVERITY})
|
||||
endif()
|
||||
set(_old_category_name)
|
||||
if (ARG_OLD_CATEGORY_NAMES)
|
||||
set(_old_category_names OLD_CATEGORY_NAMES ${ARG_OLD_CATEGORY_NAMES})
|
||||
endif()
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER ${ARG_IDENTIFIER}
|
||||
CATEGORY_NAME ${ARG_CATEGORY_NAME}
|
||||
${_old_category_names}
|
||||
${_default_severity}
|
||||
EXPORT ${ARG_EXPORT}
|
||||
DESCRIPTION "${ARG_DESCRIPTION}"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(ecm_qt_install_logging_categories)
|
||||
# on Android there is no KDebugSettings, and thus the logging categories files make no sense in APKs
|
||||
if (ANDROID)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(options SORT)
|
||||
set(oneValueArgs FILE EXPORT DESTINATION COMPONENT)
|
||||
set(multiValueArgs)
|
||||
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT ARGS_EXPORT)
|
||||
message(FATAL_ERROR "Missing EXPORT argument for ecm_qt_install_logging_categories")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_DESTINATION)
|
||||
message(FATAL_ERROR "Missing DESTINATION argument for ecm_qt_install_logging_categories")
|
||||
endif()
|
||||
|
||||
if(NOT ARGS_FILE)
|
||||
string(TOLOWER "${ARGS_EXPORT}.categories" ARGS_FILE)
|
||||
endif()
|
||||
|
||||
set(_propertyprefix "ECM_QT_LOGGING_CATEGORY_${ARGS_EXPORT}")
|
||||
get_property(has_category GLOBAL PROPERTY "${_propertyprefix}_CATEGORIES" SET)
|
||||
|
||||
if (NOT has_category)
|
||||
message(AUTHOR_WARNING "No Qt logging categories exported for \"${ARGS_EXPORT}\", generating & installing an empty file.")
|
||||
endif()
|
||||
|
||||
get_property(_categories GLOBAL PROPERTY "${_propertyprefix}_CATEGORIES")
|
||||
if (ARGS_SORT)
|
||||
list(SORT _categories)
|
||||
endif()
|
||||
|
||||
set(_renamed_categories)
|
||||
|
||||
# generate categories file
|
||||
if (NOT IS_ABSOLUTE "${ARGS_FILE}")
|
||||
set(ARGS_FILE "${CMAKE_CURRENT_BINARY_DIR}/${ARGS_FILE}")
|
||||
endif()
|
||||
|
||||
set(_categories_content
|
||||
"# KDebugSettings data file
|
||||
# This file was generated by ecm_qt_install_logging_categories(). DO NOT EDIT!
|
||||
|
||||
")
|
||||
|
||||
foreach(_category IN LISTS _categories)
|
||||
get_property(_description GLOBAL PROPERTY "${_propertyprefix}_DESCRIPTION_${_category}")
|
||||
get_property(_identifier GLOBAL PROPERTY "${_propertyprefix}_IDENTIFIER_${_category}")
|
||||
get_property(_default_severity GLOBAL PROPERTY "${_propertyprefix}_DEFAULT_SEVERITY_${_category}")
|
||||
if (_default_severity)
|
||||
string(TOUPPER "${_default_severity}" _default_severity)
|
||||
set(_default_severity "DEFAULT_SEVERITY [${_default_severity}] ") # final space wanted
|
||||
endif()
|
||||
get_property(_old_category_names GLOBAL PROPERTY "${_propertyprefix}_OLD_NAMES_${_category}")
|
||||
if (_old_category_names)
|
||||
list(APPEND _renamed_categories ${_category})
|
||||
endif()
|
||||
|
||||
# Format:
|
||||
# logname<space>description(optional <space> DEFAULT_SEVERITY [DEFAULT_CATEGORY] as WARNING/DEBUG/INFO/CRITICAL) optional IDENTIFIER [...])
|
||||
string(APPEND _categories_content "${_category} ${_description} ${_default_severity}IDENTIFIER [${_identifier}]\n")
|
||||
endforeach()
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT ${ARGS_FILE}
|
||||
CONTENT ${_categories_content}
|
||||
)
|
||||
|
||||
set(_renamed_cats_file)
|
||||
if (_renamed_categories)
|
||||
get_filename_component(_dir ${ARGS_FILE} DIRECTORY)
|
||||
get_filename_component(_base_name ${ARGS_FILE} NAME_WLE)
|
||||
set(_renamed_cats_file "${_dir}/${_base_name}.renamecategories")
|
||||
set(_renamed_cats_content
|
||||
"# KDebugSettings data file
|
||||
# This file was generated by ecm_qt_install_logging_categories(). DO NOT EDIT!
|
||||
|
||||
")
|
||||
|
||||
foreach(_category IN LISTS _renamed_categories)
|
||||
get_property(_category_name_history GLOBAL PROPERTY "${_propertyprefix}_OLD_NAMES_${_category}")
|
||||
|
||||
list(APPEND _category_name_history ${_category})
|
||||
list(GET _category_name_history 0 _old_category_name)
|
||||
list(REMOVE_AT _category_name_history 0)
|
||||
foreach(_category_name IN LISTS _category_name_history)
|
||||
# Format:
|
||||
# oldlogname<space>newlogname
|
||||
string(APPEND _renamed_cats_content "${_old_category_name} ${_category_name}\n")
|
||||
set(_old_category_name ${_category_name})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT ${_renamed_cats_file}
|
||||
CONTENT ${_renamed_cats_content}
|
||||
)
|
||||
endif()
|
||||
|
||||
# install files
|
||||
set(_component_install)
|
||||
if (ARGS_COMPONENT)
|
||||
set(_component_install COMPONENT ${ARGS_COMPONENT})
|
||||
endif()
|
||||
install(
|
||||
FILES ${ARGS_FILE} ${_renamed_cats_file}
|
||||
DESTINATION "${ARGS_DESTINATION}"
|
||||
${_component_install}
|
||||
)
|
||||
endfunction()
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// This file was generated by ecm_qt_declare_logging_category(): DO NOT EDIT!
|
||||
|
||||
#include "@HEADER_NAME@"
|
||||
|
||||
@OPEN_NAMESPACES@
|
||||
Q_LOGGING_CATEGORY(@IDENTIFIER@, "@ARG_CATEGORY_NAME@", Qt@ARG_DEFAULT_SEVERITY@Msg)
|
||||
@CLOSE_NAMESPACES@
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// This file was generated by ecm_qt_declare_logging_category(): DO NOT EDIT!
|
||||
|
||||
#ifndef @GUARD_NAME@
|
||||
#define @GUARD_NAME@
|
||||
|
||||
#include <QLoggingCategory>
|
||||
@OPEN_NAMESPACES@
|
||||
Q_DECLARE_LOGGING_CATEGORY(@IDENTIFIER@)
|
||||
@CLOSE_NAMESPACES@
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
if (${ECM_GLOBAL_FIND_VERSION} VERSION_GREATER_EQUAL 5.93)
|
||||
message(DEPRECATION "ECMQueryQmake.cmake is deprecated since 5.93, please use ECMQueryQt.cmake instead.")
|
||||
endif()
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||
find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
|
||||
|
||||
if (Qt5Core_FOUND)
|
||||
set(_qmake_executable_default "qmake-qt5")
|
||||
endif ()
|
||||
if (TARGET Qt5::qmake)
|
||||
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
|
||||
endif()
|
||||
set(QMAKE_EXECUTABLE ${_qmake_executable_default}
|
||||
CACHE FILEPATH "Location of the Qt5 qmake executable")
|
||||
|
||||
# Helper method
|
||||
# This is not public API (yet)!
|
||||
# Usage: query_qmake(<result_variable> <qt_variable> [TRY])
|
||||
# Passing TRY will result in the method not failing fatal if no qmake executable
|
||||
# has been found, but instead simply returning an empty string
|
||||
function(query_qmake result_variable qt_variable)
|
||||
set(options TRY)
|
||||
set(oneValueArgs )
|
||||
set(multiValueArgs )
|
||||
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT QMAKE_EXECUTABLE)
|
||||
if(ARGS_TRY)
|
||||
set(${result_variable} "" PARENT_SCOPE)
|
||||
message(STATUS "No qmake Qt5 binary found. Can't check ${qt_variable}")
|
||||
return()
|
||||
else()
|
||||
message(FATAL_ERROR "No qmake Qt5 binary found. Can't check ${qt_variable} as required")
|
||||
endif()
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}"
|
||||
RESULT_VARIABLE return_code
|
||||
OUTPUT_VARIABLE output
|
||||
)
|
||||
if(return_code EQUAL 0)
|
||||
string(STRIP "${output}" output)
|
||||
file(TO_CMAKE_PATH "${output}" output_path)
|
||||
set(${result_variable} "${output_path}" PARENT_SCOPE)
|
||||
else()
|
||||
message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"")
|
||||
message(FATAL_ERROR "QMake call failed: ${return_code}")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,98 @@
|
||||
# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
|
||||
# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#[=======================================================================[.rst:
|
||||
ECMQueryQt
|
||||
---------------
|
||||
This module can be used to query the installation paths used by Qt.
|
||||
|
||||
For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
|
||||
support to query the paths of a target platform when cross-compiling).
|
||||
|
||||
This module defines the following function:
|
||||
::
|
||||
|
||||
ecm_query_qt(<result_variable> <qt_variable> [TRY])
|
||||
|
||||
Passing ``TRY`` will result in the method not making the build fail if the executable
|
||||
used for querying has not been found, but instead simply print a warning message and
|
||||
return an empty string.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
include(ECMQueryQt)
|
||||
ecm_query_qt(bin_dir QT_INSTALL_BINS)
|
||||
|
||||
If the call succeeds ``${bin_dir}`` will be set to ``<prefix>/path/to/bin/dir`` (e.g.
|
||||
``/usr/lib64/qt/bin/``).
|
||||
|
||||
Since: 5.93
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||
include(CheckLanguage)
|
||||
check_language(CXX)
|
||||
if (CMAKE_CXX_COMPILER)
|
||||
# Enable the CXX language to let CMake look for config files in library dirs.
|
||||
# See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
|
||||
enable_language(CXX)
|
||||
endif()
|
||||
|
||||
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||
# QUIET to accommodate the TRY option
|
||||
find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
|
||||
set(_exec_name_text "Qt5 qmake")
|
||||
if(TARGET Qt5::qmake)
|
||||
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
|
||||
|
||||
set(QUERY_EXECUTABLE ${_qmake_executable_default})
|
||||
set(_cli_option "-query")
|
||||
endif()
|
||||
elseif(QT_MAJOR_VERSION STREQUAL "6")
|
||||
# QUIET to accommodate the TRY option
|
||||
find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
|
||||
set(_exec_name_text "Qt6 qtpaths")
|
||||
if (TARGET Qt6::qtpaths)
|
||||
get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
|
||||
|
||||
set(QUERY_EXECUTABLE ${_qtpaths_executable})
|
||||
set(_cli_option "--query")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(ecm_query_qt result_variable qt_variable)
|
||||
set(options TRY)
|
||||
set(oneValueArgs)
|
||||
set(multiValueArgs)
|
||||
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT QUERY_EXECUTABLE)
|
||||
if(ARGS_TRY)
|
||||
set(${result_variable} "" PARENT_SCOPE)
|
||||
message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
|
||||
return()
|
||||
else()
|
||||
message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
|
||||
endif()
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
|
||||
RESULT_VARIABLE return_code
|
||||
OUTPUT_VARIABLE output
|
||||
)
|
||||
if(return_code EQUAL 0)
|
||||
string(STRIP "${output}" output)
|
||||
file(TO_CMAKE_PATH "${output}" output_path)
|
||||
set(${result_variable} "${output_path}" PARENT_SCOPE)
|
||||
else()
|
||||
message(WARNING "Failed call: ${QUERY_EXECUTABLE} ${_cli_option} ${qt_variable}")
|
||||
message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
|
||||
endif()
|
||||
endfunction()
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
# SPDX-FileCopyrightText: 2018 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMSetupQtPluginMacroNames
|
||||
--------------------------
|
||||
|
||||
Instruct CMake's automoc about C++ preprocessor macros used to define Qt-style plugins.
|
||||
|
||||
::
|
||||
|
||||
ecm_setup_qtplugin_macro_names(
|
||||
[JSON_NONE <macro_name> [<macro_name> [...]]]
|
||||
[JSON_ARG1 <macro_name> [<macro_name> [...]]]
|
||||
[JSON_ARG2 <macro_name> [<macro_name> [...]]]
|
||||
[JSON_ARG3 <macro_name> [<macro_name> [...]]]
|
||||
[CONFIG_CODE_VARIABLE <variable_name>] )
|
||||
|
||||
CMake's automoc needs some support when parsing C++ source files to detect whether moc
|
||||
should be run on those files and if there are also dependencies on other files, like those
|
||||
with Qt plugin metadata in JSON format. Because automoc just greps overs the raw plain text
|
||||
of the sources without any C++ preprocessor-like processing.
|
||||
CMake in newer versions provides the variables ``CMAKE_AUTOMOC_DEPEND_FILTERS`` (CMake >= 3.9.0)
|
||||
and ``CMAKE_AUTOMOC_MACRO_NAMES`` (CMake >= 3.10) to allow the developer to assist automoc.
|
||||
|
||||
This macro cares for the explicit setup needed for those variables for common cases of
|
||||
C++ preprocessor macros used for Qt-style plugins.
|
||||
|
||||
``JSON_NONE`` lists the names of C++ preprocessor macros for Qt-style plugins which do not refer to
|
||||
external files with the plugin metadata.
|
||||
|
||||
``JSON_ARG1`` lists the names of C++ preprocessor macros for Qt-style plugins where the first argument
|
||||
to the macro is the name of the external file with the plugin metadata.
|
||||
|
||||
``JSON_ARG2`` is the same as ``JSON_ARG1`` but with the file name being the second argument.
|
||||
|
||||
``JSON_ARG3`` is the same as ``JSON_ARG1`` but with the file name being the third argument.
|
||||
|
||||
``CONFIG_CODE_VARIABLE`` specifies the name of the variable which will get set as
|
||||
value some generated CMake code for instructing automoc for the given macro names,
|
||||
as useful in an installed CMake config file. The variable can then be used as usual in
|
||||
the template file for such a CMake config file, by ``@<variable_name>@``.
|
||||
|
||||
|
||||
Example usage:
|
||||
|
||||
Given some plugin-oriented Qt-based software which defines a custom C++ preprocessor
|
||||
macro ``EXPORT_MYPLUGIN`` for declaring the central plugin object:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#define EXPORT_MYPLUGIN_WITH_JSON(classname, jsonFile) \
|
||||
class classname : public QObject \
|
||||
{ \
|
||||
Q_OBJECT \
|
||||
Q_PLUGIN_METADATA(IID "myplugin" FILE jsonFile) \
|
||||
explicit classname() {} \
|
||||
};
|
||||
|
||||
In the CMake buildsystem of the library one calls
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_setup_qtplugin_macro_names(
|
||||
JSON_ARG2
|
||||
EXPORT_MYPLUGIN_WITH_JSON
|
||||
)
|
||||
|
||||
to instruct automoc about the usage of that macro in the sources of the
|
||||
library itself.
|
||||
|
||||
Given the software installs a library including the header with the macro
|
||||
definition and a CMake config file, so 3rd-party can create additional
|
||||
plugins by linking against the library, one passes additionally the name of
|
||||
a variable which shall be set as value the CMake code needed to instruct
|
||||
automoc about the usage of that macro.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ecm_setup_qtplugin_macro_names(
|
||||
JSON_ARG2
|
||||
EXPORT_MYPLUGIN_WITH_JSON
|
||||
CONFIG_CODE_VARIABLE
|
||||
PACKAGE_SETUP_AUTOMOC_VARIABLES
|
||||
)
|
||||
|
||||
This variable then is used in the template file (e.g.
|
||||
``MyProjectConfig.cmake.in``) for the libary's installed CMake config file
|
||||
and that way will ensure that in the 3rd-party plugin's buildsystem
|
||||
automoc is instructed as well as needed:
|
||||
|
||||
::
|
||||
|
||||
@PACKAGE_SETUP_AUTOMOC_VARIABLES@
|
||||
|
||||
Since 5.45.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
macro(ecm_setup_qtplugin_macro_names)
|
||||
set(options )
|
||||
set(oneValueArgs CONFIG_CODE_VARIABLE)
|
||||
set(multiValueArgs JSON_NONE JSON_ARG1 JSON_ARG2 JSON_ARG3)
|
||||
|
||||
cmake_parse_arguments(ESQMN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ESQMN_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_QTPLUGIN_MACRO_NAMES(): \"${ESQMN_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
# CMAKE_AUTOMOC_MACRO_NAMES
|
||||
list(APPEND CMAKE_AUTOMOC_MACRO_NAMES
|
||||
${ESQMN_JSON_NONE}
|
||||
${ESQMN_JSON_ARG1}
|
||||
${ESQMN_JSON_ARG2}
|
||||
${ESQMN_JSON_ARG3}
|
||||
)
|
||||
|
||||
# CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
# CMake's automoc needs help to find names of plugin metadata files in case Q_PLUGIN_METADATA
|
||||
# is indirectly used via other C++ preprocessor macros
|
||||
foreach(macro_name ${ESQMN_JSON_ARG1})
|
||||
list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
"${macro_name}"
|
||||
"[\n^][ \t]*${macro_name}[ \t\n]*\\([ \t\n]*\"([^\"]+)\""
|
||||
)
|
||||
endforeach()
|
||||
foreach(macro_name ${ESQMN_JSON_ARG2})
|
||||
list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
"${macro_name}"
|
||||
"[\n^][ \t]*${macro_name}[ \t\n]*\\([^,]*,[ \t\n]*\"([^\"]+)\""
|
||||
)
|
||||
endforeach()
|
||||
foreach(macro_name ${ESQMN_JSON_ARG3})
|
||||
list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
"${macro_name}"
|
||||
"[\n^][ \t]*${macro_name}[ \t\n]*\\([^,]*,[^,]*,[ \t\n]*\"([^\"]+)\""
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if (ESQMN_CONFIG_CODE_VARIABLE)
|
||||
# As CMake config files of one project can be included multiple times,
|
||||
# the code to add entries to CMAKE_AUTOMOC_MACRO_NAMES & CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
# would then be also executed multiple times.
|
||||
# While there currently is no simple way known to have a unique flag to track
|
||||
# if the code was already executed, at least by the data currently passed to this macro,
|
||||
# simply check for the presence of the new entries before adding them for now.
|
||||
# Not using IN_LIST in generated code, as projects might have CMP0057 set to OLD
|
||||
set(_content
|
||||
"####################################################################################
|
||||
# CMAKE_AUTOMOC
|
||||
")
|
||||
set(_all_macro_names
|
||||
${ESQMN_JSON_NONE}
|
||||
${ESQMN_JSON_ARG1}
|
||||
${ESQMN_JSON_ARG2}
|
||||
${ESQMN_JSON_ARG3}
|
||||
)
|
||||
string(APPEND _content "
|
||||
# CMake 3.9+ warns about automoc on files without Q_OBJECT, and doesn't know about other macros.
|
||||
# 3.10+ lets us provide more macro names that require automoc.
|
||||
foreach(macro_name ${_all_macro_names})
|
||||
# we can be run multiple times, so add only once
|
||||
list (FIND CMAKE_AUTOMOC_MACRO_NAMES \"\${macro_name}\" _index)
|
||||
if(_index LESS 0)
|
||||
list(APPEND CMAKE_AUTOMOC_MACRO_NAMES \${macro_name})
|
||||
endif()
|
||||
endforeach()
|
||||
")
|
||||
|
||||
if(ESQMN_JSON_ARG1 OR ESQMN_JSON_ARG2 OR ESQMN_JSON_ARG3)
|
||||
string(APPEND _content "
|
||||
# CMake's automoc needs help to find names of plugin metadata files in case Q_PLUGIN_METADATA
|
||||
# is indirectly used via other C++ preprocessor macros
|
||||
")
|
||||
if(ESQMN_JSON_ARG1)
|
||||
string(APPEND _content
|
||||
"foreach(macro_name ${ESQMN_JSON_ARG1})
|
||||
# we can be run multiple times, so add only once
|
||||
list (FIND CMAKE_AUTOMOC_DEPEND_FILTERS \"\${macro_name}\" _index)
|
||||
if(_index LESS 0)
|
||||
list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
\"\${macro_name}\"
|
||||
\"[\\n^][ \\t]*\${macro_name}[ \\t\\n]*\\\\([ \\t\\n]*\\\"([^\\\"]+)\\\"\"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
")
|
||||
endif()
|
||||
if(ESQMN_JSON_ARG2)
|
||||
string(APPEND _content
|
||||
"foreach(macro_name ${ESQMN_JSON_ARG2})
|
||||
# we can be run multiple times, so add only once
|
||||
list (FIND CMAKE_AUTOMOC_DEPEND_FILTERS \"\${macro_name}\" _index)
|
||||
if(_index LESS 0)
|
||||
list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
\"\${macro_name}\"
|
||||
\"[\\n^][ \\t]*\${macro_name}[ \\t\\n]*\\\\([^,]*,[ \\t\\n]*\\\"([^\\\"]+)\\\"\"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
")
|
||||
endif()
|
||||
if(ESQMN_JSON_ARG3)
|
||||
string(APPEND _content
|
||||
"foreach(macro_name ${ESQMN_JSON_ARG3})
|
||||
# we can be run multiple times, so add only once
|
||||
list (FIND CMAKE_AUTOMOC_DEPEND_FILTERS \"\${macro_name}\" _index)
|
||||
if(_index LESS 0)
|
||||
list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
\"\${macro_name}\"
|
||||
\"[\\n^][ \\t]*\${macro_name}[ \\t\\n]*\\\\([^,]*,[^,]*,[ \\t\\n]*\\\"([^\\\"]+)\\\"\"
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
")
|
||||
endif()
|
||||
endif()
|
||||
string(APPEND _content "
|
||||
####################################################################################"
|
||||
)
|
||||
|
||||
set(${ESQMN_CONFIG_CODE_VARIABLE} ${_content})
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -0,0 +1,213 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2012 Alexander Neundorf <neundorf@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMSetupVersion
|
||||
---------------
|
||||
|
||||
Handle library version information.
|
||||
|
||||
::
|
||||
|
||||
ecm_setup_version(<version>
|
||||
VARIABLE_PREFIX <prefix>
|
||||
[SOVERSION <soversion>]
|
||||
[VERSION_HEADER <filename>]
|
||||
[PACKAGE_VERSION_FILE <filename> [COMPATIBILITY <compat>]] )
|
||||
|
||||
This parses a version string and sets up a standard set of version variables.
|
||||
It can optionally also create a C version header file and a CMake package
|
||||
version file to install along with the library.
|
||||
|
||||
If the ``<version>`` argument is of the form ``<major>.<minor>.<patch>``
|
||||
(or ``<major>.<minor>.<patch>.<tweak>``), The following CMake variables are
|
||||
set::
|
||||
|
||||
<prefix>_VERSION_MAJOR - <major>
|
||||
<prefix>_VERSION_MINOR - <minor>
|
||||
<prefix>_VERSION_PATCH - <patch>
|
||||
<prefix>_VERSION - <version>
|
||||
<prefix>_SOVERSION - <soversion>, or <major> if SOVERSION was not given
|
||||
|
||||
For backward-compatibility also this variable is set (only if the minimum required
|
||||
version of ECM is < 5.83)::
|
||||
|
||||
<prefix>_VERSION_STRING - <version> (use <prefix>_VERSION instead)
|
||||
|
||||
If CMake policy CMP0048 is not ``NEW``, the following CMake variables will also
|
||||
be set::
|
||||
|
||||
PROJECT_VERSION_MAJOR - <major>
|
||||
PROJECT_VERSION_MINOR - <minor>
|
||||
PROJECT_VERSION_PATCH - <patch>
|
||||
PROJECT_VERSION - <version>
|
||||
|
||||
For backward-compatibility, if CMake policy CMP0048 is not ``NEW``, also this variable is set
|
||||
(only if the minimum required version of ECM is < 5.83)::
|
||||
|
||||
PROJECT_VERSION_STRING - <version> (use PROJECT_VERSION instead)
|
||||
|
||||
If the ``VERSION_HEADER`` option is used, a simple C header is generated with the
|
||||
given filename. If filename is a relative path, it is interpreted as relative
|
||||
to ``CMAKE_CURRENT_BINARY_DIR``. The generated header contains the following
|
||||
macros::
|
||||
|
||||
<prefix>_VERSION_MAJOR - <major> as an integer
|
||||
<prefix>_VERSION_MINOR - <minor> as an integer
|
||||
<prefix>_VERSION_PATCH - <patch> as an integer
|
||||
<prefix>_VERSION_STRING - <version> as a C string
|
||||
<prefix>_VERSION - the version as an integer
|
||||
|
||||
``<prefix>_VERSION`` has ``<patch>`` in the bottom 8 bits, ``<minor>`` in the
|
||||
next 8 bits and ``<major>`` in the remaining bits. Note that ``<patch>`` and
|
||||
``<minor>`` must be less than 256.
|
||||
|
||||
If the ``PACKAGE_VERSION_FILE`` option is used, a simple CMake package version
|
||||
file is created using the ``write_basic_package_version_file()`` macro provided by
|
||||
CMake. It should be installed in the same location as the Config.cmake file of
|
||||
the library so that it can be found by ``find_package()``. If the filename is a
|
||||
relative path, it is interpreted as relative to ``CMAKE_CURRENT_BINARY_DIR``. The
|
||||
optional ``COMPATIBILITY`` option is forwarded to
|
||||
``write_basic_package_version_file()``, and defaults to ``AnyNewerVersion``.
|
||||
|
||||
If CMake policy CMP0048 is ``NEW``, an alternative form of the command is
|
||||
available::
|
||||
|
||||
ecm_setup_version(PROJECT
|
||||
[VARIABLE_PREFIX <prefix>]
|
||||
[SOVERSION <soversion>]
|
||||
[VERSION_HEADER <filename>]
|
||||
[PACKAGE_VERSION_FILE <filename>] )
|
||||
|
||||
This will use the version information set by the ``project()`` command.
|
||||
``VARIABLE_PREFIX`` defaults to the project name. Note that ``PROJECT`` must be the
|
||||
first argument. In all other respects, it behaves like the other form of the
|
||||
command.
|
||||
|
||||
Since pre-1.0.0.
|
||||
|
||||
``COMPATIBILITY`` option available since 1.6.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# save the location of the header template while CMAKE_CURRENT_LIST_DIR
|
||||
# has the value we want
|
||||
set(_ECM_SETUP_VERSION_HEADER_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/ECMVersionHeader.h.in")
|
||||
|
||||
function(ecm_setup_version _version)
|
||||
set(options )
|
||||
set(oneValueArgs VARIABLE_PREFIX SOVERSION VERSION_HEADER PACKAGE_VERSION_FILE COMPATIBILITY)
|
||||
set(multiValueArgs )
|
||||
|
||||
cmake_parse_arguments(ESV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ESV_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_VERSION(): \"${ESV_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
set(project_manages_version FALSE)
|
||||
set(use_project_version FALSE)
|
||||
cmake_policy(GET CMP0048 project_version_policy)
|
||||
if(project_version_policy STREQUAL "NEW")
|
||||
set(project_manages_version TRUE)
|
||||
if(_version STREQUAL "PROJECT")
|
||||
set(use_project_version TRUE)
|
||||
endif()
|
||||
elseif(_version STREQUAL "PROJECT")
|
||||
message(FATAL_ERROR "ecm_setup_version given PROJECT argument, but CMP0048 is not NEW")
|
||||
endif()
|
||||
|
||||
set(should_set_prefixed_vars TRUE)
|
||||
if(NOT ESV_VARIABLE_PREFIX)
|
||||
if(use_project_version)
|
||||
set(ESV_VARIABLE_PREFIX "${PROJECT_NAME}")
|
||||
set(should_set_prefixed_vars FALSE)
|
||||
else()
|
||||
message(FATAL_ERROR "Required argument PREFIX missing in ECM_SETUP_VERSION() call")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(use_project_version)
|
||||
set(_version "${PROJECT_VERSION}")
|
||||
# drop leading 0 from values to avoid bogus octal values in c/C++ e.g. with 08 or 09
|
||||
string(REGEX REPLACE "0*([0-9]+)" "\\1" _major "${PROJECT_VERSION_MAJOR}")
|
||||
string(REGEX REPLACE "0*([0-9]+)" "\\1" _minor "${PROJECT_VERSION_MINOR}")
|
||||
string(REGEX REPLACE "0*([0-9]+)" "\\1" _patch "${PROJECT_VERSION_PATCH}")
|
||||
else()
|
||||
string(REGEX REPLACE "^0*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.0*([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.0*([0-9]+).*" "\\1" _patch "${_version}")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ESV_SOVERSION) # use DEFINED, so "0" as valid SO version is not evaluated to FALSE
|
||||
set(ESV_SOVERSION ${_major})
|
||||
endif()
|
||||
|
||||
if(ECM_GLOBAL_FIND_VERSION VERSION_LESS 5.83.0)
|
||||
set(_set_backward_compat_version_string_vars TRUE)
|
||||
else()
|
||||
set(_set_backward_compat_version_string_vars FALSE)
|
||||
endif()
|
||||
|
||||
if(should_set_prefixed_vars)
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION "${_version}")
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR ${_major})
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR ${_minor})
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH ${_patch})
|
||||
endif()
|
||||
|
||||
set(${ESV_VARIABLE_PREFIX}_SOVERSION ${ESV_SOVERSION})
|
||||
|
||||
if(NOT project_manages_version)
|
||||
set(PROJECT_VERSION "${_version}")
|
||||
set(PROJECT_VERSION_MAJOR "${_major}")
|
||||
set(PROJECT_VERSION_MINOR "${_minor}")
|
||||
set(PROJECT_VERSION_PATCH "${_patch}")
|
||||
endif()
|
||||
|
||||
if(_set_backward_compat_version_string_vars)
|
||||
set(PROJECT_VERSION_STRING "${PROJECT_VERSION}")
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}")
|
||||
endif()
|
||||
|
||||
if(ESV_VERSION_HEADER)
|
||||
set(HEADER_PREFIX "${ESV_VARIABLE_PREFIX}")
|
||||
set(HEADER_VERSION "${_version}")
|
||||
set(HEADER_VERSION_MAJOR "${_major}")
|
||||
set(HEADER_VERSION_MINOR "${_minor}")
|
||||
set(HEADER_VERSION_PATCH "${_patch}")
|
||||
configure_file("${_ECM_SETUP_VERSION_HEADER_TEMPLATE}" "${ESV_VERSION_HEADER}")
|
||||
endif()
|
||||
|
||||
if(ESV_PACKAGE_VERSION_FILE)
|
||||
if(NOT ESV_COMPATIBILITY)
|
||||
set(ESV_COMPATIBILITY AnyNewerVersion)
|
||||
endif()
|
||||
write_basic_package_version_file("${ESV_PACKAGE_VERSION_FILE}" VERSION ${_version} COMPATIBILITY ${ESV_COMPATIBILITY})
|
||||
endif()
|
||||
|
||||
if(should_set_prefixed_vars)
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR "${${ESV_VARIABLE_PREFIX}_VERSION_MAJOR}" PARENT_SCOPE)
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR "${${ESV_VARIABLE_PREFIX}_VERSION_MINOR}" PARENT_SCOPE)
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH "${${ESV_VARIABLE_PREFIX}_VERSION_PATCH}" PARENT_SCOPE)
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
# always set the soversion
|
||||
set(${ESV_VARIABLE_PREFIX}_SOVERSION "${${ESV_VARIABLE_PREFIX}_SOVERSION}" PARENT_SCOPE)
|
||||
|
||||
if(NOT project_manages_version)
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE)
|
||||
set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
|
||||
set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)
|
||||
set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
if(_set_backward_compat_version_string_vars)
|
||||
set(PROJECT_VERSION_STRING "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
|
||||
set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMSourceVersionControl
|
||||
--------------------------
|
||||
|
||||
Tries to determine whether the source is under version control (git clone,
|
||||
svn checkout, etc).
|
||||
|
||||
``ECM_SOURCE_UNDER_VERSION_CONTROL`` is set when indication is found that
|
||||
``CMAKE_SOURCE_DIR`` is under version control.
|
||||
|
||||
Since 5.63
|
||||
#]=======================================================================]
|
||||
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.git" OR
|
||||
EXISTS "${CMAKE_SOURCE_DIR}/.svn" OR
|
||||
EXISTS "${CMAKE_SOURCE_DIR}/.hg" OR
|
||||
EXISTS "${CMAKE_SOURCE_DIR}/.bzr")
|
||||
set(ECM_SOURCE_UNDER_VERSION_CONTROL TRUE)
|
||||
else()
|
||||
set(ECM_SOURCE_UNDER_VERSION_CONTROL FALSE)
|
||||
endif()
|
||||
@@ -0,0 +1,50 @@
|
||||
# SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMUninstallTarget
|
||||
------------------
|
||||
|
||||
Add an ``uninstall`` target.
|
||||
|
||||
By including this module, an ``uninstall`` target will be added to your CMake
|
||||
project. This will remove all files installed (or updated) by a previous
|
||||
invocation of the ``install`` target. It will not remove files created or
|
||||
modified by an ``install(SCRIPT)`` or ``install(CODE)`` command; you should
|
||||
create a custom uninstallation target for these and use ``add_dependency`` to
|
||||
make the ``uninstall`` target depend on it:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
include(ECMUninstallTarget)
|
||||
install(SCRIPT install-foo.cmake)
|
||||
add_custom_target(uninstall_foo COMMAND ${CMAKE_COMMAND} -P uninstall-foo.cmake)
|
||||
add_dependency(uninstall uninstall_foo)
|
||||
|
||||
The target will fail if the ``install`` target has not yet been run (so it is
|
||||
not possible to run CMake on the project and then immediately run the
|
||||
``uninstall`` target).
|
||||
|
||||
.. warning::
|
||||
|
||||
CMake deliberately does not provide an ``uninstall`` target by default on
|
||||
the basis that such a target has the potential to remove important files
|
||||
from a user's computer. Use with caution.
|
||||
|
||||
Since 1.7.0.
|
||||
#]=======================================================================]
|
||||
|
||||
if (NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ecm_uninstall.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/ecm_uninstall.cmake"
|
||||
IMMEDIATE
|
||||
@ONLY
|
||||
)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/ecm_uninstall.cmake"
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,81 @@
|
||||
# SPDX-FileCopyrightText: 2011 Alexander Neundorf <neundorf@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMUseFindModules
|
||||
-----------------
|
||||
|
||||
Selectively use some of the find modules provided by extra-cmake-modules.
|
||||
|
||||
This module is automatically available once extra-cmake-modules has been
|
||||
found, so it is not necessary to ``include(ECMUseFindModules)`` explicitly.
|
||||
|
||||
::
|
||||
|
||||
ecm_use_find_modules(DIR <dir>
|
||||
MODULES module1.cmake [module2.cmake [...]]
|
||||
[NO_OVERRIDE])
|
||||
|
||||
This allows selective use of the find modules provided by ECM, including
|
||||
deferring to CMake's versions of those modules if it has them. Rather than
|
||||
adding ``${ECM_FIND_MODULE_DIR}`` to ``CMAKE_MODULE_PATH``, you use
|
||||
``ecm_use_find_modules()`` to copy the modules you want to a local (build)
|
||||
directory, and add that to ``CMAKE_MODULE_PATH``.
|
||||
|
||||
The find modules given to ``MODULES`` will be copied to the directory given by ``DIR``
|
||||
(which should be located in ``${CMAKE_BINARY_DIR}`` and added to
|
||||
``CMAKE_MODULE_PATH``). If ``NO_OVERRIDE`` is given, only modules not also
|
||||
provided by CMake will be copied.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(ECM REQUIRED)
|
||||
ecm_use_find_modules(
|
||||
DIR ${CMAKE_BINARY_DIR}/cmake
|
||||
MODULES FindEGL.cmake
|
||||
NO_OVERRIDE
|
||||
)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake)
|
||||
|
||||
This example will make ``FindEGL.cmake`` available in your project, but only
|
||||
as long as it is not yet part of CMake. Calls to ``find_package(EGL)`` will
|
||||
then make use of this copied module (or the CMake module if it exists).
|
||||
|
||||
Another possible use for this macro is to take copies of find modules that can
|
||||
be installed along with config files if they are required as a dependency (for
|
||||
example, if targets provided by the find module are in the link interface of a
|
||||
library).
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
function(ecm_use_find_modules)
|
||||
set(_options NO_OVERRIDE )
|
||||
set(_oneValueArgs DIR )
|
||||
set(_multiValueArgs MODULES )
|
||||
cmake_parse_arguments(EUFM "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN} )
|
||||
if(NOT EUFM_DIR)
|
||||
message(FATAL_ERROR "No DIR specified for ecm_use_find_modules() !")
|
||||
endif()
|
||||
|
||||
if(NOT IS_ABSOLUTE "${EUFM_DIR}")
|
||||
set(EUFM_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EUFM_DIR}")
|
||||
endif()
|
||||
file(MAKE_DIRECTORY "${EUFM_DIR}")
|
||||
|
||||
foreach(file ${EUFM_MODULES})
|
||||
if(NOT EXISTS ${ECM_FIND_MODULE_DIR}/${file} )
|
||||
message(FATAL_ERROR "File ${file} not found in ${ECM_FIND_MODULE_DIR} !")
|
||||
endif()
|
||||
if(NOT EXISTS "${CMAKE_ROOT}/Modules/${file}" OR NOT EUFM_NO_OVERRIDE)
|
||||
configure_file("${ECM_FIND_MODULE_DIR}/${file}" "${EUFM_DIR}/${file}" COPYONLY)
|
||||
endif()
|
||||
endforeach()
|
||||
# This is required by some of the find modules
|
||||
file(WRITE "${EUFM_DIR}/ECMFindModuleHelpersStub.cmake"
|
||||
"include(\"${ECM_MODULE_DIR}/ECMFindModuleHelpers.cmake\")")
|
||||
|
||||
endfunction()
|
||||
@@ -0,0 +1,12 @@
|
||||
// This file was generated by ecm_setup_version(): DO NOT EDIT!
|
||||
|
||||
#ifndef @HEADER_PREFIX@_VERSION_H
|
||||
#define @HEADER_PREFIX@_VERSION_H
|
||||
|
||||
#define @HEADER_PREFIX@_VERSION_STRING "@HEADER_VERSION@"
|
||||
#define @HEADER_PREFIX@_VERSION_MAJOR @HEADER_VERSION_MAJOR@
|
||||
#define @HEADER_PREFIX@_VERSION_MINOR @HEADER_VERSION_MINOR@
|
||||
#define @HEADER_PREFIX@_VERSION_PATCH @HEADER_VERSION_PATCH@
|
||||
#define @HEADER_PREFIX@_VERSION ((@HEADER_VERSION_MAJOR@<<16)|(@HEADER_VERSION_MINOR@<<8)|(@HEADER_VERSION_PATCH@))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
# SPDX-FileCopyrightText: 2016 Gleb Popov <6yearold@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMWinResolveSymlinks
|
||||
--------------------------
|
||||
|
||||
Resolve pseudo-symlinks created by git when cloning on Windows.
|
||||
|
||||
::
|
||||
|
||||
ecm_win_resolve_symlinks(<dir>)
|
||||
|
||||
When git checks out a repository with UNIX symlinks on Windows machine,
|
||||
it creates a text file for each symlink, containing a relative path to the
|
||||
real file.
|
||||
This function would recursively walk over specified directory and replace
|
||||
pseudo-symlinks with corresponding real file's contents. It would then run
|
||||
``git update-index --assume-unchanged`` on them to trick git.
|
||||
|
||||
This is useful for projects like "breeze-icons" that contain many identical
|
||||
icons implemented as symlinks.
|
||||
|
||||
Since 5.28
|
||||
#]=======================================================================]
|
||||
|
||||
function(ECM_WIN_RESOLVE_SYMLINKS _dir)
|
||||
get_filename_component(dir ${_dir} ABSOLUTE)
|
||||
find_program(GIT_EXECUTABLE git)
|
||||
if(NOT GIT_EXECUTABLE)
|
||||
message(SEND_ERROR "Git executable not found!")
|
||||
endif()
|
||||
|
||||
message(STATUS "Resolving symlinks in ${dir}...")
|
||||
_find_symlinks(${dir} symlinks)
|
||||
_portioned_list(symlinks ${symlinks})
|
||||
foreach(s IN LISTS symlinks)
|
||||
string(REPLACE ":" ";" s ${s})
|
||||
_assume_unchanged(NO ${dir} "${s}")
|
||||
_checkout_symlinks(${dir} "${s}")
|
||||
_resolve_symlinks(${dir} "${s}")
|
||||
_assume_unchanged(YES ${dir} "${s}")
|
||||
endforeach()
|
||||
message(STATUS "Resolving symlinks in ${dir}... Done.")
|
||||
|
||||
# touch cache every build to force CMake to re-run these functions every time
|
||||
if(NOT TARGET wrs_touch_cache)
|
||||
add_custom_target(wrs_touch_cache ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/CMakeCache.txt
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_assume_unchanged mode dir symlinks)
|
||||
if(mode)
|
||||
set(flag --assume-unchanged)
|
||||
else()
|
||||
set(flag --no-assume-unchanged)
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} update-index ${flag} ${symlinks}
|
||||
WORKING_DIRECTORY ${dir}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(_find_symlinks dir outvar)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} ls-files -s
|
||||
WORKING_DIRECTORY ${dir}
|
||||
OUTPUT_VARIABLE out
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set(${outvar})
|
||||
if(out)
|
||||
string(REPLACE "\n" ";" out ${out})
|
||||
|
||||
foreach(f IN LISTS out)
|
||||
# 120000 0db97052931e18484b6705f3bc644484ef9403b0 0 <tab> icons-dark/actions/16/CVnamespace.svg
|
||||
string(REPLACE "\t" ";" f "${f}")
|
||||
string(REPLACE " " ";" f "${f}")
|
||||
list(GET f 0 mode)
|
||||
if(mode STREQUAL "120000")
|
||||
list(GET f 3 symlink)
|
||||
list(APPEND ${outvar} ${symlink})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
set(${outvar} ${${outvar}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# In functions like _checkout_symlinks() the command line can become too lengthy for Windows.
|
||||
# So we partition it, but in a hacky way due to CMake doesn't have list of lists.
|
||||
function(_portioned_list outvar)
|
||||
list(LENGTH ARGN arglen)
|
||||
|
||||
if(arglen EQUAL 0)
|
||||
set(${outvar} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(init)
|
||||
set(tail)
|
||||
math(EXPR range "${arglen} - 1")
|
||||
foreach(i RANGE ${range})
|
||||
list(GET ARGN ${i} v)
|
||||
string(LENGTH "${init}" initlen)
|
||||
string(LENGTH ${v} vlen)
|
||||
math(EXPR sumlen "${initlen} + ${vlen}")
|
||||
if(sumlen LESS 8192)
|
||||
list(APPEND init ${v})
|
||||
else()
|
||||
list(APPEND tail ${v})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
_portioned_list(tail_portioned ${tail})
|
||||
string(REPLACE ";" ":" init "${init}") # Generally this is not safe, because filepath can contain ':' character. But not on Windows. Phew.
|
||||
set(${outvar} ${init} ${tail_portioned} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_checkout_symlinks dir symlinks)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${symlinks}
|
||||
WORKING_DIRECTORY ${dir}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(_resolve_symlinks dir symlinks)
|
||||
foreach(s IN LISTS symlinks)
|
||||
file(READ ${dir}/${s} sdata)
|
||||
get_filename_component(sdir ${dir}/${s} DIRECTORY)
|
||||
set(f "${sdir}/${sdata}")
|
||||
file(READ ${f} fdata)
|
||||
file(WRITE ${dir}/${s} ${fdata})
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,43 @@
|
||||
# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
QtVersionOption
|
||||
---------------
|
||||
|
||||
Adds a build option to select the major Qt version if necessary,
|
||||
that is, if the major Qt version has not yet been determined otherwise
|
||||
(e.g. by a corresponding ``find_package()`` call).
|
||||
This module is typically included by other modules requiring knowledge
|
||||
about the major Qt version.
|
||||
|
||||
If the ECM version passed to find_package was at least 5.240.0 Qt6 is picked by default.
|
||||
Otherwise Qt5 is picked.
|
||||
|
||||
``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
|
||||
|
||||
Since 5.82.0.
|
||||
#]=======================================================================]
|
||||
|
||||
if (DEFINED QT_MAJOR_VERSION)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (TARGET Qt5::Core)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
elseif (TARGET Qt6::Core)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
else()
|
||||
if (ECM_GLOBAL_FIND_VERSION VERSION_GREATER_EQUAL 5.240)
|
||||
option(BUILD_WITH_QT6 "Build against Qt 6" ON)
|
||||
else()
|
||||
option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
|
||||
endif()
|
||||
|
||||
if (BUILD_WITH_QT6)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
else()
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
endif()
|
||||
endif()
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2020 Andreas Cord-Landwehr <cordlandwehr@kde.org>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# key : outbound license identifier
|
||||
# values : list of acceptable licenses that are compatible with outbound license
|
||||
compatibilityMatrix = {
|
||||
"MIT": [
|
||||
"CC0-1.0",
|
||||
"MIT"],
|
||||
"BSD-2-Clause": [
|
||||
"CC0-1.0",
|
||||
"MIT",
|
||||
"BSD-2-Clause"],
|
||||
"BSD-3-Clause": [
|
||||
"CC0-1.0",
|
||||
"MIT",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause"],
|
||||
"LGPL-2.0-only": [
|
||||
"CC0-1.0",
|
||||
"LGPL-2.0-only",
|
||||
"LGPL-2.0-or-later",
|
||||
"MIT",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"bzip2-1.0.6"],
|
||||
"LGPL-2.1-only": [
|
||||
"CC0-1.0",
|
||||
"LGPL-2.0-or-later",
|
||||
"LGPL-2.1-only",
|
||||
"LGPL-2.1-or-later",
|
||||
"MIT",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"bzip2-1.0.6"],
|
||||
"LGPL-3.0-only": [
|
||||
"CC0-1.0",
|
||||
"LGPL-2.0-or-later",
|
||||
"LGPL-2.1-or-later",
|
||||
"LGPL-3.0-only",
|
||||
"LGPL-3.0-or-later",
|
||||
"LicenseRef-KDE-Accepted-LGPL",
|
||||
"MIT",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"bzip2-1.0.6"],
|
||||
"GPL-2.0-only": [
|
||||
"CC0-1.0",
|
||||
"LGPL-2.0-only",
|
||||
"LGPL-2.1-only",
|
||||
"LGPL-2.0-or-later",
|
||||
"LGPL-2.1-or-later",
|
||||
"GPL-2.0-only",
|
||||
"GPL-2.0-or-later",
|
||||
"MIT",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"bzip2-1.0.6"],
|
||||
"GPL-3.0-only": [
|
||||
"CC0-1.0",
|
||||
"LGPL-2.0-or-later",
|
||||
"LGPL-2.1-or-later",
|
||||
"LGPL-3.0-only",
|
||||
"LGPL-3.0-or-later",
|
||||
"GPL-2.0-or-later",
|
||||
"GPL-3.0-only",
|
||||
"GPL-3.0-or-later",
|
||||
"LicenseRef-KDE-Accepted-GPL",
|
||||
"LicenseRef-KDE-Accepted-LGPL",
|
||||
"MIT",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"bzip2-1.0.6"]
|
||||
}
|
||||
|
||||
def check_outbound_license(license, files, spdxDictionary):
|
||||
"""
|
||||
Asserts that the list of source files @p files, when combined into
|
||||
a library or executable, can be delivered under the combined license @p license .
|
||||
"""
|
||||
print("Checking Target License: " + license)
|
||||
if not license in compatibilityMatrix:
|
||||
print("Error: unknown license selected")
|
||||
return False
|
||||
|
||||
allLicensesAreCompatible = True
|
||||
|
||||
for sourceFile in files:
|
||||
compatible = False
|
||||
sourceFileStripped = sourceFile.strip()
|
||||
for fileLicense in spdxDictionary[sourceFileStripped]:
|
||||
if fileLicense in compatibilityMatrix[license]:
|
||||
compatible = True
|
||||
print("OK " + sourceFileStripped + " : " + fileLicense)
|
||||
if not compatible:
|
||||
allLicensesAreCompatible = False
|
||||
print("-- " + sourceFileStripped + " : ( " + ", ".join([str(i) for i in spdxDictionary[sourceFileStripped]]) + " )")
|
||||
return allLicensesAreCompatible
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Parsing SPDX BOM file")
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
# parse commands
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-l", "--license", help="set outbound license to test")
|
||||
parser.add_argument("-s", "--spdx", help="spdx bill-of-materials file")
|
||||
parser.add_argument("-i", "--input", help="input file with list of source files to test")
|
||||
args = parser.parse_args()
|
||||
|
||||
# TODO check if required arguments are present and give meaningful feedback
|
||||
|
||||
# collect name and licenses from SPDX blocks
|
||||
spdxDictionary = {}
|
||||
fileName = ""
|
||||
licenses = []
|
||||
f = open(args.spdx, "r")
|
||||
for line in f:
|
||||
if line.startswith("FileName:"):
|
||||
# strip "FileName: "
|
||||
# thus name expected to start with "./", which is relative to CMAKE_SOURCE_DIR
|
||||
fileName = line[10:].strip()
|
||||
if line.startswith("LicenseInfoInFile:"):
|
||||
licenses.append(line[19:].strip())
|
||||
if line == '' or line == "\n":
|
||||
spdxDictionary[fileName] = licenses
|
||||
fileName = ""
|
||||
licenses = []
|
||||
f.close()
|
||||
|
||||
spdxDictionary[fileName] = licenses
|
||||
|
||||
# read file with list of test files
|
||||
f = open(args.input, "r")
|
||||
testfiles = f.readlines()
|
||||
f.close()
|
||||
|
||||
if check_outbound_license(args.license, testfiles, spdxDictionary) is True:
|
||||
sys.exit(0)
|
||||
|
||||
# in any other case, return error code
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,20 @@
|
||||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
execute_process(
|
||||
COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}"
|
||||
RESULT_VARIABLE rm_retval
|
||||
)
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
Reference in New Issue
Block a user