Skip to content

Commit

Permalink
pkgconfig: Improve and document generator behaviour
Browse files Browse the repository at this point in the history
- Add libraries from InternalDependency.libraries
- Deprecate association of libraries from the "libraries" keyword
argument to the generated pkg-config file.
  • Loading branch information
xclaesse committed Dec 4, 2018
1 parent 377719c commit 8612f15
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 19 deletions.
57 changes: 47 additions & 10 deletions docs/markdown/Pkgconfig-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ This module is a simple generator for

## Usage

To use this module, just do: **`pkg = import('pkgconfig')`**. The
following function will then be available as `pkg.generate()`. You
can, of course, replace the name `pkg` with anything else.
```meson
pkg = import('pkgconfig')
bar_dep = dependency('bar')
lib = library('foo', dependencies : [bar])
pkg.generate(lib)
```

### pkg.generate()

Expand All @@ -25,14 +28,13 @@ keyword arguments.
- `libraries` a list of built libraries (usually results of
shared_library) that the user needs to link against. Arbitrary strings can
also be provided and they will be added into the `Libs` field. Since 0.45.0
dependencies of built libraries will be automatically added to `Libs.private`
field. If a dependency is provided by pkg-config then it will be added in
`Requires.private` instead. Other type of dependency objects can also be passed
and will result in their `link_args` and `compile_args` to be added to `Libs`
and `Cflags` fields.
dependencies of built libraries will be automatically added, see the
[Implicit dependencies](#Implicit_dependencies) section below for the exact
rules.
- `libraries_private` list of built libraries or strings to put in the
`Libs.private` field. Since 0.45.0 it can also contain dependency objects,
their `link_args` will be added to `Libs.private`.
`Libs.private` field. Since 0.45.0 dependencies of built libraries will be
automatically added, see the [Implicit dependencies](#Implicit_dependencies)
section below for the exact rules.
- `name` the name of this library, used to set the `Name:` field
- `subdirs` which subdirs of `include` should be added to the header
search path, for example if you install headers into
Expand All @@ -59,3 +61,38 @@ provided for all required fields of the pc file:
- `install_dir` is set to `pkgconfig` folder in the same location than the provided library.
- `description` is set to the project's name followed by the library's name.
- `name` is set to the library's name.

### Implicit dependencies

The exact rules followed to find dependencies that are implicitly added into the
pkg-config file have evolved over time. Here are the rules as of Meson *0.49.0*,
previous versions might have slightly different behaviour.

- Not found libraries or dependencies are ignored.
- Libraries and dependencies are private by default (i.e. added into
`Requires.private:` or `Libs.private:`) unless they are explicitly added in
`libraries` or `requires` keyword arguments, or is the main library (first
positional argument).
- Libraries and dependencies will be de-duplicated, if they are added in both
public and private (e.g `Requires:` and `Requires.private:`) it will be removed
from the private list.
- Shared libraries (i.e. `shared_library()` and **NOT** `library()`) add only
`-lfoo` into `Libs:` or `Libs.private:` but their dependencies are not pulled.
This is because dependencies are only needed for static link.
- Other libraries (i.e. `static_library()` or `library()`) add `-lfoo` into `Libs:`
or `Libs.private:` and recursively add their dependencies into `Libs.private:` or
`Requires.private:`.
- Dependencies provided by pkg-config are added into `Requires:` or
`Requires.private:`. If a version was specified when declaring that dependency
it will be written into the generated file too.
- The thread dependency (i.e. `dependency('thread')`) adds `-pthread` into
`Libs:` or `Libs.private:`.
- Internal dependencies (i.e.
`declare_dependency(compiler_args : '-DFOO', link_args : '-Wl,something', link_with : foo)`)
add `compiler_args` into `Cflags:` if public, `link_args` and `link_with` into
`Libs:` if public or `Libs.private:` if private.
- Other dependency types add their compiler arguments into `Cflags:` if public,
and linker arguments into `Libs:` if public or `Libs.private:` if private.
- Once a pkg-config file is generated for a library using `pkg.generate(mylib)`,
any subsequent call to `pkg.generate()` where mylib appears, will generate a
`Requires:` or `Requires.private` instead of a `Libs:` or `Libs.private:`.
34 changes: 34 additions & 0 deletions docs/markdown/snippets/pkgconfig_break.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Deprecation warning in pkg-config generator

All libraries passed to the `libraries` keyword argument of the `generate()`
method used to be associated with that generated pkg-config file. That means
that any subsequent call to `generate()` where those libraries appear would add
the filebase of the `generate()` that first contained them into `Requires:` or
`Requires.private:` field instead of adding an `-l` to `Libs:` or `Libs.private:`.

This behaviour is now deprecated. The library that should be associated with
the generated pkg-config file should be passed as first positional argument
instead of in the `libraries` keyword argument. The previous behaviour is
maintained but prints a deprecation warning and support for this will be removed
in a future Meson release. If you can not create the needed pkg-config file
without this warning, please file an issue with as much details as possible
about the situation.

For example this sample will write `Requires: liba` into `libb.pc` but print a
deprecation warning:
```meson
liba = library(...)
pkg.generate(libraries : liba)
libb = library(...)
pkg.generate(libraries : [liba, libb])
```

It can be fixed by passing `liba` as first positional argument::
```meson
liba = library(...)
pkg.generate(liba)
libb = library(...)
pkg.generate(libb, libraries : [liba])
```
48 changes: 40 additions & 8 deletions mesonbuild/modules/pkgconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import os, types
from pathlib import PurePath

from .. import build
Expand Down Expand Up @@ -50,11 +50,24 @@ def add_pub_reqs(self, reqs):
def add_priv_reqs(self, reqs):
self.priv_reqs += self._process_reqs(reqs)

def _check_generated_pc_deprecation(self, obj):
if hasattr(obj, 'generated_pc_warn'):
mlog.deprecation('Library', mlog.bold(obj.name), 'was passed to the '
'"libraries" keyword argument of a previous call '
'to generate() method instead of first positional '
'argument.', 'Adding', mlog.bold(obj.generated_pc),
'to "Requires" field, but this is a deprecated '
'behaviour that will change in a future version '
'of Meson. Please report the issue if this '
'warning cannot be avoided in your case.',
location=obj.generated_pc_warn)

def _process_reqs(self, reqs):
'''Returns string names of requirements'''
processed_reqs = []
for obj in mesonlib.listify(reqs, unholder=True):
if hasattr(obj, 'generated_pc'):
self._check_generated_pc_deprecation(obj)
processed_reqs.append(obj.generated_pc)
elif hasattr(obj, 'pcdep'):
pcdeps = mesonlib.listify(obj.pcdep)
Expand Down Expand Up @@ -93,7 +106,8 @@ def _process_libs(self, libs, public):
for d in pcdeps:
processed_reqs.append(d.name)
self.add_version_reqs(d.name, obj.version_reqs)
elif hasattr(obj, 'generated_pc') and obj.generated_pc != self.name:
elif hasattr(obj, 'generated_pc'):
self._check_generated_pc_deprecation(obj)
processed_reqs.append(obj.generated_pc)
elif isinstance(obj, dependencies.PkgConfigDependency):
if obj.found():
Expand All @@ -102,6 +116,14 @@ def _process_libs(self, libs, public):
elif isinstance(obj, dependencies.ThreadDependency):
processed_libs += obj.get_compiler().thread_link_flags(obj.env)
processed_cflags += obj.get_compiler().thread_flags(obj.env)
elif isinstance(obj, dependencies.InternalDependency):
if obj.found():
processed_libs += obj.get_link_args()
processed_cflags += obj.get_compile_args()
if public:
self.add_pub_libs(obj.libraries)
else:
self.add_priv_libs(obj.libraries)
elif isinstance(obj, dependencies.Dependency):
if obj.found():
processed_libs += obj.get_link_args()
Expand All @@ -114,14 +136,8 @@ def _process_libs(self, libs, public):
# than needed build deps.
# See https://bugs.freedesktop.org/show_bug.cgi?id=105572
processed_libs.append(obj)
if public:
if not hasattr(obj, 'generated_pc'):
obj.generated_pc = self.name
elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)):
processed_libs.append(obj)
if public:
if not hasattr(obj, 'generated_pc'):
obj.generated_pc = self.name
if isinstance(obj, build.StaticLibrary) and public:
self.add_pub_libs(obj.get_dependencies(internal=False))
self.add_pub_libs(obj.get_external_deps())
Expand Down Expand Up @@ -407,6 +423,22 @@ def parse_variable_list(stringlist):
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables)
res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), pcfile), pkgroot)
# Associate the main library with this generated pc file. If the library
# is used in any subsequent call to the generated, it will generate a
# 'Requires:' or 'Requires.private:'.
# Backward compatibility: We used to set 'generated_pc' on all public
# libraries instead of just the main one. Keep doing that but warn if
# anyone is relying on that deprecated behaviour.
if mainlib:
if not hasattr(mainlib, 'generated_pc'):
mainlib.generated_pc = filebase
else:
mlog.warning('Already generated a pkg-config file for', mlog.bold(mainlib.name))
for lib in deps.pub_libs:
if not isinstance(lib, str) and not hasattr(lib, 'generated_pc'):
lib.generated_pc = filebase
lib.generated_pc_warn = types.SimpleNamespace(subdir=state.subdir,
lineno=state.current_lineno)
return ModuleReturnValue(res, [res])

def initialize(*args, **kwargs):
Expand Down
3 changes: 3 additions & 0 deletions test cases/common/48 pkgconfig-gen/dependencies/custom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int custom_function() {
return 42;
}
3 changes: 2 additions & 1 deletion test cases/common/48 pkgconfig-gen/dependencies/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pkgg = import('pkgconfig')
exposed_lib = shared_library('libexposed', 'exposed.c')
internal_lib = shared_library('libinternal', 'internal.c')
main_lib = both_libraries('libmain', link_with : [exposed_lib, internal_lib])
custom_lib = shared_library('custom', 'custom.c')

pkgg.generate(exposed_lib)

Expand All @@ -14,7 +15,7 @@ pc_dep = dependency('libfoo', version : '>=1.0')
pc_dep_dup = dependency('libfoo', version : '>= 1.0')
notfound_dep = dependency('notfound', required : false)
threads_dep = dependency('threads')
custom_dep = declare_dependency(link_args : ['-lcustom'], compile_args : ['-DCUSTOM'])
custom_dep = declare_dependency(link_with : custom_lib, compile_args : ['-DCUSTOM'])
custom2_dep = declare_dependency(link_args : ['-lcustom2'], compile_args : ['-DCUSTOM2'])

# Generate a PC file:
Expand Down

0 comments on commit 8612f15

Please sign in to comment.