Skip to content

Commit

Permalink
pkgconfig: Generate -uninstalled.pc files
Browse files Browse the repository at this point in the history
  • Loading branch information
xclaesse committed Dec 4, 2018
1 parent 2ba97d5 commit 2aa5986
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 17 deletions.
11 changes: 11 additions & 0 deletions docs/markdown/Pkgconfig-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,21 @@ keyword arguments.
`Version:` field. Defaults to the project version if unspecified.
- `d_module_versions` a list of module version flags used when compiling
D sources referred to by this pkg-config file
- `uninstalled_variables` used instead of the `variables` keyword argument, when
generating the uninstalled pkg-config file. Since *0.49.0*

Since 0.46 a `StaticLibrary` or `SharedLibrary` object can optionally be passed
as first positional argument. If one is provided a default value will be
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.

Since 0.49.0 uninstalled pkg-config files are generated as well. They are
located in `<build dir>/meson-uninstalled/`. It is sometimes
useful to build projects against libraries built by meson without having to
install them into a prefix. In order to do so, just set
`PKG_CONFIG_PATH=<builddir>/meson-uninstalled` before building your
application. That will cause pkg-config to prefer those `-uninstalled.pc` files
and find libraries and headers from the meson builddir. This is an experimental
feature provided on a best-effort basis, it might not work in all use-cases.
8 changes: 8 additions & 0 deletions docs/markdown/snippets/uninstalled-pkgconfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Uninstalled pkg-config files

The `pkgconfig` module now generates uninstalled pc files as well. For any generated
`foo.pc` file, an extra `foo-uninstalled.pc` file is placed into
`<builddir>/meson-uninstalled`. They can be used to build applications against
libraries built by meson without installing them, by pointing `PKG_CONFIG_PATH`
to that directory. This is an experimental feature provided on a best-effort
basis, it might not work in all use-cases.
81 changes: 65 additions & 16 deletions mesonbuild/modules/pkgconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,30 @@ def _make_relative(self, prefix, subdir):
return subdir

def generate_pkgconfig_file(self, state, deps, subdirs, name, description,
url, version, pcfile, conflicts, variables):
url, version, pcfile, conflicts, variables,
uninstalled=False):
deps.remove_dups()
coredata = state.environment.get_coredata()
outdir = state.environment.scratch_dir
fname = os.path.join(outdir, pcfile)
prefix = PurePath(coredata.get_builtin_option('prefix'))
# These always return paths relative to prefix
libdir = PurePath(coredata.get_builtin_option('libdir'))
incdir = PurePath(coredata.get_builtin_option('includedir'))
if not uninstalled:
fname = os.path.join(state.environment.scratch_dir, pcfile)
else:
outdir = os.path.join(state.environment.build_dir, 'meson-uninstalled')
if not os.path.exists(outdir):
os.mkdir(outdir)
fname = os.path.join(outdir, pcfile)
with open(fname, 'w') as ofile:
ofile.write('prefix={}\n'.format(self._escape(prefix)))
ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
if not uninstalled:
prefix = PurePath(coredata.get_builtin_option('prefix'))
# These always return paths relative to prefix
libdir = PurePath(coredata.get_builtin_option('libdir'))
incdir = PurePath(coredata.get_builtin_option('includedir'))
ofile.write('prefix={}\n'.format(self._escape(prefix)))
ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
else:
ofile.write('prefix=\n')
ofile.write('libdir=\n')
ofile.write('includedir=\n')
if variables:
ofile.write('\n')
for k, v in variables:
Expand Down Expand Up @@ -272,6 +283,11 @@ def generate_libs_flags(libs):
for l in libs:
if isinstance(l, str):
yield l
elif uninstalled:
path = state.backend.get_target_filename_abs(l)
if path not in Lflags:
Lflags.append(path)
yield path
else:
install_dir = l.get_custom_install_dir()[0]
if install_dir is False:
Expand All @@ -297,22 +313,48 @@ def generate_libs_flags(libs):
if 'cs' not in l.compilers:
yield '-l%s' % lname

def get_uninstalled_include_dirs(libs):
result = []
for l in libs:
if isinstance(l, str):
continue
if l.get_subdir() not in result:
result.append(l.get_subdir())
for i in l.get_include_dirs():
curdir = i.get_curdir()
for d in i.get_incdirs():
path = os.path.join(curdir, d)
if path not in result:
result.append(path)
return result

def generate_uninstalled_cflags(libs):
for d in get_uninstalled_include_dirs(libs):
for basedir in [state.environment.get_source_dir(),
state.environment.get_build_dir()]:
path = os.path.join(basedir, d)
yield '-I%s' % self._escape(path)

if len(deps.pub_libs) > 0:
ofile.write('Libs: {}\n'.format(' '.join(generate_libs_flags(deps.pub_libs))))
if len(deps.priv_libs) > 0:
ofile.write('Libs.private: {}\n'.format(' '.join(generate_libs_flags(deps.priv_libs))))
ofile.write('Cflags:')
for h in subdirs:
ofile.write(' ')
if h == '.':
ofile.write('-I${includedir}')
else:
ofile.write(self._escape(PurePath('-I${includedir}') / h))
if uninstalled:
ofile.write(' '.join(generate_uninstalled_cflags(deps.pub_libs + deps.priv_libs)))
else:
for h in subdirs:
ofile.write(' ')
if h == '.':
ofile.write('-I${includedir}')
else:
ofile.write(self._escape(PurePath('-I${includedir}') / h))
for f in deps.cflags:
ofile.write(' ')
ofile.write(self._escape(f))
ofile.write('\n')

@FeatureNewKwargs('pkgconfig.generate', '0.49.0', ['uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags'])
@FeatureNewKwargs('pkgconfig.generate', '0.41.0', ['variables'])
@permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
Expand Down Expand Up @@ -407,6 +449,13 @@ 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)

variables = parse_variable_list(mesonlib.stringlistify(kwargs.get('uninstalled_variables', [])))
pcfile = filebase + '-uninstalled.pc'
self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,
uninstalled=True)

return ModuleReturnValue(res, [res])

def initialize(*args, **kwargs):
Expand Down
15 changes: 15 additions & 0 deletions run_unittests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3556,6 +3556,21 @@ def test_pkgconfig_gen_deps(self):
out = self._run(cmd + ['--print-requires-private']).strip().split('\n')
self.assertEqual(sorted(out), sorted(['libexposed', 'libfoo >= 1.0', 'libhello']))

def test_pkgconfig_uninstalled(self):
testdir = os.path.join(self.common_test_dir, '48 pkgconfig-gen')
self.init(testdir)
self.build()

os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(self.builddir, 'meson-uninstalled')
if is_cygwin():
os.environ['PATH'] += os.pathsep + self.builddir

self.new_builddir()
testdir = os.path.join(self.common_test_dir, '48 pkgconfig-gen', 'dependencies')
self.init(testdir)
self.build()
self.run_tests()

def check_pkg_flags_are_same(self, output, expected):
if is_osx() or is_haiku():
expected = [x for x in expected if x != '-pthread']
Expand Down
6 changes: 6 additions & 0 deletions test cases/common/48 pkgconfig-gen/dependencies/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <simple.h>

int main(int argc, char *argv[])
{
return simple_function() == 42 ? 0 : 1;
}
3 changes: 3 additions & 0 deletions test cases/common/48 pkgconfig-gen/dependencies/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ threads_dep = dependency('threads')
custom_dep = declare_dependency(link_args : ['-lcustom'], compile_args : ['-DCUSTOM'])
custom2_dep = declare_dependency(link_args : ['-lcustom2'], compile_args : ['-DCUSTOM2'])

exe = executable('test1', 'main.c', dependencies : [pc_dep])
test('Test1', exe)

# Generate a PC file:
# - Having libmain in libraries should pull implicitly libexposed and libinternal in Libs.private
# - Having libexposed in libraries should remove it from Libs.private
Expand Down
1 change: 0 additions & 1 deletion test cases/common/48 pkgconfig-gen/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
project('pkgconfig-gen', 'c')

# First check we have pkg-config >= 0.29

pkgconfig = find_program('pkg-config', required: false)
if not pkgconfig.found()
error('MESON_SKIP_TEST: pkg-config not found')
Expand Down

0 comments on commit 2aa5986

Please sign in to comment.