Allow override_find_program to use an executable.

With this it is now possible to do

foobar = executable('foobar', ...)
meson.override_find_program('foobar', foobar)

Which is convenient for a project like protobuf which produces both a
dependency and a tool. If protobuf is updated to use
override_find_program, it can be used as

protobuf_dep = dependency('protobuf', version : '>=3.3.1',
                          fallback : ['protobuf', 'protobuf_dep'])
protoc_prog = find_program('protoc')
pull/3873/head
Rafael Ávila de Espíndola 6 years ago committed by Jussi Pakkanen
parent 862019e6de
commit 07d2d88fa9
  1. 4
      docs/markdown/Reference-manual.md
  2. 8
      docs/markdown/snippets/overrideexe.md
  3. 4
      mesonbuild/build.py
  4. 4
      mesonbuild/dependencies/base.py
  5. 14
      mesonbuild/interpreter.py
  6. 11
      run_unittests.py
  7. 0
      test cases/common/206 override with exe/main2.input
  8. 15
      test cases/common/206 override with exe/meson.build
  9. 12
      test cases/common/206 override with exe/subprojects/sub/foobar.c
  10. 3
      test cases/common/206 override with exe/subprojects/sub/meson.build
  11. 3
      test cases/failing/81 override exe config/foo.c
  12. 6
      test cases/failing/81 override exe config/meson.build

@ -1529,7 +1529,9 @@ the following methods.
specifies that whenever `find_program` is used to find a program specifies that whenever `find_program` is used to find a program
named `progname`, Meson should not not look it up on the system but named `progname`, Meson should not not look it up on the system but
instead return `program`, which may either be the result of instead return `program`, which may either be the result of
`find_program` or `configure_file`. `find_program`, `configure_file` or `executable`.
If `program` is an `executable`, it cannot be used during configure.
- `project_version()` returns the version string specified in - `project_version()` returns the version string specified in
`project` function call. `project` function call.

@ -0,0 +1,8 @@
## More flexible `override_find_program()`.
It is now possible to pass an `executable` to
`override_find_program()` if the overridden program is not used during
configure.
This is particularly useful for fallback dependencies like Protobuf
that also provide a tool like protoc.

@ -1314,6 +1314,10 @@ class Executable(BuildTarget):
# Only linkwithable if using export_dynamic # Only linkwithable if using export_dynamic
self.is_linkwithable = self.export_dynamic self.is_linkwithable = self.export_dynamic
def description(self):
'''Human friendly description of the executable'''
return self.name
def type_suffix(self): def type_suffix(self):
return "@exe" return "@exe"

@ -1057,6 +1057,10 @@ class ExternalProgram:
r = '<{} {!r} -> {!r}>' r = '<{} {!r} -> {!r}>'
return r.format(self.__class__.__name__, self.name, self.command) return r.format(self.__class__.__name__, self.name, self.command)
def description(self):
'''Human friendly description of the command'''
return ' '.join(self.command)
@staticmethod @staticmethod
def from_cross_info(cross_info, name): def from_cross_info(cross_info, name):
if name not in cross_info.config['binaries']: if name not in cross_info.config['binaries']:

@ -480,7 +480,7 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder):
return self.held_object.get_path() return self.held_object.get_path()
def found(self): def found(self):
return self.held_object.found() return isinstance(self.held_object, build.Executable) or self.held_object.found()
def get_command(self): def get_command(self):
return self.held_object.get_command() return self.held_object.get_command()
@ -1780,9 +1780,8 @@ class MesonMain(InterpreterObject):
if not os.path.exists(abspath): if not os.path.exists(abspath):
raise InterpreterException('Tried to override %s with a file that does not exist.' % name) raise InterpreterException('Tried to override %s with a file that does not exist.' % name)
exe = dependencies.ExternalProgram(abspath) exe = dependencies.ExternalProgram(abspath)
if not isinstance(exe, dependencies.ExternalProgram): if not isinstance(exe, (dependencies.ExternalProgram, build.Executable)):
# FIXME, make this work if the exe is an Executable target. raise InterpreterException('Second argument must be an external program or executable.')
raise InterpreterException('Second argument must be an external program.')
self.interpreter.add_find_program_override(name, exe) self.interpreter.add_find_program_override(name, exe)
@noPosargs @noPosargs
@ -2184,6 +2183,11 @@ external dependencies (including libraries) must go to "dependencies".''')
'or configure_file(), or a compiler object; not {!r}' 'or configure_file(), or a compiler object; not {!r}'
if isinstance(cmd, ExternalProgramHolder): if isinstance(cmd, ExternalProgramHolder):
cmd = cmd.held_object cmd = cmd.held_object
if isinstance(cmd, build.Executable):
progname = node.args.arguments[0].value
msg = 'Program {!r} was overridden with the compiled executable {!r}'\
' and therefore cannot be used during configuration'
raise InterpreterException(msg.format(progname, cmd.description()))
elif isinstance(cmd, CompilerHolder): elif isinstance(cmd, CompilerHolder):
cmd = cmd.compiler.get_exelist()[0] cmd = cmd.compiler.get_exelist()[0]
prog = ExternalProgram(cmd, silent=True) prog = ExternalProgram(cmd, silent=True)
@ -2758,7 +2762,7 @@ external dependencies (including libraries) must go to "dependencies".''')
exe = self.build.find_overrides[name] exe = self.build.find_overrides[name]
if not silent: if not silent:
mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'),
'(overridden: %s)' % ' '.join(exe.command)) '(overridden: %s)' % exe.description())
return ExternalProgramHolder(exe) return ExternalProgramHolder(exe)
return None return None

@ -3881,6 +3881,17 @@ endian = 'little'
return return
raise RuntimeError('Could not find the rpath') raise RuntimeError('Could not find the rpath')
def test_override_with_exe_dep(self):
'''
Test that we produce the correct dependencies when a program is overridden with an executable.
'''
testdir = os.path.join(self.common_test_dir, '206 override with exe')
self.init(testdir)
with open(os.path.join(self.builddir, 'build.ninja')) as bfile:
for line in bfile:
if 'main1.c:' in line or 'main2.c:' in line:
self.assertIn('| subprojects/sub/foobar', line)
@skipIfNoPkgconfig @skipIfNoPkgconfig
def test_usage_external_library(self): def test_usage_external_library(self):
''' '''

@ -0,0 +1,15 @@
project('myexe', 'c')
sub = subproject('sub')
prog = find_program('foobar')
custom1 = custom_target('custom1',
build_by_default : true,
input : [],
output : 'main1.c',
command : [prog, '@OUTPUT@'])
gen = generator(prog,
output : '@BASENAME@.c',
arguments : ['@OUTPUT@'])
custom2 = gen.process('main2.input')
executable('e1', custom1)
executable('e2', custom2)

@ -0,0 +1,12 @@
#include <assert.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
FILE *f = fopen(argv[1], "w");
const char msg[] = "int main(void) {return 0;}\n";
size_t w = fwrite(msg, 1, sizeof(msg) - 1, f);
assert(w == sizeof(msg) - 1);
int r = fclose(f);
assert(r == 0);
return 0;
}

@ -0,0 +1,3 @@
project('sub', 'c')
foobar = executable('foobar', 'foobar.c', native : true)
meson.override_find_program('foobar', foobar)

@ -0,0 +1,3 @@
int main(void) {
return 0;
}

@ -0,0 +1,6 @@
project('myexe', 'c')
foo = executable('foo', 'foo.c')
meson.override_find_program('bar', foo)
bar = find_program('bar')
run_command(bar)
Loading…
Cancel
Save