Add just enough backend to make the simple case work.

gentarget
Jussi Pakkanen 5 years ago
parent 8c144adc7a
commit 3a4a94d28d
  1. 34
      mesonbuild/backend/ninjabackend.py
  2. 125
      mesonbuild/build.py
  3. 29
      mesonbuild/interpreter.py
  4. 14
      test cases/common/234 generator target/meson.build

@ -483,8 +483,13 @@ int dummy;
def generate_target(self, target): def generate_target(self, target):
if isinstance(target, build.CustomTarget): if isinstance(target, build.CustomTarget):
self.generate_custom_target(target) self.generate_custom_target(target)
return
if isinstance(target, build.RunTarget): if isinstance(target, build.RunTarget):
self.generate_run_target(target) self.generate_run_target(target)
return
if isinstance(target, build.GeneratorTarget):
self.generate_generatortarget_target(target)
return
name = target.get_id() name = target.get_id()
if name in self.processed_targets: if name in self.processed_targets:
return return
@ -705,6 +710,35 @@ int dummy;
self.add_build(elem) self.add_build(elem)
self.processed_targets[target.get_id()] = True self.processed_targets[target.get_id()] = True
def generate_generatortarget_target(self, target):
'''Brought to you by the department of redundancy department.'''
self.processed_targets[target.get_id()] = True
assert(target.output.owning_gentarget is target)
genlist = target.output
generator = target.generator
assert(generator is genlist.get_generator())
# FIXME. start simple, add multiple outputs later.
assert(len(genlist.get_outputs()) == len(genlist.get_inputs()))
# A generatortarget is special compared to other target types.
# Its output goes to a named subdirectory in the build dir
# that can contain an arbitrary number of files and subdirs.
output_subdir = os.path.join(target.subdir, target.name)
for input in genlist.get_inputs():
ifile_str = input.rel_to_builddir(self.build_to_src)
cmd = generator.get_exe().get_command() + generator.get_arglist(ifile_str)
outputs = os.path.join(output_subdir, genlist.get_outputs_for(input)[0])
e = NinjaBuildElement(self.all_outputs, outputs, 'CUSTOM_COMMAND', ifile_str)
# HACK, just to get something working.
cmd2 = []
for c in cmd:
if c == '@INPUT@':
c = ifile_str
if c == '@OUTPUT@':
c = outputs
cmd2.append(c)
e.add_item('COMMAND', cmd2)
self.add_build(e)
def build_run_target_name(self, target): def build_run_target_name(self, target):
if target.subproject != '': if target.subproject != '':
subproject_prefix = '{}@@'.format(target.subproject) subproject_prefix = '{}@@'.format(target.subproject)

@ -1413,8 +1413,15 @@ class Generator:
relpath = pathlib.PurePath(trial).relative_to(parent) relpath = pathlib.PurePath(trial).relative_to(parent)
return relpath.parts[0] != '..' # For subdirs we can only go "down". return relpath.parts[0] != '..' # For subdirs we can only go "down".
def process_files(self, name, files, state, preserve_path_from=None, extra_args=None): def process_files(self, name, files, state, *,
output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else []) preserve_path_from=None,
extra_args=None,
owning_gentarget=None):
output = GeneratedList(self,
state.subdir,
preserve_path_from=preserve_path_from,
extra_args=extra_args if extra_args is not None else [],
owning_gentarget=owning_gentarget)
for f in files: for f in files:
if isinstance(f, str): if isinstance(f, str):
f = File.from_source_file(state.environment.source_dir, state.subdir, f) f = File.from_source_file(state.environment.source_dir, state.subdir, f)
@ -1429,7 +1436,7 @@ class Generator:
class GeneratedList: class GeneratedList:
def __init__(self, generator, subdir, preserve_path_from=None, extra_args=None): def __init__(self, generator, subdir, *, preserve_path_from=None, extra_args=None, owning_gentarget=None):
self.generator = unholder(generator) self.generator = unholder(generator)
self.name = self.generator.exe self.name = self.generator.exe
self.subdir = subdir self.subdir = subdir
@ -1448,6 +1455,11 @@ class GeneratedList:
# Can only add a dependency on an external program which we # Can only add a dependency on an external program which we
# know the absolute path of # know the absolute path of
self.depend_files.append(File.from_absolute_file(path)) self.depend_files.append(File.from_absolute_file(path))
# If None, this generated list is of the old freestanding type.
# Its output goes in the target private directory. Otherwise it is
# the outcome of a generator_target and the output goes in its
# output dir.
self.owning_gentarget = owning_gentarget
def add_preserved_path_segment(self, infile, outfiles, state): def add_preserved_path_segment(self, infile, outfiles, state):
result = [] result = []
@ -1993,8 +2005,41 @@ class SharedModule(SharedLibrary):
def get_default_install_dir(self, environment): def get_default_install_dir(self, environment):
return environment.get_shared_module_dir() return environment.get_shared_module_dir()
class CustomMixin:
def __init__(self, *args, **kwargs):
self.dependencies = []
self.extra_depends = []
self.depend_files = [] # Files that this target depends on but are not on the command line.
self.depfile = None
class CustomTarget(Target): def flatten_command(self, cmd):
cmd = unholder(listify(cmd))
final_cmd = []
for c in cmd:
if isinstance(c, str):
final_cmd.append(c)
elif isinstance(c, File):
self.depend_files.append(c)
final_cmd.append(c)
elif isinstance(c, dependencies.ExternalProgram):
if not c.found():
raise InvalidArguments('Tried to use not-found external program in "command"')
path = c.get_path()
if os.path.isabs(path):
# Can only add a dependency on an external program which we
# know the absolute path of
self.depend_files.append(File.from_absolute_file(path))
final_cmd += c.get_command()
elif isinstance(c, (BuildTarget, CustomTarget)):
self.dependencies.append(c)
final_cmd.append(c)
elif isinstance(c, list):
final_cmd += self.flatten_command(c)
else:
raise InvalidArguments('Argument {!r} in "command" is invalid'.format(c))
return final_cmd
class CustomTarget(Target, CustomMixin):
known_kwargs = set([ known_kwargs = set([
'input', 'input',
'output', 'output',
@ -2016,11 +2061,8 @@ class CustomTarget(Target):
def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False, backend=None): def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False, backend=None):
self.typename = 'custom' self.typename = 'custom'
# TODO expose keyword arg to make MachineChoice.HOST configurable # TODO expose keyword arg to make MachineChoice.HOST configurable
super().__init__(name, subdir, subproject, False, MachineChoice.HOST) Target.__init__(self, name, subdir, subproject, False, MachineChoice.HOST)
self.dependencies = [] CustomMixin.__init__(self)
self.extra_depends = []
self.depend_files = [] # Files that this target depends on but are not on the command line.
self.depfile = None
self.process_kwargs(kwargs, backend) self.process_kwargs(kwargs, backend)
self.extra_files = [] self.extra_files = []
# Whether to use absolute paths for all files on the commandline # Whether to use absolute paths for all files on the commandline
@ -2065,33 +2107,6 @@ class CustomTarget(Target):
bdeps.update(d.get_transitive_build_target_deps()) bdeps.update(d.get_transitive_build_target_deps())
return bdeps return bdeps
def flatten_command(self, cmd):
cmd = unholder(listify(cmd))
final_cmd = []
for c in cmd:
if isinstance(c, str):
final_cmd.append(c)
elif isinstance(c, File):
self.depend_files.append(c)
final_cmd.append(c)
elif isinstance(c, dependencies.ExternalProgram):
if not c.found():
raise InvalidArguments('Tried to use not-found external program in "command"')
path = c.get_path()
if os.path.isabs(path):
# Can only add a dependency on an external program which we
# know the absolute path of
self.depend_files.append(File.from_absolute_file(path))
final_cmd += c.get_command()
elif isinstance(c, (BuildTarget, CustomTarget)):
self.dependencies.append(c)
final_cmd.append(c)
elif isinstance(c, list):
final_cmd += self.flatten_command(c)
else:
raise InvalidArguments('Argument {!r} in "command" is invalid'.format(c))
return final_cmd
def process_kwargs(self, kwargs, backend): def process_kwargs(self, kwargs, backend):
self.process_kwargs_base(kwargs) self.process_kwargs_base(kwargs)
self.sources = unholder(extract_as_list(kwargs, 'input')) self.sources = unholder(extract_as_list(kwargs, 'input'))
@ -2261,6 +2276,44 @@ class CustomTarget(Target):
for i in self.outputs: for i in self.outputs:
yield CustomTargetIndex(self, i) yield CustomTargetIndex(self, i)
class GeneratorTarget(Target, CustomMixin):
def __init__(self, name, state, kwargs, backend=None):
self.typename = 'gent'
Target.__init__(self, name, state.subdir, state.subproject, False, MachineChoice.HOST)
CustomMixin.__init__(self)
self.sources = unholder(extract_as_list(kwargs, 'input'))
self.process_kwargs(kwargs, backend)
self.extra_files = []
self.output = self.generator.process_files('GeneratorTarget',
self.sources,
state,
owning_gentarget=self)
assert(isinstance(self.output, GeneratedList))
def __repr__(self):
repr_str = "<{0} {1}: {2}>"
return repr_str.format(self.__class__.__name__, self.get_id(), self.command)
def type_suffix(self):
return "@gta"
def get_dependencies(self):
return self.dependencies
def process_kwargs(self, kwargs, backend):
if 'generator' not in kwargs:
raise InvalidArguments('Missing keyword argument "command".')
self.generator = unholder(kwargs['generator'])
if not isinstance(self.generator, Generator):
raise InvalidArguments("Generator argument is not a generator object.")
def should_install(self):
return False
def get_outputs(self):
return []
class RunTarget(Target): class RunTarget(Target):
def __init__(self, name, command, args, dependencies, subdir, subproject): def __init__(self, name, command, args, dependencies, subdir, subproject):
self.typename = 'run' self.typename = 'run'

@ -638,8 +638,11 @@ class GeneratorHolder(InterpreterObject, ObjectHolder):
raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.') raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
else: else:
preserve_path_from = None preserve_path_from = None
gl = self.held_object.process_files('Generator', args, self.interpreter, gl = self.held_object.process_files('Generator',
preserve_path_from, extra_args=extras) args,
self.interpreter,
preserve_path_from=preserve_path_from,
extra_args=extras)
return GeneratedListHolder(gl) return GeneratedListHolder(gl)
@ -953,6 +956,10 @@ class CustomTargetHolder(TargetHolder):
return IncludeDirsHolder(build.IncludeDirs('', [], False, return IncludeDirsHolder(build.IncludeDirs('', [], False,
[os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))])) [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))]))
class GeneratorTargetHolder(TargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
class RunTargetHolder(TargetHolder): class RunTargetHolder(TargetHolder):
def __init__(self, target, interp): def __init__(self, target, interp):
super().__init__(target, interp) super().__init__(target, interp)
@ -2265,6 +2272,9 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'depfile', 'depfile',
'capture', 'capture',
'preserve_path_from'}, 'preserve_path_from'},
'generator_target': {'generator',
'output',
'sources'},
'include_directories': {'is_system'}, 'include_directories': {'is_system'},
'install_data': {'install_dir', 'install_mode', 'rename', 'sources'}, 'install_data': {'install_dir', 'install_mode', 'rename', 'sources'},
'install_headers': {'install_dir', 'install_mode', 'subdir'}, 'install_headers': {'install_dir', 'install_mode', 'subdir'},
@ -2382,6 +2392,7 @@ class Interpreter(InterpreterBase):
'error': self.func_error, 'error': self.func_error,
'executable': self.func_executable, 'executable': self.func_executable,
'generator': self.func_generator, 'generator': self.func_generator,
'generator_target': self.func_generator_target,
'gettext': self.func_gettext, 'gettext': self.func_gettext,
'get_option': self.func_get_option, 'get_option': self.func_get_option,
'get_variable': self.func_get_variable, 'get_variable': self.func_get_variable,
@ -3809,6 +3820,20 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
self.generators.append(gen) self.generators.append(gen)
return gen return gen
@stringArgs
@permittedKwargs(permitted_kwargs['generator_target'])
def func_generator_target(self, node, args, kwargs):
if len(args) != 1:
raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name')
name = args[0]
gt = GeneratorTargetHolder(build.GeneratorTarget(name,
self,
kwargs),
self)
self.add_target(name, gt.held_object)
return gt
@FeatureNewKwargs('benchmark', '0.46.0', ['depends']) @FeatureNewKwargs('benchmark', '0.46.0', ['depends'])
@FeatureNewKwargs('benchmark', '0.52.0', ['priority']) @FeatureNewKwargs('benchmark', '0.52.0', ['priority'])
@permittedKwargs(permitted_kwargs['benchmark']) @permittedKwargs(permitted_kwargs['benchmark'])

@ -1,11 +1,11 @@
project('generator target', 'c') project('generator target', 'c')
subdir('gen1') subdir('gen1')
subdir('gen2') #subdir('gen2')
subdir('genzip') #subdir('genzip')
subdir('gensrc') #subdir('gensrc')
subdir('prog') #subdir('prog')
add_test('validate_zip', #add_test('validate_zip',
find_program('validate_zip.py'), # find_program('validate_zip.py'),
args: [zip_output]) # args: [zip_output])

Loading…
Cancel
Save