mesonbuild / meson

The Meson Build System
http://mesonbuild.com
Apache License 2.0
5.59k stars 1.63k forks source link

Vala pkg-config dependency checks don't look for vapi files #1195

Open nirbheek opened 7 years ago

nirbheek commented 7 years ago

Currently, our vala pkg-config support works like this:

1) We use a simple pkg-config dependency check: dependency('foo-2.4') 2) While compiling vala files for a target, we unconditionally add --pkg=foo-2.4 for each pkg-config dependency listed for that target

This is correct about 80% of the time. It breaks in the following cases:

a) There's no foo-2.4.vapi file corresponding to the foo-2.4.pc file. This can happen because of one of two reasons:

  1. No .vapi file is shipped with any vala package for that library (e.g.: uuid.pc)
  2. The correct vala development package hasn't been installed (e.g.: libosinfo-vala on Fedora)

b) We don't actually want to use that dependency in the Vala sources. It is only used in the C sources. This is harmless if a vapi file for that dep actually exists, but if it doesn't it will cause an unnecessary error. c) The foo-2.4.vapi file corresponding to the foo-2.4.pc file is shipped with the source tree. (e.g. govirt-1.0.vapi is shipped with the Boxes source tree. d) No foo-2.4.vapi file is needed because the information contained in the vapi file is embedded in the source (e.g. Boxes does this for libuuid).

I haven't found any cases where the vapi file has a different basename than the pc file.

nirbheek commented 7 years ago

Another requirement is that some vapis actually require other vapis to be usable. For instance, libosinfo-1.0 needs gio-2.0. So valac --pkg=libosinfo-1.0 test.vala will fail. You need to run valac --pkg=gio-2.0 --pkg=libosinfo-1.0 test.vala.

I think with all this combined, and looking at how this is done in Autotools, the best solution is something like this:

# Find all these pkg-config files or custom-type deps (like sdl2) at once
# returns a single ExternalDependency object
dependencies('uuid', 'glib-2.0', 'gio-2.0', 'libosinfo-1.0') # Implicitly method : 'auto'
# Find all these pkg-config files and corresponding vapi files
# returns a single ExternalDependency object
dependencies('glib-2.0', 'gio-2.0', 'libosinfo-1.0', method : ['vala', 'pkgconfig'])
# Find all these vapi files, returns a single ExternalDependency object
dependencies('glib-2.0', 'gio-2.0', 'libosinfo-1.0', method : 'vala')
arteymix commented 7 years ago

If you are to use a pkg-config dependency in a Vala target that does not have its corresponding system-wide VAPI, it will very likely result in a local file shipped along the sources. I think it's well covered with add_project_arguments now:

Maybe with add_project_include_directory #1139 we could have a more elegant solution?

add_project_arguments('--vapidir=vapi, language: 'vala')
add_project_include_directory('vapi', language: 'vala') # alternative

uuid_dep = dependency('uuid')

If we don't need the dependency from Vala, it's okay to create a dummy VAPI and it's likely that you will put some definition in it eventually.

You will not find cases where the VAPI is named differently. Doing that is very unfriendly because it prevents valac from resolving flags.

Also, if you use a VAPI that is not available system-wide, it's typically better to not expose its APIs.

The case that tickles me is when you have to combine both find_library from the C and Vala compilers (e.g. no pkg-config at all). I would love to see that combined in a single declare_dependency line and the included into a target.

Thus I think that we should systematically use pkg-config with dependency and extend the path for searching VAPI instead.

arteymix commented 7 years ago

Oh, also, the advantage of extending the VAPI path is that it gives you the ability to override system-wide ones. I use this for https://github.com/rainwoodman/vast to have a decent gobject-introspection-1.0.vapi.

nirbheek commented 7 years ago

If you are to use a pkg-config dependency in a Vala target that does not have its corresponding system-wide VAPI, it will very likely result in a local file shipped along the sources. I think it's well covered with add_project_arguments now

This is a workaround at best because it is not discoverable, the error message you get if you don't do this is arcane, it is inconsistent with how we handle vapis in other cases (such as generated vapis), and it also looks hacky (because it is).

The easiest way to use vapis available in the source tree is by just adding them to the list of sources for a target, and this already works just fine. What we want is for dependency to not add --pkg=foo without even checking if foo.vapi exists in the system locations. We do this by adding the method kwarg which will do a vapi search and will also solve #711 at the same time.

That would also solve the situation where you don't want to use the .vapi available in the system: just don't use method : 'vala' for that dependency.

But, while checking for vapis, you often need other vapis to be available at the same time, which is why I proposed dependencies. It also has the advantage that can print all the needed dependencies if some of them are not found, which also solves #985.

arteymix commented 7 years ago

I agree that we need a way to add a dependency to a target that does not imply a --pkg flag. I would be more in favor of a language-specific option like has_vapi:

uuid_dep = dependency('uuid', has_vapi: false)

For private bindings, it's effectively the best approach to add them directly to the sources, but for public ones, it's better to keep the in standard/extended paths as you would expect them to be. We could use the has_vapi flag with a private binding shipped with the sources.

This is the error you get, which is not that cryptic:

error: Package `uuid' not found in specified Vala API directories or GObject-Introspection GIR directories
Compilation failed: 1 error(s), 0 warning(s)

Ideally we would be searching for all the VAPIs at configure-time, but that's a non-trivial task. The search path depends on the API version of the compiler, the *.deps files found alongside and GIR packages are also supported. I think it's best to process them like we do for headers: use inclusion paths and explicit files directly on the target.

inigomartinez commented 7 years ago

I've been working on a vala project that uses x as a dependency, and although the pc file is called libgit2-glib-1.0.pc, the vapi file has a different base name: ggit-1.0.vapi.

Due to this issue, meson (actually ninja) produces an error because it's not able to find the provided vapi file.

astavale commented 7 years ago

@inigomartinez please see https://bugzilla.gnome.org/show_bug.cgi?id=787198 The VAPI should be renamed.

inigomartinez commented 7 years ago

I've been working with gsettings-desktop-schemas in gitg which is written in vala.

gitg provides the vapi file which is called gdesktop-enums-3.0.vapi and a metadata file called GDesktopEnums-3.0.metadata. These names follow the name used for the GIR file GDesktopEnums-3.0.gir, so they make sense. However the pc file is called gsettings-desktop-schemas.pc, which causes an error when the vapi file is not found, which dependency is already added by using valac.find_library.

I have my doubts about this situation. The package uses the pc file to exporte the include dir for the only installed header. Should the pc file be renamed, or maybe meson should provide a way to disable vala following this approach?

astavale commented 7 years ago

@inigomartinez:

gitg provides the vapi file which is called gdesktop-enums-3.0.vapi and a metadata file called GDesktopEnums-3.0.metadata. These names follow the name used for the GIR file GDesktopEnums-3.0.gir, so they make sense. However the pc file is called gsettings-desktop-schemas.pc, which causes an error when the vapi file is not found

The .metadata file has to follow the name of the GIR, but the VAPI generated can be called whatever you like. In this case it should be gsettings-desktop-schemas.vapi. Here is an example using vapigen for libvips:

vapigen \
         --library vips \
         --pkg gobject-2.0 \
         --pkg glib-2.0 \
         --pkg gmodule-2.0 \
         --metadatadir . \
         Vips-8.0-custom.vala \
         /usr/share/gir-1.0/Vips-8.0.gir

This produces a VAPI called vips.vapi. vapigen uses the library name passed to it and adds the .vapi extension. Using gnome.generate_vapi() in a meson.build file would be something like (untested):

gnome.generate_vapi( 'vips',
  packages: ['gobject-2.0', 'glib-2.0', 'gmodule-2.0'],
  sources: ['Vips-8.0', 'Vips-8.0-custom.vala']
  )

So you simply need to change gdesktop-enums-3.0.vapi to gsettings-desktop-schemas.vapi in your build.

I would question though why gitg is carrying several third party VAPIs: https://git.gnome.org/browse/gitg/tree/vapi I haven't checked all, but bindings like libsoup-2.4 are carried upstream and are likely to be removed from the Vala distribution itself shortly - see https://bugzilla.gnome.org/show_bug.cgi?id=773197 Having these VAPIs also distributed with gitg doesn't make sense.

So for gdesktop-enums-3.0.vapi you should ideally generate this as part of the gsettings-desktop-schemas project and rename the VAPI to gsettings-desktop-schemas.vapi. So it is then distributed with the development files.

Your example is more to do with how the Vala community manage distribution of bindings than it is to do with Meson working around any problems. If you can't get this upstream then gnome.generate_vapi should be enough to generate a correctly named VAPI.

inigomartinez commented 7 years ago

Thank you @astavale!

I have just sent a patch to rename the vapi file accordingly. Hope it gets merged so the "workaround" won't be necessary.

astavale commented 5 years ago

meson.get_compiler('vala').find_library('my_lib') only checks for VAPIs in the default Vala search paths, and not any additional paths added using add_project_arguments. It could help to use that function internally to check for a VAPI when dependency is used.

inigomartinez commented 5 years ago

No, find_library doesn't use any additional search paths from add_project_arguments. However, find_library has a parameter called dirs that can be used to add additional search paths.

gitg uses this feature to include custom VAPI packages.

astavale commented 5 years ago

@inigomartinez Thanks. I'll have a go at updating Meson's Vala docs to show that is the current best way of doing this.

Could be useful to have the VAPI search path added to in one place. Maybe:

meson.get_compiler('vala').include_directories = join_paths(meson.current_source_dir(), 'vapi')

Although that idea didn't get very far in #906

valpackett commented 3 years ago

The ability to customize what meson passes as the --pkg flag for a dependency is important for one more reason: valac can consume GIRs directly, without explicitly generating a vapi. (How was this completely ignored by meson devs?) And gir files don't have pkgconfig-matching names, they have CamelCase names.

astavale commented 3 years ago

The ability to customize what meson passes as the --pkg flag for a dependency is important for one more reason: valac can consume GIRs directly, without explicitly generating a vapi. (How was this completely ignored by meson devs?) And gir files don't have pkgconfig-matching names, they have CamelCase names.

The is what vapigen is for and why VAPIs distributed with Vala are built that way - see how all the VAPIs are generated from GIRs in the Vala source tree. Consuming GIRs the way you suggest is slow and can also produce unexpected errors - that is what all the metadata is for.

If you really wish to consume a GObject Introspection Repository that way have you tried something like:

add_project_arguments(['--pkg', `NotSomethingIRecommendBinding'], language: 'vala')
valpackett commented 3 years ago

I'm not the one writing Vala, I'm more on the side of the package being depended on :) Yes, a custom --pkg arg is what I recommended, but that feels janky, it would be much better if the dependency(..) object would still be used.

slow and can also produce unexpected errors

Not if the gir is like, 5 very simple functions in total.

astavale commented 3 years ago

I'm not the one writing Vala, I'm more on the side of the package being depended on :)

If it's any help, there is gnome.generate_vapi() so a VAPI can be generated and distributed as part of the dependent package. Alternatively that Meson function can instead be used by the Vala application if upstream don't want to include a VAPI in their distributed package.

valpackett commented 3 years ago

it would be much better if the dependency(..) object would still be used

And actually, it's required because meson does linking on its own (rather than via valac), and without the dependency it won't link the library..

astavale commented 3 years ago

it would be much better if the dependency(..) object would still be used

And actually, it's required because meson does linking on its own (rather than via valac), and without the dependency it won't link the library..

When I added a lot more details about using libraries with Vala in the Meson Vala documentation I found that dependency() does a lot behind the scenes and it requires some conventions to be followed to allow the interface files, both VAPI and C headers, to be found. The main convention is to use the same filename for the VAPI file and pkg-config file, this is a convention for Vala, not just Meson.

The problem you have is the GIR is a different name, including a version number, so in complex cases I found that using each compiler's find_library() method worked better. This is detailed in the documentation mentioned. So dependency() gets split in to meson.get_compiler('vala').find_library() and meson.get_compiler('c').find_library(). I've not tested it with GIR files, but it does work for more complex configurations. I'm not sure how additional search paths and filename conversions can be built in to dependency() to make it as simple as you suggest.