diff options
-rw-r--r-- | ci/azure-steps.yml | 4 | ||||
-rw-r--r-- | docs/markdown/Pkgconfig-module.md | 11 | ||||
-rw-r--r-- | docs/markdown/Reference-manual.md | 6 | ||||
-rw-r--r-- | docs/markdown/snippets/uninstalled-pkgconfig.md | 8 | ||||
-rw-r--r-- | mesonbuild/build.py | 4 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 6 | ||||
-rw-r--r-- | mesonbuild/environment.py | 2 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 27 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 86 | ||||
-rwxr-xr-x | run_unittests.py | 23 | ||||
-rw-r--r-- | test cases/common/14 configure file/meson.build | 6 | ||||
-rw-r--r-- | test cases/common/144 custom target multiple outputs/meson.build | 13 | ||||
-rw-r--r-- | test cases/common/47 pkgconfig-gen/dependencies/main.c | 6 | ||||
-rw-r--r-- | test cases/common/47 pkgconfig-gen/dependencies/meson.build | 3 | ||||
-rw-r--r-- | test cases/common/47 pkgconfig-gen/meson.build | 1 |
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') |