diff --git a/docs/module/ECMSetupQtPluginMacroNames.rst b/docs/module/ECMSetupQtPluginMacroNames.rst new file mode 100644 --- /dev/null +++ b/docs/module/ECMSetupQtPluginMacroNames.rst @@ -0,0 +1 @@ +.. ecm-module:: ../../modules/ECMSetupQtPluginMacroNames.cmake diff --git a/modules/ECMSetupQtPluginMacroNames.cmake b/modules/ECMSetupQtPluginMacroNames.cmake new file mode 100644 --- /dev/null +++ b/modules/ECMSetupQtPluginMacroNames.cmake @@ -0,0 +1,232 @@ +#.rst: +# ECMSetupQtPluginMacroNames +# --------------- +# +# Instruct CMake's automoc about C++ preprocessor macros used to define Qt-style plugins. +# +# :: +# +# ecm_setup_qtplugin_macro_names( +# [JSON_NONE [ [...]]] +# [JSON_ARG1 [ [...]]] +# [JSON_ARG2 [ [...]]] +# [JSON_ARG3 [ [...]]] +# [CONFIG_CODE_VARIABLE ] ) +# +# 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 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 the generated code for instructing automoc for the given macro names, +# as useful in a CMake Config file. The variable can then be used as usual in +# the template file for the CMake Config file, by ``@@``. +# +# +# 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: +# .. code-block:: cmake +# +# @PACKAGE_SETUP_AUTOMOC_VARIABLES@ +# +# Since 5.45.0. + +#============================================================================= +# Copyright 2018 Friedrich W. H. Kossebau +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +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 + if(NOT CMAKE_VERSION VERSION_LESS "3.10.0") + # 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. + list(APPEND CMAKE_AUTOMOC_MACRO_NAMES + ${ESQMN_JSON_NONE} + ${ESQMN_JSON_ARG1} + ${ESQMN_JSON_ARG2} + ${ESQMN_JSON_ARG3} + ) + endif() + + # CMAKE_AUTOMOC_DEPEND_FILTERS + if(NOT CMAKE_VERSION VERSION_LESS "3.9.0") + # 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 + # 3.9+ lets us provide some filter rule pairs (keyword, regexp) to match the names of such files + # in the plain text of the sources. See AUTOMOC_DEPEND_FILTERS docs for details. + 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() + endif() + + if (ESQMN_CONFIG_CODE_VARIABLE) + set(_content +"#################################################################################### +# CMAKE_AUTOMOC +") + set(_all_macro_names + ${ESQMN_JSON_NONE} + ${ESQMN_JSON_ARG1} + ${ESQMN_JSON_ARG2} + ${ESQMN_JSON_ARG3} + ) + string(APPEND _content " +if(NOT CMAKE_VERSION VERSION_LESS \"3.10.0\") + # 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. + list(APPEND CMAKE_AUTOMOC_MACRO_NAMES ${_all_macro_names}) +endif() +") + + if(ESQMN_JSON_ARG1 OR ESQMN_JSON_ARG2 OR ESQMN_JSON_ARG3) + string(APPEND _content " +if(NOT CMAKE_VERSION VERSION_LESS \"3.9.0\") + # 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 + # 3.9+ lets us provide some filter rule pairs (keyword, regexp) to match the names of such files + # in the plain text of the sources. See AUTOMOC_DEPEND_FILTERS docs for details. +") + if(ESQMN_JSON_ARG1) + string(APPEND _content +" foreach(macro_name ${ESQMN_JSON_ARG1}) + list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS + \"\${macro_name}\" + \"[\\n^][ \\t]*\${macro_name}[ \\t\\n]*\\\\([ \\t\\n]*\\\"([^\\\"]+)\\\"\" + ) + endforeach() +") + endif() + if(ESQMN_JSON_ARG2) + string(APPEND _content +" foreach(macro_name ${ESQMN_JSON_ARG2}) + list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS + \"\${macro_name}\" + \"[\\n^][ \\t]*\${macro_name}[ \\t\\n]*\\\\([^,]*,[ \\t\\n]*\\\"([^\\\"]+)\\\"\" + ) + endforeach() +") + endif() + if(ESQMN_JSON_ARG3) + string(APPEND _content +" foreach(macro_name ${ESQMN_JSON_ARG3}) + list(APPEND CMAKE_AUTOMOC_DEPEND_FILTERS + \"\${macro_name}\" + \"[\\n^][ \\t]*\${macro_name}[ \\t\\n]*\\\\([^,]*,[^,]*,[ \\t\\n]*\\\"([^\\\"]+)\\\"\" + ) + endforeach() +") + endif() + string(APPEND _content "endif()") + endif() + string(APPEND _content " +####################################################################################" + ) + + set(${ESQMN_CONFIG_CODE_VARIABLE} ${_content}) + endif() +endmacro()