Repository: mesonbuild/meson

Issue:
ENH: Access to pkg-config variables
With pkg-config, packages can define custom variables containing whatever build information they like. For example, Qt4 includes `moc_location` which points to its metaobject compiler. On Fedora, this executable is _not_ on the default path, so there is no way to find it (`find_program` fails, and there don't appear to be any paths associated with the `dependency` object.)

Having some method to access the pkg-config variables (i.e., run `pkg-config --variable=moc_location QtCore` in the above case) would be very helpful. I'm sure there are other variables that might be useful to capture in the same manner.



Discussion:
So it turns out that this does exist, as the `get_variable` method on a `PkgConfigDependency`. However, it does not appear to be documented. Additionally, the `qt4` and `qt5` modules do not query it for the correct `moc` location.

Nice! I've been wanting/needing this as well :)

Unfortunately, I don't think the method is exposed to the `meson.build` side of things.

If memory serves the reason it is not exposed is that a) nobody needed it yet and (more importantly) b) it is pkgconfig specific. Other dependency methods might have something similar so it would be nice to get one unified interface to all of them if possible.

On @sdroege's debian box, a simple `import('qt5')` fails fatally already with:

```
Detecting Qt tools.

Meson encountered an error in file subprojects/gst-plugins-base/tests/examples/overlay/meson.build, line 11, column 4:
Moc preprocessor is not for Qt 5. Output:

moc: could not exec '/usr/lib/x86_64-linux-gnu/qt4/bin/moc': No such file or directory
```

after uninstalling qt5-defaults (or if it's not installed in the first place).

That's pretty unfortunate, I think an import statement should never fail.

This affects gst-plugins-base, we'll have to comment out all qt5 stuff for now.

Note that when installing the qt4 moc, it will report some 4.x version when run there and still fail the import. The problem is _not_ that moc falls apart completely, but that any moc (or other) check in there during import is fatal if it fails.


Resolving patch:
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py
--- a/mesonbuild/dependencies.py
+++ b/mesonbuild/dependencies.py
@@ -63,6 +63,9 @@ def need_threads(self):
     def type_name(self):
         return self.type_name
 
+    def get_pkgconfig_variable(self, variable_name):
+        raise MesonException('Tried to get a pkg-config variable from a non-pkgconfig dependency.')
+
 class InternalDependency(Dependency):
     def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps):
         super().__init__('internal')
@@ -190,7 +193,7 @@ def _set_libs(self):
                 self.is_libtool = True
             self.libs.append(lib)
 
-    def get_variable(self, variable_name):
+    def get_pkgconfig_variable(self, variable_name):
         ret, out = self._call_pkgbin(['--variable=' + variable_name, self.name])
         variable = ''
         if ret != 0:
@@ -199,8 +202,7 @@ def get_variable(self, variable_name):
                                           (self.type_string, self.name))
         else:
             variable = out.strip()
-        mlog.debug('return of subprocess : %s' % variable)
-
+        mlog.debug('Got pkgconfig variable %s : %s' % (variable_name, variable))
         return variable
 
     def get_modversion(self):
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -284,7 +284,10 @@ def __init__(self, dep):
         self.held_object = dep
         self.methods.update({'found' : self.found_method,
                              'type_name': self.type_name_method,
-                             'version': self.version_method})
+                             'version': self.version_method,
+                             'version': self.version_method,
+                             'get_pkgconfig_variable': self.pkgconfig_method,
+                             })
 
     def type_name_method(self, args, kwargs):
         return self.held_object.type_name
@@ -298,6 +301,29 @@ def found_method(self, args, kwargs):
     def version_method(self, args, kwargs):
         return self.held_object.get_version()
 
+    def pkgconfig_method(self, args, kwargs):
+        if not isinstance(args, list):
+            args = [args]
+        if len(args) != 1:
+            raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
+        varname = args[0]
+        if not isinstance(varname, str):
+            raise InterpreterException('Variable name must be a string.')
+        return self.held_object.get_pkgconfig_variable(varname)
+
+class InternalDependencyHolder(InterpreterObject):
+    def __init__(self, dep):
+        InterpreterObject.__init__(self)
+        self.held_object = dep
+        self.methods.update({'found' : self.found_method,
+                             'version': self.version_method,
+                             })
+
+    def found_method(self, args, kwargs):
+        return True
+
+    def version_method(self, args, kwargs):
+        return self.held_object.get_version()
 
 class ExternalProgramHolder(InterpreterObject):
     def __init__(self, ep):
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -267,7 +267,7 @@ def get_dependencies_flags(self, deps, state, depends=None):
                     ldflags.update([lib])
 
                 if isinstance(dep, dependencies.PkgConfigDependency):
-                    girdir = dep.get_variable("girdir")
+                    girdir = dep.get_pkgconfig_variable("girdir")
                     if girdir:
                         gi_includes.update([girdir])
             elif isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
@@ -422,7 +422,7 @@ def generate_gir(self, state, args, kwargs):
                             )
                         ]
             elif isinstance(dep, dependencies.PkgConfigDependency):
-                girdir = dep.get_variable("girdir")
+                girdir = dep.get_pkgconfig_variable("girdir")
                 if girdir:
                     typelib_cmd += ["--includedir=%s" % (girdir, )]
 


Test patch:
diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build
--- a/test cases/linuxlike/1 pkg-config/meson.build	
+++ b/test cases/linuxlike/1 pkg-config/meson.build	
@@ -9,6 +9,10 @@ assert(dep.version().version_compare('>=1.2.8'), 'Pkg-config version numbers exp
 
 test('zlibtest', exe)
 
+zprefix = dep.get_pkgconfig_variable('prefix') # Always set but we can't be sure what the value is.
+# pkg-config returns empty string for not defined variables
+assert(dep.get_pkgconfig_variable('nonexisting') == '', 'Value of unknown variable is not empty.')
+
 # Test that dependencies of dependencies work.
 dep2 = declare_dependency(dependencies : dep)
 exe2 = executable('zlibprog2', 'prog.c', dependencies : dep2)