aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ci/azure-steps.yml4
-rw-r--r--docs/markdown/Pkgconfig-module.md11
-rw-r--r--docs/markdown/Reference-manual.md6
-rw-r--r--docs/markdown/snippets/uninstalled-pkgconfig.md8
-rw-r--r--mesonbuild/build.py4
-rw-r--r--mesonbuild/coredata.py6
-rw-r--r--mesonbuild/environment.py2
-rw-r--r--mesonbuild/interpreter.py27
-rw-r--r--mesonbuild/modules/pkgconfig.py86
-rwxr-xr-xrun_unittests.py23
-rw-r--r--test cases/common/14 configure file/meson.build6
-rw-r--r--test cases/common/144 custom target multiple outputs/meson.build13
-rw-r--r--test cases/common/47 pkgconfig-gen/dependencies/main.c6
-rw-r--r--test cases/common/47 pkgconfig-gen/dependencies/meson.build3
-rw-r--r--test cases/common/47 pkgconfig-gen/meson.build1
15 files changed, 176 insertions, 30 deletions
diff --git a/ci/azure-steps.yml b/ci/azure-steps.yml
index ef31208..66a7eed 100644
--- a/ci/azure-steps.yml
+++ b/ci/azure-steps.yml
@@ -102,6 +102,10 @@ steps:
$vcvars = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
}
+ ## A multiline commit message containing "=" can interact badly with this
+ ## hack to extract the environment from vcvarsall.bat
+ Remove-Item env:BUILD_SOURCEVERSIONMESSAGE
+
## ask cmd.exe to output the environment table after the batch file completes
$tempFile = [IO.Path]::GetTempFileName()
cmd /c " `"$vcvars`" $env:arch && set > `"$tempFile`" "
diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md
index 4aa82f6..678090b 100644
--- a/docs/markdown/Pkgconfig-module.md
+++ b/docs/markdown/Pkgconfig-module.md
@@ -54,6 +54,8 @@ keyword arguments.
`Version:` field. (*since 0.46.0*) 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.54.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
@@ -62,6 +64,15 @@ provided for all required fields of the pc file:
- `description` is set to the project's name followed by the library's name.
- `name` is set to the library's name.
+Since 0.54.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.
+
### Implicit dependencies
The exact rules followed to find dependencies that are implicitly added into the
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 57fc1f7..e3830cc 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -2299,6 +2299,8 @@ contains a target with the following methods:
NOTE: In most cases using the object itself will do the same job as
this and will also allow Meson to setup inter-target dependencies
correctly. Please file a bug if that doesn't work for you.
+ *Since 0.54.0* it can be also called on indexes objects:
+ `custom_targets[i].full_path()`.
- `[index]` returns an opaque object that references this target, and
can be used as a source in other targets. When it is used as such it
@@ -2306,6 +2308,10 @@ contains a target with the following methods:
source added will be the one that corresponds to the index of the
custom target's output argument.
+- `to_list()` *Since 0.54.0*, returns a list of opaque objects that references
+ this target, and can be used as a source in other targets. This can be used to
+ iterate outputs with `foreach` loop.
+
### `dependency` object
This object is returned by [`dependency()`](#dependency) and contains
diff --git a/docs/markdown/snippets/uninstalled-pkgconfig.md b/docs/markdown/snippets/uninstalled-pkgconfig.md
new file mode 100644
index 0000000..2e265ab
--- /dev/null
+++ b/docs/markdown/snippets/uninstalled-pkgconfig.md
@@ -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.
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 1db930e..0a8ca45 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -2250,6 +2250,10 @@ class CustomTarget(Target):
def __delitem__(self, index):
raise NotImplementedError
+ def __iter__(self):
+ for i in self.outputs:
+ yield CustomTargetIndex(self, i)
+
class RunTarget(Target):
def __init__(self, name, command, args, dependencies, subdir, subproject):
self.typename = 'run'
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 44eaa02..cdee20d 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -501,7 +501,7 @@ class CoreData:
# Create builtin options with default values
self.builtins = {}
for key, opt in builtin_options.items():
- self.builtins[key] = opt.init_option()
+ self.builtins[key] = opt.init_option(key, default_prefix())
self.builtins_per_machine = PerMachine({}, {})
for for_machine in iter(MachineChoice):
for key, opt in builtin_options_per_machine.items():
@@ -959,9 +959,9 @@ class BuiltinOption(T.Generic[_T, _U]):
self.choices = choices
self.yielding = yielding
- def init_option(self) -> _U:
+ def init_option(self, name: str = 'prefix', prefix: str = '') -> _U:
"""Create an instance of opt_type and return it."""
- keywords = {'yielding': self.yielding, 'value': self.default}
+ keywords = {'yielding': self.yielding, 'value': self.prefixed_default(name, prefix)}
if self.choices:
keywords['choices'] = self.choices
return self.opt_type(self.description, **keywords)
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 21d33a5..beaff76 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -1336,6 +1336,8 @@ class Environment:
cls = MonoCompiler
elif "Visual C#" in out:
cls = VisualStudioCsCompiler
+ else:
+ continue
self.coredata.add_lang_args(cls.language, cls, for_machine, self)
return cls(comp, version, for_machine, info)
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 0d87bab..e32e0a1 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -871,15 +871,23 @@ class JarHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
-class CustomTargetIndexHolder(InterpreterObject, ObjectHolder):
- def __init__(self, object_to_hold):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, object_to_hold)
+class CustomTargetIndexHolder(TargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+ self.methods.update({'full_path': self.full_path_method,
+ })
+
+ @FeatureNew('custom_target[i].full_path', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def full_path_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
class CustomTargetHolder(TargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method,
+ 'to_list': self.to_list_method,
})
def __repr__(self):
@@ -892,8 +900,17 @@ class CustomTargetHolder(TargetHolder):
def full_path_method(self, args, kwargs):
return self.interpreter.backend.get_target_filename_abs(self.held_object)
+ @FeatureNew('custom_target.to_list', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def to_list_method(self, args, kwargs):
+ result = []
+ for i in self.held_object:
+ result.append(CustomTargetIndexHolder(i, self.interpreter))
+ return result
+
def __getitem__(self, index):
- return CustomTargetIndexHolder(self.held_object[index])
+ return CustomTargetIndexHolder(self.held_object[index], self.interpreter)
def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method]
raise InterpreterException('Cannot set a member of a CustomTarget')
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 2341bd2..da0a60e 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -256,24 +256,39 @@ class PkgConfigModule(ExtensionModule):
prefix = prefix.as_posix()
if isinstance(subdir, PurePath):
subdir = subdir.as_posix()
- if subdir.startswith(prefix):
- subdir = subdir.replace(prefix, '')
+ try:
+ if os.path.commonpath([prefix, subdir]) == prefix:
+ skip = len(prefix) + 1
+ subdir = subdir[skip:]
+ except ValueError:
+ pass
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
+ if uninstalled:
+ outdir = os.path.join(state.environment.build_dir, 'meson-uninstalled')
+ if not os.path.exists(outdir):
+ os.mkdir(outdir)
+ prefix = PurePath(state.environment.get_build_dir())
+ srcdir = PurePath(state.environment.get_source_dir())
+ else:
+ outdir = state.environment.scratch_dir
+ 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'))
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'))
with open(fname, 'w', encoding='utf-8') 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 uninstalled:
+ ofile.write('srcdir={}\n'.format(self._escape(srcdir)))
+ else:
+ ofile.write('libdir={}\n'.format(self._escape('${prefix}' / libdir)))
+ ofile.write('includedir={}\n'.format(self._escape('${prefix}' / incdir)))
if variables:
ofile.write('\n')
for k, v in variables:
@@ -303,17 +318,20 @@ class PkgConfigModule(ExtensionModule):
if isinstance(l, str):
yield l
else:
- install_dir = l.get_custom_install_dir()[0]
+ if uninstalled:
+ install_dir = os.path.dirname(state.backend.get_target_filename_abs(l))
+ else:
+ install_dir = l.get_custom_install_dir()[0]
if install_dir is False:
continue
if 'cs' in l.compilers:
if isinstance(install_dir, str):
- Lflag = '-r${prefix}/%s/%s ' % (self._escape(self._make_relative(prefix, install_dir)), l.filename)
+ Lflag = '-r${prefix}/%s/%s' % (self._escape(self._make_relative(prefix, install_dir)), l.filename)
else: # install_dir is True
Lflag = '-r${libdir}/%s' % l.filename
else:
if isinstance(install_dir, str):
- Lflag = '-L${prefix}/%s ' % self._escape(self._make_relative(prefix, install_dir))
+ Lflag = '-L${prefix}/%s' % self._escape(self._make_relative(prefix, install_dir))
else: # install_dir is True
Lflag = '-L${libdir}'
if Lflag not in Lflags:
@@ -327,22 +345,47 @@ class PkgConfigModule(ExtensionModule):
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 ['${prefix}', '${srcdir}']:
+ 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.54.0', ['uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags'])
@FeatureNewKwargs('pkgconfig.generate', '0.41.0', ['variables'])
@permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase',
@@ -447,6 +490,11 @@ class PkgConfigModule(ExtensionModule):
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)
# 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:'.
diff --git a/run_unittests.py b/run_unittests.py
index cf40955..9acdca9 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1857,8 +1857,14 @@ class AllPlatformTests(BasePlatformTests):
# N.B. We don't check 'libdir' as it's platform dependent, see
# default_libdir():
}
+
+ if mesonbuild.mesonlib.default_prefix() == '/usr/local':
+ expected[None] = expected['/usr/local']
+
for prefix in expected:
- args = ['--prefix', prefix]
+ args = []
+ if prefix:
+ args += ['--prefix', prefix]
self.init(testdir, extra_args=args, default_args=False)
opts = self.introspect('--buildoptions')
for opt in opts:
@@ -5005,6 +5011,21 @@ class LinuxlikeTests(BasePlatformTests):
out = self._run(cmd + ['--libs'], override_envvars=env).strip().split()
self.assertEqual(out, ['-llibmain2', '-llibinternal'])
+ def test_pkgconfig_uninstalled(self):
+ testdir = os.path.join(self.common_test_dir, '47 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, '47 pkgconfig-gen', 'dependencies')
+ self.init(testdir)
+ self.build()
+ self.run_tests()
+
def test_pkg_unfound(self):
testdir = os.path.join(self.unit_test_dir, '23 unfound pkgconfig')
self.init(testdir)
diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build
index 4a2f15a..f40dc52 100644
--- a/test cases/common/14 configure file/meson.build
+++ b/test cases/common/14 configure file/meson.build
@@ -185,6 +185,12 @@ ret = run_command(check_file, inf, outf)
if ret.returncode() != 0
error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
endif
+# Now the same, but using a File object as an argument.
+inf2 = files('invalid-utf8.bin.in')[0]
+ret = run_command(check_file, inf2, outf)
+if ret.returncode() != 0
+ error('Error running command: @0@\n@1@'.format(ret.stdout(), ret.stderr()))
+endif
# Test copy of a binary file
outf = configure_file(input : inf,
diff --git a/test cases/common/144 custom target multiple outputs/meson.build b/test cases/common/144 custom target multiple outputs/meson.build
index 6412864..382c9b2 100644
--- a/test cases/common/144 custom target multiple outputs/meson.build
+++ b/test cases/common/144 custom target multiple outputs/meson.build
@@ -21,8 +21,19 @@ custom_target('only-install-first',
install : true,
install_dir : [join_paths(get_option('prefix'), get_option('includedir')), false])
-custom_target('only-install-second',
+targets = custom_target('only-install-second',
output : ['second.h', 'second.sh'],
command : [gen, 'second', '@OUTDIR@'],
install : true,
install_dir : [false, join_paths(get_option('prefix'), get_option('bindir'))])
+
+paths = []
+foreach i : targets.to_list()
+ paths += i.full_path()
+endforeach
+
+# Skip on Windows because paths are not identical, '/' VS '\'.
+if host_machine.system() != 'windows'
+ assert(paths == [meson.current_build_dir() / 'second.h',
+ meson.current_build_dir() / 'second.sh'])
+endif
diff --git a/test cases/common/47 pkgconfig-gen/dependencies/main.c b/test cases/common/47 pkgconfig-gen/dependencies/main.c
new file mode 100644
index 0000000..61708d3
--- /dev/null
+++ b/test cases/common/47 pkgconfig-gen/dependencies/main.c
@@ -0,0 +1,6 @@
+#include <simple.h>
+
+int main(int argc, char *argv[])
+{
+ return simple_function() == 42 ? 0 : 1;
+}
diff --git a/test cases/common/47 pkgconfig-gen/dependencies/meson.build b/test cases/common/47 pkgconfig-gen/dependencies/meson.build
index 22bcc47..fb4e6b4 100644
--- a/test cases/common/47 pkgconfig-gen/dependencies/meson.build
+++ b/test cases/common/47 pkgconfig-gen/dependencies/meson.build
@@ -18,6 +18,9 @@ threads_dep = dependency('threads')
custom_dep = declare_dependency(link_with : custom_lib, 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
diff --git a/test cases/common/47 pkgconfig-gen/meson.build b/test cases/common/47 pkgconfig-gen/meson.build
index 7e6c670..09c46c5 100644
--- a/test cases/common/47 pkgconfig-gen/meson.build
+++ b/test cases/common/47 pkgconfig-gen/meson.build
@@ -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')