1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
|
# 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)
get_property(dep_gir_lib TARGET "gir-${dep}" PROPERTY GI_GIR_LIBRARY)
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 ()
if (dep_gir_lib)
list(APPEND scanner_flags "--library=${dep_gir_lib}")
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}" "CFLAGS=${CMAKE_C_FLAGS}"
"${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_GIR_LIBRARY "$<TARGET_FILE_BASE_NAME:${opt_TARGET}>")
set_property(TARGET "gir-${namespace}" PROPERTY GI_PACKAGE "${opt_PACKAGE}-${nsversion}")
endfunction()
|