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 Nov 4, 2018
1 parent 63f4f94 commit f58ccd5
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 19 deletions.
10 changes: 10 additions & 0 deletions docs/markdown/Pkgconfig-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,20 @@ keyword arguments.
- `version` a string describing the version of this library
- `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>/pkgconfig-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>/pkgconfig-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.
7 changes: 7 additions & 0 deletions docs/markdown/snippets/uninstalled-pkgconfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## 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>/pkgconfig-uninstalled`. They can be used to build applications against
libraries built by meson without installing them, by pointing `PKG_CONFIG_PATH`
to that directory.
75 changes: 58 additions & 17 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, 'pkgconfig-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 @@ -276,7 +287,10 @@ def generate_libs_flags(libs):
install_dir = l.get_custom_install_dir()[0]
if install_dir is False:
continue
if isinstance(install_dir, str):
if uninstalled:
path = os.path.dirname(state.backend.get_target_filename_abs(l))
Lflag = '-L%s' % self._escape(path)
elif isinstance(install_dir, str):
Lflag = '-L${prefix}/%s ' % self._escape(self._make_relative(prefix, install_dir))
else: # install_dir is True
Lflag = '-L${libdir}'
Expand All @@ -290,22 +304,42 @@ def generate_libs_flags(libs):
mlog.warning(msg.format(l.name, 'name_suffix', lname, pcfile))
yield '-l%s' % lname

def generate_include_flags(libs):
Iflags = []
for l in libs:
if isinstance(l, str):
continue
for i in l.get_include_dirs():
curdir = i.get_curdir()
for d in i.get_incdirs():
for basedir in [state.environment.get_source_dir(),
state.environment.get_build_dir()]:
path = os.path.join(basedir, curdir, d)
Iflag = '-I%s' % self._escape(path)
if Iflag not in Iflags:
Iflags.append(Iflag)
yield Iflag

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_include_flags(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 @@ -400,6 +434,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
12 changes: 12 additions & 0 deletions run_unittests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3319,6 +3319,18 @@ 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, 'pkgconfig-uninstalled')

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
4 changes: 2 additions & 2 deletions 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 Expand Up @@ -37,7 +36,8 @@ test('pkgconfig-validation', pkgconfig,
# Test that name_prefix='' and name='libfoo' results in '-lfoo'
lib2 = shared_library('libfoo', 'simple.c',
name_prefix : '',
version : libver)
version : libver,
include_directories : include_directories('.'))

pkgg.generate(
libraries : lib2,
Expand Down

0 comments on commit f58ccd5

Please sign in to comment.