Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gnome: Add support for gi-compile-repository for generate_gir #13997

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/markdown/Porting-from-autotools.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ gnome.generate_gir(asglib,
identifier_prefix : 'As',
export_packages : 'appstream-glib',
includes : ['GObject-2.0', 'Gio-2.0', 'GdkPixbuf-2.0'],
install : true
install : true,
use_gi_repository_compile: true
)
```

Expand Down
4 changes: 2 additions & 2 deletions mesonbuild/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File
return self._interpreter.find_program_impl(prog, required=required, version_func=version_func,
wanted=wanted, silent=silent, for_machine=for_machine)

def find_tool(self, name: str, depname: str, varname: str, required: bool = True,
def find_tool(self, name: str, depname: str, varname: T.Optional[str] = None, required: bool = True,
wanted: T.Optional[str] = None) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']:
# Look in overrides in case it's built as subproject
progobj = self._interpreter.program_from_overrides([name], [])
Expand All @@ -94,7 +94,7 @@ def find_tool(self, name: str, depname: str, varname: str, required: bool = True

# Check if pkgconfig has a variable
dep = self.dependency(depname, native=True, required=False, wanted=wanted)
if dep.found() and dep.type_name == 'pkgconfig':
if dep.found() and dep.type_name == 'pkgconfig' and varname:
value = dep.get_variable(pkgconfig=varname)
if value:
progobj = ExternalProgram(value)
Expand Down
56 changes: 39 additions & 17 deletions mesonbuild/modules/gnome.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class GenerateGir(TypedDict):
nsversion: str
sources: T.List[T.Union[FileOrString, build.GeneratedTypes]]
symbol_prefix: T.List[str]
use_gi_repository_compile: bool

class GtkDoc(TypedDict):

Expand Down Expand Up @@ -252,6 +253,7 @@ class GnomeModule(ExtensionModule):
def __init__(self, interpreter: 'Interpreter') -> None:
super().__init__(interpreter)
self.gir_dep: T.Optional[Dependency] = None
self.girepository_dep: T.Optional[Dependency] = None
self.giscanner: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None
self.gicompiler: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None
self.install_glib_compile_schemas = False
Expand Down Expand Up @@ -307,17 +309,18 @@ def _print_gdbus_warning() -> None:
@staticmethod
def _find_tool(state: 'ModuleState', tool: str) -> 'ToolType':
tool_map = {
'gio-querymodules': 'gio-2.0',
'glib-compile-schemas': 'gio-2.0',
'glib-compile-resources': 'gio-2.0',
'gdbus-codegen': 'gio-2.0',
'glib-genmarshal': 'glib-2.0',
'glib-mkenums': 'glib-2.0',
'g-ir-scanner': 'gobject-introspection-1.0',
'g-ir-compiler': 'gobject-introspection-1.0',
'gio-querymodules': ('gio-2.0', 'gio_querymodules'),
'glib-compile-schemas': ('gio-2.0', 'glib_compile_schemas'),
'glib-compile-resources': ('gio-2.0', 'glib_compile_resources'),
'gdbus-codegen': ('gio-2.0', 'gdbus_codegen'),
'glib-genmarshal': ('glib-2.0', 'glib_genmarshal'),
'glib-mkenums': ('glib-2.0', 'glib_mkenums'),
# TODO: Use gi_compile_repository once GLib exposes it
'gi-compile-repository': ('girepository-2.0', None),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code as-written does not allow looking up tools which are not in pkg-config, this adapts the code to allow finding tools via binary lookup only

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. That's just state.find_program() instead of state.find_tool()...
  2. Upstream MR has been merged (not released) so I guess we can simplify this again.

'g-ir-scanner': ('gobject-introspection-1.0', 'g_ir_scanner'),
'g-ir-compiler': ('gobject-introspection-1.0', 'g_ir_compiler'),
}
depname = tool_map[tool]
varname = tool.replace('-', '_')
depname, varname = tool_map[tool]
return state.find_tool(tool, depname, varname)

@typed_kwargs(
Expand Down Expand Up @@ -763,7 +766,7 @@ def _get_dependencies_flags(

return cflags, internal_ldflags, external_ldflags, gi_includes, depends

def _unwrap_gir_target(self, girtarget: T.Union[Executable, build.StaticLibrary, build.SharedLibrary], state: 'ModuleState'
def _unwrap_gir_target(self, girtarget: T.Union[Executable, build.StaticLibrary, build.SharedLibrary], state: 'ModuleState', use_gi_repository_compile: bool
) -> T.Union[Executable, build.StaticLibrary, build.SharedLibrary]:
if not isinstance(girtarget, (Executable, build.SharedLibrary,
build.StaticLibrary)):
Expand All @@ -772,7 +775,7 @@ def _unwrap_gir_target(self, girtarget: T.Union[Executable, build.StaticLibrary,
STATIC_BUILD_REQUIRED_VERSION = ">=1.58.1"
if isinstance(girtarget, (build.StaticLibrary)) and \
not mesonlib.version_compare(
self._get_gir_dep(state)[0].get_version(),
self._get_gir_dep(state, use_gi_repository_compile=use_gi_repository_compile)[0].get_version(),
STATIC_BUILD_REQUIRED_VERSION):
raise MesonException('Static libraries can only be introspected with GObject-Introspection ' + STATIC_BUILD_REQUIRED_VERSION)

Expand All @@ -787,12 +790,28 @@ def postconf_hook(self, b: build.Build) -> None:
if self.devenv is not None:
b.devenv.append(self.devenv)

def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, T.Union[Executable, 'ExternalProgram', 'OverrideProgram'],
T.Union[Executable, 'ExternalProgram', 'OverrideProgram']]:
def _supports_girepository2(self, state: 'ModuleState') -> bool:
glib_version = self._get_native_glib_version(state)

return mesonlib.version_compare(glib_version, '>= 2.79.2')

def _get_gir_dep(self, state: 'ModuleState', use_gi_repository_compile: bool
) -> T.Tuple[Dependency, T.Union[Executable, 'ExternalProgram', 'OverrideProgram'],
T.Union[Executable, 'ExternalProgram', 'OverrideProgram']]:
supports_girepository_2 = self._supports_girepository2(state)
if supports_girepository_2 and not self.girepository_dep and use_gi_repository_compile:
self.girepository_dep = state.dependency('girepository-2.0')

if not self.gir_dep:
self.gir_dep = state.dependency('gobject-introspection-1.0')
self.giscanner = self._find_tool(state, 'g-ir-scanner')
self.gicompiler = self._find_tool(state, 'g-ir-compiler')

if not self.gicompiler:
if use_gi_repository_compile and supports_girepository_2:
self.gicompiler = self._find_tool(state, 'gi-compile-repository')
else:
self.gicompiler = self._find_tool(state, 'g-ir-compiler')

return self.gir_dep, self.giscanner, self.gicompiler
Comment on lines 805 to 815
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation-wise, this is very problematic. The current design requires people to specify which compiler they want via a meson.build DSL keyword argument, but once passed for the first time it will cache the value globally.

This means for example that if you have a project which knows nothing about this new feature -- it supports meson 0.40.0, or more charitably, supports meson 1.6.0 -- it will configure to build a gir using g-ir-compiler, and then it invokes a subproject that has a minimum meson_version: '>=1.7.0' and makes heavy use of GIRepository 2.0 features and is simply incompatible with GIRepository 1.0... and fails to compile, even though it passes your use_gi_repository_compile: true kwarg, because meson simply detects that self.gicompiler is g-ir-compiler.

This is one of the reasons I was hoping it would be practical to simply use the newer tool by default. :)


@functools.lru_cache(maxsize=None)
Expand Down Expand Up @@ -1112,6 +1131,7 @@ def _get_scanner_ldflags(ldflags: T.Iterable[str]) -> T.Iterable[str]:
KwargInfo('install_dir_gir', (str, bool, NoneType),
deprecated_values={False: ('0.61.0', 'Use install_gir to disable installation')},
validator=lambda x: 'as boolean can only be false' if x is True else None),
KwargInfo('use_gi_repository_compile', (bool), default=False),
KwargInfo('install_typelib', (bool, NoneType), since='0.61.0'),
KwargInfo('install_dir_typelib', (str, bool, NoneType),
deprecated_values={False: ('0.61.0', 'Use install_typelib to disable installation')},
Expand All @@ -1127,11 +1147,13 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut
# Ensure we have a C compiler even in C++ projects.
state.add_language('c', MachineChoice.HOST)

girtargets = [self._unwrap_gir_target(arg, state) for arg in args[0]]
use_gi_repository_compile = kwargs['use_gi_repository_compile']

girtargets = [self._unwrap_gir_target(arg, state, use_gi_repository_compile=use_gi_repository_compile) for arg in args[0]]
if len(girtargets) > 1 and any(isinstance(el, Executable) for el in girtargets):
raise MesonException('generate_gir only accepts a single argument when one of the arguments is an executable')

gir_dep, giscanner, gicompiler = self._get_gir_dep(state)
gir_dep, giscanner, gicompiler = self._get_gir_dep(state, use_gi_repository_compile=use_gi_repository_compile)

ns = kwargs['namespace']
nsversion = kwargs['nsversion']
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env python3

import sys
import shutil

shutil.copy(sys.argv[1], sys.argv[2])
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation

import argparse
import shutil

def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('src')
parser.add_argument('dest')
args = parser.parse_args()

shutil.copy(args.src, args.dest)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "dep1.h"

struct _MesonDep1
{
GObject parent_instance;
};

G_DEFINE_TYPE (MesonDep1, meson_dep1, G_TYPE_OBJECT)

/**
* meson_dep1_new:
*
* Allocates a new #MesonDep1.
*
* Returns: (transfer full): a #MesonDep1.
*/
MesonDep1 *
meson_dep1_new (void)
{
return g_object_new (MESON_TYPE_DEP1, NULL);
}

static void
meson_dep1_finalize (GObject *object)
{
G_OBJECT_CLASS (meson_dep1_parent_class)->finalize (object);
}

static void
meson_dep1_class_init (MesonDep1Class *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);

object_class->finalize = meson_dep1_finalize;
}

static void
meson_dep1_init (MesonDep1 *self)
{
}

/**
* meson_dep1_just_return_it:
* @dep: a #MesonDep2.
*
* Returns the #MesonDep2 that is passed in
*
* Returns: (transfer none): a #MesonDep2
*/
MesonDep2*
meson_dep1_just_return_it (MesonDep1 *self, MesonDep2 *dep)
{
g_return_val_if_fail (MESON_IS_DEP1 (self), NULL);

return dep;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef MESON_DEP1_H
#define MESON_DEP1_H

#if !defined (MESON_TEST_1)
#error "MESON_TEST_1 not defined."
#endif

#include <glib-object.h>
#include "dep2/dep2.h"

G_BEGIN_DECLS

#define MESON_TYPE_DEP1 (meson_dep1_get_type())

G_DECLARE_FINAL_TYPE (MesonDep1, meson_dep1, MESON, DEP1, GObject)

MesonDep1 *meson_dep1_new (void);
MesonDep2 *meson_dep1_just_return_it (MesonDep1 *self,
MesonDep2 *dep);

G_END_DECLS

#endif /* MESON_DEP1_H */
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "dep2.h"

struct _MesonDep2
{
GObject parent_instance;

gchar *msg;
};

G_DEFINE_TYPE (MesonDep2, meson_dep2, G_TYPE_OBJECT)

enum {
PROP_0,
PROP_MSG,
LAST_PROP
};

static GParamSpec *gParamSpecs [LAST_PROP];

/**
* meson_dep2_new:
* @msg: The message to set.
*
* Allocates a new #MesonDep2.
*
* Returns: (transfer full): a #MesonDep2.
*/
MesonDep2 *
meson_dep2_new (const gchar *msg)
{
g_return_val_if_fail (msg != NULL, NULL);

return g_object_new (MESON_TYPE_DEP2,
"message", msg,
NULL);
}

static void
meson_dep2_finalize (GObject *object)
{
MesonDep2 *self = (MesonDep2 *)object;

g_clear_pointer (&self->msg, g_free);

G_OBJECT_CLASS (meson_dep2_parent_class)->finalize (object);
}

static void
meson_dep2_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MesonDep2 *self = MESON_DEP2 (object);

switch (prop_id)
{
case PROP_MSG:
g_value_set_string (value, self->msg);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}

static void
meson_dep2_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MesonDep2 *self = MESON_DEP2 (object);

switch (prop_id)
{
case PROP_MSG:
self->msg = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}

static void
meson_dep2_class_init (MesonDep2Class *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);

object_class->finalize = meson_dep2_finalize;
object_class->get_property = meson_dep2_get_property;
object_class->set_property = meson_dep2_set_property;

gParamSpecs [PROP_MSG] =
g_param_spec_string ("message",
"Message",
"The message to print.",
NULL,
(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));

g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
}

static void
meson_dep2_init (MesonDep2 *self)
{
}

/**
* meson_dep2_return_message:
* @self: a #MesonDep2.
*
* Returns the message.
*
* Returns: (transfer none): a const gchar*
*/
const gchar*
meson_dep2_return_message (MesonDep2 *self)
{
g_return_val_if_fail (MESON_IS_DEP2 (self), NULL);

return (const gchar*) self->msg;
}
Loading
Loading