| # Copyright (C) 2022 Igalia S.L. |
| # |
| # 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 above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``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 COPYRIGHT HOLDER OR ITS |
| # CONTRIBUTORS 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. |
| # |
| #[=======================================================================[.rst: |
| FindGI |
| ------ |
| |
| Finds the GObject-Introspection tools and adds the :command:`GI_INTROSPECT` |
| command. The following variables will also be set: |
| |
| ``GI_FOUND`` |
| True if the GObject-Introspection tools are available. |
| ``GI_VERSION`` |
| Version of the GObject-Introspection tools. |
| ``GI_SCANNER_EXE`` |
| Path to the ``g-ir-scanner`` program. |
| ``GI_COMPILER_EXE`` |
| Path to the ``g-ir-compiler`` program. |
| ``GI_GIRDIR`` |
| Path where to install ``.gir`` files in the target system. |
| ``GI_TYPELIBDIR`` |
| Path where to install ``.typelib`` files in the target system. |
| ``GI_HAVE_SOURCES_TOP_DIRS`` |
| Whether the introspection scannner supports the ``--sources-top-dirs=`` |
| command line flag. |
| |
| #]=======================================================================] |
| |
| # Add a dummy command in case introspection is disabled. This allows |
| # always use it and automatically have it be a noop in that case, instead |
| # of having a check next to each invocation. |
| if (NOT ENABLE_INTROSPECTION) |
| function(GI_INTROSPECT) |
| endfunction() |
| return() |
| endif () |
| |
| find_package(PkgConfig QUIET) |
| |
| if (PKG_CONFIG_FOUND) |
| if (PACKAGE_FIND_VERSION_COUNT GREATER 0) |
| set(_gi_version_cmp ">=${PACKAGE_FIND_VERSION}") |
| endif () |
| pkg_check_modules(PC_GI gobject-introspection-1.0${_gi_version_cmp}) |
| if (PC_GI_FOUND) |
| pkg_get_variable(_GI_SCANNER_EXE gobject-introspection-1.0 g_ir_scanner) |
| pkg_get_variable(_GI_COMPILER_EXE gobject-introspection-1.0 g_ir_compiler) |
| pkg_get_variable(_GI_GIRDIR gobject-introspection-1.0 girdir) |
| pkg_get_variable(_GI_TYPELIBDIR gobject-introspection-1.0 typelibdir) |
| pkg_get_variable(_GI_PREFIX gobject-introspection-1.0 prefix) |
| set(GI_VERSION ${PC_GI_VERSION}) |
| endif () |
| endif () |
| |
| find_program(GI_SCANNER_EXE NAMES ${_GI_SCANNER_EXE} g-ir-scanner) |
| find_program(GI_COMPILER_EXE NAMES ${_GI_COMPILER_EXE} g-ir-compiler) |
| |
| include(GNUInstallDirs) |
| if (_GI_GIRDIR AND _GI_PREFIX) |
| string(FIND "${_GI_GIRDIR}" "${_GI_PREFIX}" _idx) |
| if (_idx EQUAL 0) |
| string(LENGTH "${_GI_PREFIX}" _idx) |
| string(SUBSTRING "${_GI_GIRDIR}" ${_idx} -1 _GI_GIRDIR) |
| set(_GI_GIRDIR "${CMAKE_INSTALL_PREFIX}/${_GI_GIRDIR}") |
| else () |
| unset(_GI_GIRDIR) |
| endif () |
| endif () |
| |
| if (NOT _GI_GIRDIR) |
| set(_GI_GIRDIR "${CMAKE_INSTALL_DATADIR}/gir-1.0") |
| endif () |
| |
| if (_GI_TYPELIBDIR AND _GI_PREFIX) |
| string(FIND "${_GI_TYPELIBDIR}" "${_GI_PREFIX}" _idx) |
| if (_idx EQUAL 0) |
| string(LENGTH "${_GI_PREFIX}" _idx) |
| string(SUBSTRING "${_GI_TYPELIBDIR}" ${_idx} -1 _GI_TYPELIBDIR) |
| set(_GI_TYPELIBDIR "${CMAKE_INSTALL_PREFIX}/${_GI_TYPELIBDIR}") |
| else () |
| unset(_GI_TYPELIBDIR) |
| endif () |
| endif () |
| |
| if (NOT _GI_TYPELIBDIR) |
| set(_GI_TYPELIBDIR "${CMAKE_INSTALL_LIBDIR}/girepository-1.0") |
| endif () |
| |
| set(GI_GIRDIR "${_GI_GIRDIR}" CACHE PATH "Path to installed .gir files") |
| set(GI_TYPELIBDIR "${_GI_TYPELIBDIR}" CACHE PATH "Path to installed .typelib files") |
| |
| if (NOT GI_VERSION AND GI_SCANNER_EXE) |
| execute_process( |
| COMMAND "${GI_SCANNER_EXE}" --version |
| OUTPUT_VARIABLE GI_VERSION |
| OUTPUT_STRIP_TRAILING_WHITESPACE |
| COMMAND_ERROR_IS_FATAL ANY |
| ERROR_QUIET |
| ) |
| if (GI_VERSION MATCHES "^g-ir-scanner[[:space:]]+([0-9.]+)") |
| set(GI_VERSION ${CMAKE_MATCH_1}) |
| else () |
| unset(GI_VERSION) |
| endif () |
| endif () |
| |
| if (GI_VERSION VERSION_GREATER_EQUAL 1.59.1) |
| set(GI_HAVE_SOURCES_TOP_DIRS TRUE) |
| else () |
| set(GI_HAVE_SOURCES_TOP_DIRS FALSE) |
| endif () |
| |
| include(FindPackageHandleStandardArgs) |
| find_package_handle_standard_args(GI |
| REQUIRED_VARS GI_SCANNER_EXE GI_COMPILER_EXE GI_GIRDIR GI_TYPELIBDIR |
| VERSION_VAR GI_VERSION |
| ) |
| |
| if (NOT GI_FOUND) |
| return() |
| endif () |
| |
| define_property(TARGET |
| PROPERTY GI_GIR_PATH |
| BRIEF_DOCS "Path to .gir file" |
| FULL_DOCS "Path to .gir file generated by the target" |
| ) |
| define_property(TARGET |
| PROPERTY GI_PACKAGE |
| BRIEF_DOCS "Exported package" |
| FULL_DOCS "Name of the pkg-config package for the target" |
| ) |
| |
| |
| #[=======================================================================[.rst: |
| |
| .. command:: GI_INTROSPECT |
| |
| .. code-block:: cmake |
| |
| GI_INTROSPECT(<namespace> <nsversion> <header> |
| [TARGET <target>] |
| [SYMBOL_PREFIX <string>] |
| [IDENTIFIER_PREFIX <string>] |
| [PACKAGE <pkgname>] |
| [DEPENDENCIES <dependency>...] |
| [SOURCES <file>... |
| [NO_IMPLICIT_SOURCES]) |
| |
| Enables generating introspection data for a library ``<target>``, which |
| will make the introspected API available in the ``<namespace>-<nsversion>`` |
| module. Both ``.gir`` and ``.typelib`` will be built and configured for |
| installation. |
| |
| The ``<header>`` argument indicates how to include the top-level *public* |
| API header for the library, for example ``gtk/gtk.h``. |
| |
| ``TARGET`` specifies the name of the CMake used to build the library to scan |
| for introspection data. If not specified, the default value is the same as |
| the ``<namespace>``. |
| |
| ``SYMBOL_PREFIX`` specifies the prefix of symbols (functions) to scan for. |
| If not specified, the default value is the ``<namespace>`` converted to |
| lowercase. |
| |
| ``IDENTIFIER_PREFIX`` specifies the prefix of identifiers (types) to scan |
| for. If not specified, the default value is the ``<namespace>`` converted |
| to uppercase. |
| |
| ``PACKAGE`` indicates the ``pkg-config`` package exported by the generated |
| ``.gir``. If not specified, the default value is the ``<namespace>`` |
| converted to lowercase. |
| |
| ``DEPENDENCIES`` specifies an optional list of dependencies of the library |
| being scanned for introspection data. Each dependecy can be one of: |
| |
| * A GObject-Introspection module name, e.g. ``GObject-2.0``. |
| * A GObject-Introspection module name, a colon, and the name of its |
| ``pkg-config`` package, e.g. ``Gtk-4.0:gtk4``. This is useful for |
| those modules where both names don't match. |
| * Another ``<namespace>`` from a previous usage of the command. This |
| will add its ``.gir`` as an uninstalled dependency and ensure that |
| dependencies are built beforehand. |
| |
| By default the sources scanned for introspection documentation comments |
| are those used to build the ``<target>`` library, plus those specified |
| with ``SOURCES``. Adding the ``NO_IMPLICIT_SOURCES`` flag uses only the |
| latter. |
| |
| The command creates a target named ``gir-<namespace>`` to build the |
| ``.gir`` file, with two properties: ``GI_GIR_PATH`` contains the path |
| to the generated (uninstalled) file, and ``GI_PACKAGE`` containing the |
| string ``<pkgname>-<nsversion>``. |
| |
| A target named ``typelib-<namespace>`` is created as well to build the |
| ``.typelib`` file. |
| |
| Targets ``gir-all`` and ``typelib-all`` can be used to build all the |
| ``.gir`` and ``.typelib`` files for a project. |
| |
| #]=======================================================================] |
| |
| function(GI_INTROSPECT namespace nsversion header) |
| cmake_parse_arguments(PARSE_ARGV 2 opt |
| "NO_IMPLICIT_SOURCES" |
| "IDENTIFIER_PREFIX;PACKAGE;SYMBOL_PREFIX;TARGET" |
| "DEPENDENCIES;SOURCES;OPTIONS" |
| ) |
| if (NOT opt_PACKAGE) |
| string(TOLOWER "${namespace}" opt_PACKAGE) |
| endif () |
| if (NOT opt_SYMBOL_PREFIX) |
| string(TOLOWER "${namespace}" opt_SYMBOL_PREFIX) |
| endif () |
| if (NOT opt_IDENTIFIER_PREFIX) |
| string(TOUPPER "${opt_SYMBOL_PREFIX}" opt_IDENTIFIER_PREFIX) |
| endif () |
| if (NOT opt_TARGET) |
| set(opt_TARGET "${namespace}") |
| endif () |
| |
| if (NOT TARGET ${opt_TARGET}) |
| message(FATAL_ERROR "Target '${opt_TARGET}' was not defined") |
| endif () |
| |
| set(gir_deps) |
| set(gir_name "${namespace}-${nsversion}") |
| set(gir_path "${CMAKE_BINARY_DIR}/${gir_name}.gir") |
| set(typ_path "${CMAKE_BINARY_DIR}/${gir_name}.typelib") |
| |
| set(scanner_flags) |
| if (GI_HAVE_SOURCES_TOP_DIRS) |
| list(APPEND scanner_flags "--sources-top-dirs=${CMAKE_SOURCE_DIR}") |
| endif () |
| |
| # Each dependency can be: |
| # * GI include, i.e. "GObject-2.0", implies --include=GObject-2.0, --pkg=gobject-2.0 |
| # * GI include ":" pkgconfig module, i.e. "Gtk-4.0:gtk4", implies --include=Gtk-4.0, --pkg=gtk4 |
| # * CMake target, i.e. "JavaScriptCore", implies --include-uninstalled=<girfile>. The target |
| # must have been previously used with GI_INTROSPECT(), and for each use on the target the |
| # corresponding <girfile> will be picked automatically. |
| foreach (dep IN LISTS opt_DEPENDENCIES) |
| if (TARGET "gir-${dep}") |
| get_property(dep_gir_path TARGET "gir-${dep}" PROPERTY GI_GIR_PATH) |
| if (dep_gir_path) |
| list(APPEND scanner_flags "--include-uninstalled=${dep_gir_path}") |
| list(APPEND gir_deps "${dep_gir_path}") |
| else () |
| message(AUTHOR_WARNING |
| "Target '${dep}' listed as a dependency but it has not " |
| "been previously configured with GI_INTROSPECT()" |
| ) |
| endif () |
| elseif (dep MATCHES "^([a-zA-Z0-9._-]+):([a-z0-9._\\+-]+)$") |
| list(APPEND scanner_flags |
| "--include=${CMAKE_MATCH_1}" |
| "--pkg=${CMAKE_MATCH_2}" |
| ) |
| else () |
| string(TOLOWER "${dep}" dep_pkg) |
| list(APPEND scanner_flags |
| "--include=${dep}" |
| "--pkg=${dep_pkg}" |
| ) |
| endif () |
| endforeach () |
| |
| get_property(target_srcdir TARGET ${opt_TARGET} PROPERTY SOURCE_DIR) |
| foreach (incdir IN LISTS ${opt_TARGET}_INTERFACE_INCLUDE_DIRECTORIES) |
| if (NOT IS_ABSOLUTE "${incdir}") |
| get_filename_component(incdir "${incdir}" REALPATH BASE_DIR "${target_srcdir}") |
| endif () |
| list(APPEND scanner_flags "-I${incdir}") |
| endforeach () |
| |
| set(gir_srcs "") |
| foreach (src IN LISTS opt_SOURCES) |
| if (NOT IS_ABSOLUTE "${src}") |
| get_filename_component(src "${src}" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") |
| endif () |
| if (IS_DIRECTORY "${src}") |
| if (EXISTS "${src}") |
| file(GLOB src_files LIST_DIRECTORIES FALSE CONFIGURE_DEPENDS "${src}/*.c" "${src}/*.cpp") |
| if (src_files) |
| list(APPEND gir_srcs ${src_files}) |
| else () |
| message(AUTHOR_WARNING "Directory '${src}' specified as source, but contains no source files") |
| endif () |
| else () |
| message(AUTHOR_WARNING "Directory '${src}' specified as source, but it does not exist") |
| endif () |
| else () |
| list(APPEND gir_srcs "${src}") |
| endif () |
| endforeach () |
| |
| if (NOT opt_NO_IMPLICIT_SOURCES) |
| foreach (src IN LISTS ${opt_TARGET}_INSTALLED_HEADERS ${opt_TARGET}_SOURCES) |
| if (NOT IS_ABSOLUTE "${src}") |
| get_filename_component(src "${src}" REALPATH BASE_DIR "${target_srcdir}") |
| endif () |
| list(APPEND gir_srcs "${src}") |
| endforeach () |
| endif () |
| |
| if (NOT gir_srcs) |
| message(FATAL_ERROR "No sources to scan specified") |
| endif () |
| |
| # Generate .gir |
| set(target_def "$<TARGET_PROPERTY:${opt_TARGET},COMPILE_DEFINITIONS>") |
| set(target_inc "$<TARGET_PROPERTY:${opt_TARGET},INTERFACE_INCLUDE_DIRECTORIES>") |
| add_custom_command( |
| OUTPUT "${gir_path}" |
| COMMENT "Generating ${gir_name}.gir" |
| WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" |
| DEPENDS ${gir_deps} ${gir_srcs} |
| VERBATIM |
| COMMAND_EXPAND_LISTS |
| COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_C_COMPILER}" |
| "${GI_SCANNER_EXE}" --quiet --warn-all --warn-error --no-libtool |
| "--output=${gir_path}" |
| "--library=$<TARGET_FILE_BASE_NAME:${opt_TARGET}>" |
| "--library-path=$<TARGET_FILE_DIR:${opt_TARGET}>" |
| "--namespace=${namespace}" |
| "--nsversion=${nsversion}" |
| "--c-include=${header}" |
| "--identifier-prefix=${opt_IDENTIFIER_PREFIX}" |
| "--symbol-prefix=${opt_SYMBOL_PREFIX}" |
| "--pkg-export=${opt_PACKAGE}-${nsversion}" |
| "$<$<BOOL:${target_def}>:-D$<JOIN:${target_def},;-D>>" |
| "$<$<BOOL:${target_inc}>:-I$<JOIN:${target_inc},;-I>>" |
| ${scanner_flags} |
| ${opt_OPTIONS} |
| ${gir_srcs} |
| ) |
| |
| add_custom_target("gir-${namespace}" DEPENDS "${gir_path}") |
| |
| if (NOT TARGET gir-all) |
| add_custom_target(gir-all COMMENT "All GI .gir targets") |
| endif () |
| add_dependencies(gir-all "gir-${namespace}") |
| |
| install( |
| FILES "${gir_path}" |
| DESTINATION "${GI_GIRDIR}" |
| COMPONENT runtime |
| ) |
| |
| # Generate .typelib |
| add_custom_command( |
| OUTPUT "${typ_path}" |
| COMMENT "Generating ${gir_name}.typelib" |
| DEPENDS "${gir_path}" |
| VERBATIM |
| COMMAND "${GI_COMPILER_EXE}" |
| "--includedir=${CMAKE_BINARY_DIR}" |
| "--output=${typ_path}" |
| "${gir_path}" |
| ) |
| |
| add_custom_target("typelib-${namespace}" DEPENDS "${typ_path}") |
| |
| if (NOT TARGET typelib-all) |
| add_custom_target(typelib-all ALL COMMENT "All GI .typelib targets") |
| endif () |
| add_dependencies(typelib-all "typelib-${namespace}") |
| |
| install( |
| FILES "${typ_path}" |
| DESTINATION "${GI_TYPELIBDIR}" |
| COMPONENT runtime |
| ) |
| |
| # Record in targets to use later on e.g. with gi-docgen. |
| set_property(TARGET "gir-${namespace}" PROPERTY GI_GIR_PATH "${gir_path}") |
| set_property(TARGET "gir-${namespace}" PROPERTY GI_PACKAGE "${opt_PACKAGE}-${nsversion}") |
| endfunction() |