1 Introduction

Précédemment, nous avions repris notre petit projet Luciole pour l'adapter à l'introspection GObject. Nous nous étions arrêté sur une note amère : il faut pouvoir exécuter un programme juste compilé pour créer ce fichier.

introspection.png

Figure 1 : Graphe de dépendances actuel ; les ellipses représentent les fichiers objets, les rectangles représentent les fichiers sources (distribués comme tels) ; la couleur jaune indique les fichiers écrits à la main (à versionner).

La figure 1 récapitule la situation. Nous avons quatre fichiers source, et Luciole-0.1.gir est considéré comme un fichier compilé.

2 Le problème

Cette situation pourrait être évitée : en effet, le résultat de l'introspection ne dépend en fait que du code source. En effet, si le code source ne change pas, la bibliothèque libluciole.la sera également la même 1.

Nous aimerions donc avoir la situation où Luciole-0.1.gir ne dépend que des sources.

introspection-voulue.png

Figure 2 : Graphe de dépendances souhaité ; les ellipses représentent toujours les fichiers objets, les rectangles représentent toujours les fichiers sources (distribués comme tels) ; la couleur jaune indique toujours les fichiers écrits à la main (à versionner).

La figure 2 montre ce que l'on souhaiterait. Le fichier Luciole-0.1.gir ferait ainsi partie de la distribution.

Que se passe-t-il lorsqu'on rajoute la cible factice dist, qui permet d'empaqueter tous les fichiers à distribuer ?

introspection-avec-dist.png

Figure 3 : Graphe de dépendances avec la cible factice dist ; les ellipses représentent toujours les fichiers objets, les rectangles représentent toujours les fichiers sources (distribués comme tels), et le losange représente une cible factice ; la couleur jaune indique toujours les fichiers écrits à la main (à versionner).

La figure 3 nous montre le graphe total. Que se passe-t-il si l'on demande la cible dist en ne disposant que des fichiers sources (en jaune) ? La cible échoue, puisque la bibliothèque libluciole.la n'a pas été compilée. Ajouter une dépendance entre dist et libluciole.la ne résout pas le problème, car il faudrait insérer cette dépendance avant Luciole-0.1.gir, et désactiver le multi-threading. Ce n'est pas une bonne solution.

3 Now introducing… le Make Récursif !

introspection-avec-dist-et-fausse-dep.png

Figure 4 : Graphe de dépendances avec la cible factice dist et une fausse dépendance ; les ellipses représentent toujours les fichiers objets, les rectangles représentent toujours les fichiers sources (distribués comme tels), et le losange représente une cible factice ; la couleur jaune indique toujours les fichiers écrits à la main (à versionner) ; on introduit une fausse dépendance en pointillés entre Luciole-0.1.gir et libluciole.la.

C'est simple : il suffit de chercher à obtenir le résultat figure 4. La flèche en pointillés indique qu'il faut recompiler libluciole.la avant de recompiler Luciole-0.1.gir, mais il ne s'agit pas d'une dépendance, c'est-à-dire qu'il ne faut pas recompiler Luciole-0.1.gir si libluciole.la change.

Concrètement, il faut modifier la règle de fabrication de Luciole-0.1.gir en appelant le programme "make" pour construire libluciole.la avant toute chose. Ainsi, l'introspection pourra toujours s'appuyer sur une version à jour de libluciole.la. On comprend maintenant pourquoi cela s'appelle recursive make, puisqu'on appelle le programme make dans un Makefile.

Le recursive make est à consommer avec modération, pour plusieurs raisons.

La première est idéologique : le but de make est d'avoir accès à l'ensemble du graphe de dépendance rien qu'en analysant le Makefile. De toute évidence, en faisant un appel à make en cours de route, une bonne partie du graphe lui échappe, puisqu'on va se retrouver à compiler presque tout le paquet pour pouvoir effectuer l'opération dist sans que ce soit indiqué sous forme de dépendances.

La seconde est plus concrète : il est possible de faire une boucle infinie avec un appel récursif de make mal placé.

Enfin, il est impossible de maîtriser le nombre de travaux concurrents que fait make, ce qui peut amener à perdre beaucoup de temps.

4 Application à notre projet

Nous allons principalement effectuer deux grosses modifications dans notre projet :

  1. considérer que le .gir est un fichier source, et donc le chercher dans le dossier $(srcdir) (pour pouvoir utiliser la fonctionnalité VPATH) et l'ajouter à la variable EXTRA_DIST, et
  2. hacker la règle de fabrication du .gir et du .typelib !

Commençons par le plus intéressant.

4.1 Modifier la règle de fabrication du fichier .gir

Après un peu de méta-introspection (c'est-à-dire, d'introspection de l'introspection), on se rend compte que cette règle est définie dans le fichier système Makefile.introspection. Sur mon debian, je l'ai trouvé dans /usr/share/gobject-introspection-1.0/Makefile.introspection.

# -*- Mode: make -*-
# Copyright 2009-2010 Johan Dahlin
#
# This file is free software; the author(s) gives unlimited
# permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# * Input variables:
#
#   INTROSPECTION_GIRS - List of GIRS that should be generated
#   INTROSPECTION_SCANNER - Command to invoke scanner, normally set by
#      GOBJECT_INTROSPECTION_REQUIRE/CHECK() in introspection.m4
#   INTROSPECTION_SCANNER_ARGS - Additional args to pass in to the scanner
#   INTROSPECTION_SCANNER_ENV - Environment variables to set before running
#      the scanner
#   INTROSPECTION_COMPILER - Command to invoke compiler, normally set by
#      GOBJECT_INTROSPECTION_REQUIRE/CHECK() in introspection.m4
#   INTROSPECTION_COMPILER_ARGS - Additional args to pass in to the compiler
#
# * Simple tutorial
#
# Add this to configure.ac:
#   -Wno-portability to AM_INIT_AUTOMAKE
#   GOBJECT_INTROSPECTION_CHECK([0.6.7])
#
# Add this to Makefile.am where your library/program is built:
#   include $(INTROSPECTION_MAKEFILE)
#   INTROSPECTION_GIRS = YourLib-1.0.gir
#   YourLib-1.0.gir: libyourlib.la
#   YourLib_1_0_gir_NAMESPACE = YourLib
#   YourLib_1_0_gir_VERSION = 1.0
#   YourLib_1_0_gir_LIBS = libyourlib.la
#   YourLib_1_0_gir_FILES = $(libyourlib_1_0_SOURCES)
#   girdir = $(datadir)/gir-1.0
#   dist_gir_DATA = YourLib-1.0.gir
#   typelibdir = $(libdir)/girepository-1.0
#   typelib_DATA = YourLib-1.0.typelib
#   CLEANFILES = $(dist_gir_DATA) $(typelib_DATA)
#

# Make sure the required variables are set, these should under normal
# circumstances come from introspection.m4
$(if $(INTROSPECTION_SCANNER),,$(error Need to define INTROSPECTION_SCANNER))
$(if $(INTROSPECTION_COMPILER),,$(error Need to define INTROSPECTION_COMPILER))

# Private functions

## Transform the gir filename to something which can reference through a variable
## without automake/make complaining, eg Gtk-2.0.gir -> Gtk_2_0_gir
_gir_name = $(subst /,_,$(subst -,_,$(subst .,_,$(1))))

# Namespace and Version is either fetched from the gir filename
# or the _NAMESPACE/_VERSION variable combo
_gir_namespace = $(or $($(_gir_name)_NAMESPACE),$(firstword $(subst -, ,$(notdir $(1)))))
_gir_version = $(or $($(_gir_name)_VERSION),$(lastword $(subst -, ,$(1:.gir=))))

# _PROGRAM is an optional variable which needs it's own --program argument
_gir_program = $(if $($(_gir_name)_PROGRAM),--program=$($(_gir_name)_PROGRAM))

# Variables which provides a list of things
_gir_libraries = $(foreach lib,$($(_gir_name)_LIBS),--library=$(lib))
_gir_packages = $(foreach pkg,$($(_gir_name)_PACKAGES),--pkg=$(pkg))
_gir_includes = $(foreach include,$($(_gir_name)_INCLUDES),--include=$(include))
_gir_export_packages = $(foreach pkg,$($(_gir_name)_EXPORT_PACKAGES),--pkg-export=$(pkg))

# Reuse the LIBTOOL variable from automake if it's set, but
# work around MSYS weirdness: When running g-ir-scanner, MSYS changes
# a command-line argument --libtool="/bin/sh ../../libtool" into
# --libtool=c:/opt/msys/1.0/bin/libtool. So just use sh.exe without path
# because we already "know" where the libtool configure produced is.
_gir_libtool = $(if $(findstring MINGW,$(shell uname -s)),--libtool="$(top_builddir)/libtool",$(if $(LIBTOOL),--libtool="$(LIBTOOL)"))

# Macros for AM_SILENT_RULES prettiness
_gir_verbosity = $(if $(AM_DEFAULT_VERBOSITY),$(AM_DEFAULT_VERBOSITY),1)

_gir_silent_scanner_prefix = $(_gir_silent_scanner_prefix_$(V))
_gir_silent_scanner_prefix_ = $(_gir_silent_scanner_prefix_$(_gir_verbosity))
_gir_silent_scanner_prefix_0 = @echo "  GISCAN   $(1)";
_gir_silent_scanner_opts = $(_gir_silent_scanner_opts_$(V))
_gir_silent_scanner_opts_ = $(_gir_silent_scanner_opts_$(_gir_verbosity))
_gir_silent_scanner_opts_0 = --quiet

_gir_silent_compiler = $(_gir_silent_compiler_$(V))
_gir_silent_compiler_ = $(_gir_silent_compiler_$(_gir_verbosity))
_gir_silent_compiler_0 = @echo "  GICOMP   $(1)";

_gir_default_scanner_env = CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" CC="$(CC)" PKG_CONFIG="$(PKG_CONFIG)" DLLTOOL="$(DLLTOOL)"

#
# Creates a GIR by scanning C headers/sources
# $(1) - Name of the gir file (output)
#
# If output is Gtk-2.0.gir then you should name the variables like
# Gtk_2_0_gir_NAMESPACE, Gtk_2_0_gir_VERSION etc.
# Required variables:
# FILES - C sources and headers which should be scanned
#
# One of these variables are required:
# LIBS - Library where the symbol represented in the gir can be found
# PROGRAM - Program where the symbol represented in the gir can be found
#
# Optional variables
# NAMESPACE - Namespace of the gir, first letter capital,
#   rest should be lower case, for instance: 'Gtk', 'Clutter', 'ClutterGtk'.
#   If not present the namespace will be fetched from the gir filename,
#   the part before the first dash. For 'Gtk-2.0', namespace will be 'Gtk'.
# VERSION - Version of the gir, if not present, will be fetched from gir
# filename, the part after the first dash. For 'Gtk-2.0', version will be '2.0'.
# LIBTOOL - Command to invoke libtool, usually set by automake
# SCANNERFLAGS - Flags to pass in to the scanner, see g-ir-scanner(1) for a list
# CFLAGS - Flags to pass in to the parser when scanning headers
# LDFLAGS - Linker flags used by the scanner
# PACKAGES - list of pkg-config names which cflags are required to parse
#   the headers of this gir
# INCLUDES - Gir files to include without the .gir suffix, for instance
#   GLib-2.0, Gtk-2.0. This is needed for all libraries which you depend on that
#   provides introspection information.
# EXPORT_PACKAGES - list of pkg-config names that are provided by this gir.
#   By default the names in the PACKAGES variable will be used.
#

define introspection-scanner

# Basic sanity check, to make sure required variables are set
$(if $($(_gir_name)_FILES),,$(error Need to define $(_gir_name)_FILES))
$(if $(or $(findstring --header-only,$($(_gir_name)_SCANNERFLAGS)),
          $($(_gir_name)_LIBS),
          $($(_gir_name)_PROGRAM)),,
    $(error Need to define $(_gir_name)_LIBS or $(_gir_name)_PROGRAM))

# Only dependencies we know are actually filenames goes into _FILES, make
# sure these are built before running the scanner. Libraries and programs
# needs to be added manually.
$(1): $$($(_gir_name)_FILES)
	@ $(MKDIR_P) $(dir $(1))
	$(_gir_silent_scanner_prefix) $(_gir_default_scanner_env) $(INTROSPECTION_SCANNER_ENV) $(INTROSPECTION_SCANNER) $(_gir_silent_scanner_opts) \
	$(INTROSPECTION_SCANNER_ARGS) \
	  --namespace=$(_gir_namespace) \
	  --nsversion=$(_gir_version) \
	  $(_gir_libtool) \
	  $(_gir_packages) \
	  $(_gir_includes) \
	  $(_gir_export_packages) \
	  $(_gir_program) \
	  $(_gir_libraries) \
	  $($(_gir_name)_SCANNERFLAGS) \
	  --cflags-begin \
	  $($(_gir_name)_CFLAGS) \
	  --cflags-end \
	  $($(_gir_name)_LDFLAGS) \
	  $$^ \
	  --output $(1)
endef

$(foreach gir,$(INTROSPECTION_GIRS),$(eval $(call introspection-scanner,$(gir))))

#
# Compiles a gir into a typelib
# $(1): gir filename (input)
# $(2): typelib filename (output)
#
define introspection-compiler
$(_gir_silent_compiler) $(INTROSPECTION_COMPILER) $(INTROSPECTION_COMPILER_ARGS) --includedir=. $(1) -o $(2)
endef

# Simple rule to compile a typelib.
%.typelib: %.gir
	$(call introspection-compiler,$<,$@)

Le fichier se décompose en plusieurs parties.

Tout d'abord, une ligne de mode incorrecte

Tout d'abord, une partie de tutoriel qui nous apprend comment utiliser ce Makefile.

Ensuite, une définition de variables, ayant pour préfixe _gir.

Ensuite, une définition de fonction, qui pour chaque fichier gir, vérifie que toutes les déclarations sont en ordre et définit la règle de compilation.

Enfin, tout à la fin se trouve une règle très simple pour créer un fichier .typelib, qui ne nous intéresse pas pour l'instant.

Je vous propose d'ajouter un appel récursif à make, ligne 135, et à modifier les emplacements des fichiers pour les chercher dans le répertoire source, lignes 35, 37, 133, 134, 152 et 163.

  1: # -*- Mode: makefile -*-
  2: # Copyright 2009-2010 Johan Dahlin
  3: #
  4: # This file is free software; the author(s) gives unlimited
  5: # permission to copy and/or distribute it, with or without
  6: # modifications, as long as this notice is preserved.
  7: #
  8: # * Input variables:
  9: #
 10: #   INTROSPECTION_GIRS - List of GIRS that should be generated
 11: #   INTROSPECTION_SCANNER - Command to invoke scanner, normally set by
 12: #      GOBJECT_INTROSPECTION_REQUIRE/CHECK() in introspection.m4
 13: #   INTROSPECTION_SCANNER_ARGS - Additional args to pass in to the scanner
 14: #   INTROSPECTION_SCANNER_ENV - Environment variables to set before running
 15: #      the scanner
 16: #   INTROSPECTION_COMPILER - Command to invoke compiler, normally set by
 17: #      GOBJECT_INTROSPECTION_REQUIRE/CHECK() in introspection.m4
 18: #   INTROSPECTION_COMPILER_ARGS - Additional args to pass in to the compiler
 19: #
 20: # * Simple tutorial
 21: #
 22: # Add this to configure.ac:
 23: #   -Wno-portability to AM_INIT_AUTOMAKE
 24: #   GOBJECT_INTROSPECTION_CHECK([0.6.7])
 25: #
 26: # Add this to Makefile.am where your library/program is built:
 27: #   include $(INTROSPECTION_MAKEFILE)
 28: #   INTROSPECTION_GIRS = YourLib-1.0.gir
 29: #   YourLib-1.0.gir: libyourlib.la
 30: #   YourLib_1_0_gir_NAMESPACE = YourLib
 31: #   YourLib_1_0_gir_VERSION = 1.0
 32: #   YourLib_1_0_gir_LIBS = libyourlib.la
 33: #   YourLib_1_0_gir_FILES = $(libyourlib_1_0_SOURCES)
 34: #   girdir = $(datadir)/gir-1.0
 35: #   dist_gir_DATA = $(srcdir)/YourLib-1.0.gir
 36: #   typelibdir = $(libdir)/girepository-1.0
 37: #   typelib_DATA = $(srcdir)/YourLib-1.0.typelib
 38: #
 39: 
 40: # Make sure the required variables are set, these should under normal
 41: # circumstances come from introspection.m4
 42: $(if $(INTROSPECTION_SCANNER),,$(error Need to define INTROSPECTION_SCANNER))
 43: $(if $(INTROSPECTION_COMPILER),,$(error Need to define INTROSPECTION_COMPILER))
 44: 
 45: # Private functions
 46: 
 47: ## Transform the gir filename to something which can reference through a variable
 48: ## without automake/make complaining, eg Gtk-2.0.gir -> Gtk_2_0_gir
 49: _gir_name = $(subst /,_,$(subst -,_,$(subst .,_,$(1))))
 50: 
 51: # Namespace and Version is either fetched from the gir filename
 52: # or the _NAMESPACE/_VERSION variable combo
 53: _gir_namespace = $(or $($(_gir_name)_NAMESPACE),$(firstword $(subst -, ,$(notdir $(1)))))
 54: _gir_version = $(or $($(_gir_name)_VERSION),$(lastword $(subst -, ,$(1:.gir=))))
 55: 
 56: # _PROGRAM is an optional variable which needs it's own --program argument
 57: _gir_program = $(if $($(_gir_name)_PROGRAM),--program=$($(_gir_name)_PROGRAM))
 58: 
 59: # Variables which provides a list of things
 60: _gir_libraries = $(foreach lib,$($(_gir_name)_LIBS),--library=$(lib))
 61: _gir_packages = $(foreach pkg,$($(_gir_name)_PACKAGES),--pkg=$(pkg))
 62: _gir_includes = $(foreach include,$($(_gir_name)_INCLUDES),--include=$(include))
 63: _gir_export_packages = $(foreach pkg,$($(_gir_name)_EXPORT_PACKAGES),--pkg-export=$(pkg))
 64: 
 65: # Reuse the LIBTOOL variable from automake if it's set, but
 66: # work around MSYS weirdness: When running g-ir-scanner, MSYS changes
 67: # a command-line argument --libtool="/bin/sh ../../libtool" into
 68: # --libtool=c:/opt/msys/1.0/bin/libtool. So just use sh.exe without path
 69: # because we already "know" where the libtool configure produced is.
 70: _gir_libtool = $(if $(findstring MINGW,$(shell uname -s)),--libtool="$(top_builddir)/libtool",$(if $(LIBTOOL),--libtool="$(LIBTOOL)"))
 71: 
 72: # Macros for AM_SILENT_RULES prettiness
 73: _gir_verbosity = $(if $(AM_DEFAULT_VERBOSITY),$(AM_DEFAULT_VERBOSITY),1)
 74: 
 75: _gir_silent_scanner_prefix = $(_gir_silent_scanner_prefix_$(V))
 76: _gir_silent_scanner_prefix_ = $(_gir_silent_scanner_prefix_$(_gir_verbosity))
 77: _gir_silent_scanner_prefix_0 = @echo "  GISCAN   $(1)";
 78: _gir_silent_scanner_opts = $(_gir_silent_scanner_opts_$(V))
 79: _gir_silent_scanner_opts_ = $(_gir_silent_scanner_opts_$(_gir_verbosity))
 80: _gir_silent_scanner_opts_0 = --quiet
 81: 
 82: _gir_silent_compiler = $(_gir_silent_compiler_$(V))
 83: _gir_silent_compiler_ = $(_gir_silent_compiler_$(_gir_verbosity))
 84: _gir_silent_compiler_0 = @echo "  GICOMP   $(1)";
 85: 
 86: _gir_default_scanner_env = CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" CC="$(CC)" PKG_CONFIG="$(PKG_CONFIG)" DLLTOOL="$(DLLTOOL)"
 87: 
 88: #
 89: # Creates a GIR by scanning C headers/sources
 90: # $(1) - Name of the gir file (output)
 91: #
 92: # If output is Gtk-2.0.gir then you should name the variables like
 93: # Gtk_2_0_gir_NAMESPACE, Gtk_2_0_gir_VERSION etc.
 94: # Required variables:
 95: # FILES - C sources and headers which should be scanned
 96: #
 97: # One of these variables are required:
 98: # LIBS - Library where the symbol represented in the gir can be found
 99: # PROGRAM - Program where the symbol represented in the gir can be found
100: #
101: # Optional variables
102: # NAMESPACE - Namespace of the gir, first letter capital,
103: #   rest should be lower case, for instance: 'Gtk', 'Clutter', 'ClutterGtk'.
104: #   If not present the namespace will be fetched from the gir filename,
105: #   the part before the first dash. For 'Gtk-2.0', namespace will be 'Gtk'.
106: # VERSION - Version of the gir, if not present, will be fetched from gir
107: # filename, the part after the first dash. For 'Gtk-2.0', version will be '2.0'.
108: # LIBTOOL - Command to invoke libtool, usually set by automake
109: # SCANNERFLAGS - Flags to pass in to the scanner, see g-ir-scanner(1) for a list
110: # CFLAGS - Flags to pass in to the parser when scanning headers
111: # LDFLAGS - Linker flags used by the scanner
112: # PACKAGES - list of pkg-config names which cflags are required to parse
113: #   the headers of this gir
114: # INCLUDES - Gir files to include without the .gir suffix, for instance
115: #   GLib-2.0, Gtk-2.0. This is needed for all libraries which you depend on that
116: #   provides introspection information.
117: # EXPORT_PACKAGES - list of pkg-config names that are provided by this gir.
118: #   By default the names in the PACKAGES variable will be used.
119: #
120: 
121: define introspection-scanner
122: 
123: # Basic sanity check, to make sure required variables are set
124: $(if $($(_gir_name)_FILES),,$(error Need to define $(_gir_name)_FILES))
125: $(if $(or $(findstring --header-only,$($(_gir_name)_SCANNERFLAGS)),
126:           $($(_gir_name)_LIBS),
127:           $($(_gir_name)_PROGRAM)),,
128:     $(error Need to define $(_gir_name)_LIBS or $(_gir_name)_PROGRAM))
129: 
130: # Only dependencies we know are actually filenames goes into _FILES, make
131: # sure these are built before running the scanner. Libraries and programs
132: # needs to be added manually.
133: $(srcdir)/$(1): $$($(_gir_name)_FILES) #
134:         @ $(MKDIR_P) $(dir $(srcdir)/$(1)) #
135:         @ $(MAKE) $$($(_gir_name)_LIBS) $$($(_gir_name)_PROGRAM) #
136:         $(_gir_silent_scanner_prefix) $(_gir_default_scanner_env) $(INTROSPECTION_SCANNER_ENV) $(INTROSPECTION_SCANNER) $(_gir_silent_scanner_opts) \
137:         $(INTROSPECTION_SCANNER_ARGS) \
138:           --namespace=$(_gir_namespace) \
139:           --nsversion=$(_gir_version) \
140:           $(_gir_libtool) \
141:           $(_gir_packages) \
142:           $(_gir_includes) \
143:           $(_gir_export_packages) \
144:           $(_gir_program) \
145:           $(_gir_libraries) \
146:           $($(_gir_name)_SCANNERFLAGS) \
147:           --cflags-begin \
148:           $($(_gir_name)_CFLAGS) \
149:           --cflags-end \
150:           $($(_gir_name)_LDFLAGS) \
151:           $$^ \
152:           --output $(srcdir)/$(1) #
153: endef
154: 
155: $(foreach gir,$(INTROSPECTION_GIRS),$(eval $(call introspection-scanner,$(gir))))
156: 
157: #
158: # Compiles a gir into a typelib
159: # $(1): gir filename (input)
160: # $(2): typelib filename (output)
161: #
162: define introspection-compiler
163: $(_gir_silent_compiler) $(INTROSPECTION_COMPILER) $(INTROSPECTION_COMPILER_ARGS) --includedir=. $(srcdir)/$(1) -o $(srcdir)/$(2) #
164: endef
165: 
166: # Simple rule to compile a typelib.
167: %.typelib: %.gir
168:         $(call introspection-compiler,$<,$@)

4.2 Modifier notre système de compilation

Nous allons modifier le fichier Makefile.am pour :

  • utiliser notre fichier Makefile.introspection modifié (lignes 3 et 48) ;
  • inclure le .gir dans la distribution (ligne 48) ;
  • chercher le .gir dans le dossier source (ligne 36) ;
  • désactiver l'introspection dans la règle distcheck (ligne 1).
 1: DISTCHECK_CONFIGURE_FLAGS = --disable-introspection #
 2: 
 3: -include Makefile.introspection #
 4: 
 5: INTROSPECTION_GIRS =
 6: INTROSPECTION_SCANNER_ARGS = \
 7:         --add-include-path=$(srcdir) \
 8:         --libtool=$(top_abs_builddir)/libtool \
 9:         --add-init-section="if (luciole_init () != 0) exit (1);" \
10:         --c-include="luciole_glib.h" \
11:         --include-first-in-src="luciole_glib.h" \
12:         --accept-unprefixed \
13:         --warn-all
14: 
15: bin_PROGRAMS = luciole
16: lib_LTLIBRARIES = libluciole.la
17: 
18: include_HEADERS = luciole.h luciole_glib.h
19: libluciole_la_SOURCES = luciole.h luciole_glib.h luciole.c luciole_glib.c
20: libluciole_la_CPPFLAGS = @GOBJECT_CFLAGS@
21: 
22: libluciole_la_LDFLAGS = -no-undefined -version-info 1:0:1 @GOBJECT_LIBS@
23: 
24: luciole_SOURCES = main.c
25: luciole_LDADD = libluciole.la
26: 
27: if HAVE_INTROSPECTION
28:   introspection_sources = $(libluciole_la_SOURCES)
29:   Luciole_0_2_gir_INCLUDES = GObject-2.0
30:   Luciole_0_2_gir_CFLAGS = $(INCLUDES) -I$(srcdir)
31:   Luciole_0_2_gir_LIBS = libluciole.la
32:   Luciole_0_2_gir_FILES = $(introspection_sources)
33:   INTROSPECTION_GIRS += Luciole-0.2.gir
34: 
35:   girdir = $(datadir)/gir-1.0
36:   gir_DATA = $(srcdir)/Luciole-0.2.gir #
37: 
38:   typelibdir = $(libdir)/girepository-1.0
39:   typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
40: 
41:   CLEANFILES = $(gir_DATA) $(typelib_DATA)
42: endif
43: 
44: ACLOCAL_AMFLAGS = -I m4
45: 
46: EXTRA_DIST = m4/introspection.m4 \
47:         $(srcdir)/Makefile.introspection \
48:         $(srcdir)/Luciole-0.2.gir #

4.3 Et c'est tout

Le reste du code source n'a pas changé, à part :

  • configure.ac, pour changer le numéro de version ;
  • README, pour pointer vers l'article correspondant ;
  • ChangeLog, pour tenir compte de nos petits changements.

5 Vérifions si tout fonctionne bien

L'ensemble des fichiers est disponible ici.

Compilons :

autoreconf --install &&
./configure \
    --enable-silent-rules \
    CPPFLAGS="-fsanitize=address" \
    LDFLAGS="-fsanitize=address" && \
make dist
> > > > libtoolize: putting auxiliary files in '.'.
libtoolize: copying file './ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
libtoolize: copying file 'm4/libtool.m4'
libtoolize: copying file 'm4/ltoptions.m4'
libtoolize: copying file 'm4/ltsugar.m4'
libtoolize: copying file 'm4/ltversion.m4'
libtoolize: copying file 'm4/lt~obsolete.m4'
configure.ac:10: installing './compile'
configure.ac:11: installing './config.guess'
configure.ac:11: installing './config.sub'
configure.ac:6: installing './install-sh'
configure.ac:6: installing './missing'
Makefile.am: installing './INSTALL'
Makefile.am: installing './COPYING' using GNU General Public License v3 file
Makefile.am:     Consider adding the COPYING file to the version control system
Makefile.am:     for your code, to avoid questions about which license your project uses
Makefile.am: installing './depcomp'
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
(MAKE)... yes
checking whether make supports nested variables... yes
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking dependency style of gcc... gcc3
checking for gcc option to accept ISO C99... none needed
checking for gcc option to accept ISO Standard C... (cached) none needed
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking how to print strings... printf
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1635000
checking how to convert x86_64-pc-linux-gnu file names to x86_64-pc-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
s\n
checking for ar... ar
checking for archiver @FILE support... @
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from gcc object... ok
checking for sysroot... no
checking for a working dd... /bin/dd
checking how to truncate binary pipes... /bin/dd bs=4096 count=1
checking for mt... mt
checking if mt is a manifest tool... no
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fPIC -DPIC
checking if gcc PIC flag -fPIC -DPIC works... yes
checking if gcc static flag -static works... no
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for gobject-introspection... yes
checking for GOBJECT... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
config.status: executing libtool commands
make  dist-gzip am__post_remove_distdir='@:'
make[1] : on entre dans le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
make[2] : on entre dans le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
  CC       libluciole_la-luciole.lo
  CC       libluciole_la-luciole_glib.lo
  CCLD     libluciole.la
ar: `u' modifier ignored since `D' is the default (see `U')
make[2] : on quitte le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
  GISCAN   Luciole-0.2.gir
/home/vkraus/blog/code/luciole-gi-dist/tmp-introspect51d711hh/Luciole-0.2.c: In function ‘main’:
/home/vkraus/blog/code/luciole-gi-dist/tmp-introspect51d711hh/Luciole-0.2.c:602:7: warning: implicit declaration of function ‘luciole_init’ [-Wimplicit-function-declaration]
   if (luciole_init () != 0) exit (1);
       ^~~~~~~~~~~~
if test -d "luciole-0.2"; then find "luciole-0.2" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "luciole-0.2" || { sleep 5 && rm -rf "luciole-0.2"; }; else :; fi
test -d "luciole-0.2" || mkdir "luciole-0.2"
test -n "" \
|| find "luciole-0.2" -type d ! -perm -755 \
	-exec chmod u+rwx,go+rx {} \; -o \
  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -444 -exec /bin/bash /home/vkraus/blog/code/luciole-gi-dist/install-sh -c -m a+r {} {} \; \
|| chmod -R a+r "luciole-0.2"
{TAR-tar} chof - "$tardir" | GZIP=--best gzip -c >luciole-0.2.tar.gz
make[1] : on quitte le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
if test -d "luciole-0.2"; then find "luciole-0.2" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "luciole-0.2" || { sleep 5 && rm -rf "luciole-0.2"; }; else :; fi

Victoire ! La cible dist a bien généré l'introspection.

Voyons voir ce qu'il se passe lorsqu'on appelle la cible une seconde fois (il n'est pas nécessaire de refaire l'introspection) :

make dist
make  dist-gzip am__post_remove_distdir='@:'
make[1] : on entre dans le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
if test -d "luciole-0.2"; then find "luciole-0.2" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "luciole-0.2" || { sleep 5 && rm -rf "luciole-0.2"; }; else :; fi
test -d "luciole-0.2" || mkdir "luciole-0.2"
test -n "" \
|| find "luciole-0.2" -type d ! -perm -755 \
	-exec chmod u+rwx,go+rx {} \; -o \
  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -444 -exec /bin/bash /home/vkraus/blog/code/luciole-gi-dist/install-sh -c -m a+r {} {} \; \
|| chmod -R a+r "luciole-0.2"
{TAR-tar} chof - "$tardir" | GZIP=--best gzip -c >luciole-0.2.tar.gz
make[1] : on quitte le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
if test -d "luciole-0.2"; then find "luciole-0.2" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "luciole-0.2" || { sleep 5 && rm -rf "luciole-0.2"; }; else :; fi

L'introspection n'a pas été refaite. Maintenant, vérifions si l'introspection dépend bien du code source :

touch luciole_glib.c
make dist
make  dist-gzip am__post_remove_distdir='@:'
make[1] : on entre dans le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
make[2] : on entre dans le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
  CC       libluciole_la-luciole_glib.lo
  CCLD     libluciole.la
ar: `u' modifier ignored since `D' is the default (see `U')
make[2] : on quitte le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
  GISCAN   Luciole-0.2.gir
/home/vkraus/blog/code/luciole-gi-dist/tmp-introspecth3gsn9go/Luciole-0.2.c: In function ‘main’:
/home/vkraus/blog/code/luciole-gi-dist/tmp-introspecth3gsn9go/Luciole-0.2.c:602:7: warning: implicit declaration of function ‘luciole_init’ [-Wimplicit-function-declaration]
   if (luciole_init () != 0) exit (1);
       ^~~~~~~~~~~~
if test -d "luciole-0.2"; then find "luciole-0.2" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "luciole-0.2" || { sleep 5 && rm -rf "luciole-0.2"; }; else :; fi
test -d "luciole-0.2" || mkdir "luciole-0.2"
test -n "" \
|| find "luciole-0.2" -type d ! -perm -755 \
	-exec chmod u+rwx,go+rx {} \; -o \
  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
  ! -type d ! -perm -444 -exec /bin/bash /home/vkraus/blog/code/luciole-gi-dist/install-sh -c -m a+r {} {} \; \
|| chmod -R a+r "luciole-0.2"
{TAR-tar} chof - "$tardir" | GZIP=--best gzip -c >luciole-0.2.tar.gz
make[1] : on quitte le répertoire « /home/vkraus/blog/code/luciole-gi-dist »
if test -d "luciole-0.2"; then find "luciole-0.2" -type d ! -perm -200 -exec chmod u+w {} ';' && rm -rf "luciole-0.2" || { sleep 5 && rm -rf "luciole-0.2"; }; else :; fi

L'introspection a en effet été refaite.

((make distcheck > /tmp/distcheck-log 2>&1) &&
     echo "make distcheck fonctionne.") ||
    echo "make distcheck ne fonctionne pas, c'est grave.")
make distcheck fonctionne.

6 Conclusion

Nous avons un nouveau paquet logiciel ici, avec l'introspection GObject, distribuée. Le code source utilisateur est disponible ici, avec l'introspection. La compilation croisée est réparée, il suffit de faire une première fois make dist pour avoir l'introspection, puis avec un nouveau compilateur il n'y aura pas besoin de reconstruire l'introspection.

Il nous reste toujours à savoir ce qu'on va bien pouvoir faire de ce joli fichier XML…

Notes de bas de page:

1
Sauf si la bibliothèque charge des extensions qui définissent d'autres classes, mais ce n'est pas notre cas ici