summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2020-04-13 21:17:56 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2020-05-10 19:03:42 +0300
commit3a4a94d28d47bd142a27e53a5a0670fcf1dae5c4 (patch)
tree7ad17c5aa2fad8290734869d29dc962675ecaebb
parent8c144adc7a32f23e63eb85b989739adf476d04b4 (diff)
downloadmeson-gentarget.tar.gz
Add just enough backend to make the simple case work.gentarget
-rw-r--r--mesonbuild/backend/ninjabackend.py34
-rw-r--r--mesonbuild/build.py125
-rw-r--r--mesonbuild/interpreter.py29
-rw-r--r--test cases/common/234 generator target/meson.build14
4 files changed, 157 insertions, 45 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 9b895c908..b79c8eba1 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -483,8 +483,13 @@ int dummy;
def generate_target(self, target):
if isinstance(target, build.CustomTarget):
self.generate_custom_target(target)
+ return
if isinstance(target, build.RunTarget):
self.generate_run_target(target)
+ return
+ if isinstance(target, build.GeneratorTarget):
+ self.generate_generatortarget_target(target)
+ return
name = target.get_id()
if name in self.processed_targets:
return
@@ -705,6 +710,35 @@ int dummy;
self.add_build(elem)
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):
if target.subproject != '':
subproject_prefix = '{}@@'.format(target.subproject)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 2b4b1b980..4fb977e7c 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1413,8 +1413,15 @@ class Generator:
relpath = pathlib.PurePath(trial).relative_to(parent)
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):
- output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [])
+ def process_files(self, name, files, state, *,
+ 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:
if isinstance(f, str):
f = File.from_source_file(state.environment.source_dir, state.subdir, f)
@@ -1429,7 +1436,7 @@ class Generator:
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.name = self.generator.exe
self.subdir = subdir
@@ -1448,6 +1455,11 @@ class GeneratedList:
# 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))
+ # 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):
result = []
@@ -1993,8 +2005,41 @@ class SharedModule(SharedLibrary):
def get_default_install_dir(self, environment):
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([
'input',
'output',
@@ -2016,11 +2061,8 @@ class CustomTarget(Target):
def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False, backend=None):
self.typename = 'custom'
# TODO expose keyword arg to make MachineChoice.HOST configurable
- super().__init__(name, subdir, subproject, False, MachineChoice.HOST)
- self.dependencies = []
- self.extra_depends = []
- self.depend_files = [] # Files that this target depends on but are not on the command line.
- self.depfile = None
+ Target.__init__(self, name, subdir, subproject, False, MachineChoice.HOST)
+ CustomMixin.__init__(self)
self.process_kwargs(kwargs, backend)
self.extra_files = []
# 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())
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):
self.process_kwargs_base(kwargs)
self.sources = unholder(extract_as_list(kwargs, 'input'))
@@ -2261,6 +2276,44 @@ class CustomTarget(Target):
for i in self.outputs:
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):
def __init__(self, name, command, args, dependencies, subdir, subproject):
self.typename = 'run'
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index a3e9dee6a..cce47fe39 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -638,8 +638,11 @@ class GeneratorHolder(InterpreterObject, ObjectHolder):
raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
else:
preserve_path_from = None
- gl = self.held_object.process_files('Generator', args, self.interpreter,
- preserve_path_from, extra_args=extras)
+ gl = self.held_object.process_files('Generator',
+ args,
+ self.interpreter,
+ preserve_path_from=preserve_path_from,
+ extra_args=extras)
return GeneratedListHolder(gl)
@@ -953,6 +956,10 @@ class CustomTargetHolder(TargetHolder):
return IncludeDirsHolder(build.IncludeDirs('', [], False,
[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):
def __init__(self, target, interp):
super().__init__(target, interp)
@@ -2265,6 +2272,9 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'depfile',
'capture',
'preserve_path_from'},
+ 'generator_target': {'generator',
+ 'output',
+ 'sources'},
'include_directories': {'is_system'},
'install_data': {'install_dir', 'install_mode', 'rename', 'sources'},
'install_headers': {'install_dir', 'install_mode', 'subdir'},
@@ -2382,6 +2392,7 @@ class Interpreter(InterpreterBase):
'error': self.func_error,
'executable': self.func_executable,
'generator': self.func_generator,
+ 'generator_target': self.func_generator_target,
'gettext': self.func_gettext,
'get_option': self.func_get_option,
'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)
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.52.0', ['priority'])
@permittedKwargs(permitted_kwargs['benchmark'])
diff --git a/test cases/common/234 generator target/meson.build b/test cases/common/234 generator target/meson.build
index 02f84d7a4..2cde4dcd8 100644
--- a/test cases/common/234 generator target/meson.build
+++ b/test cases/common/234 generator target/meson.build
@@ -1,11 +1,11 @@
project('generator target', 'c')
subdir('gen1')
-subdir('gen2')
-subdir('genzip')
-subdir('gensrc')
-subdir('prog')
+#subdir('gen2')
+#subdir('genzip')
+#subdir('gensrc')
+#subdir('prog')
-add_test('validate_zip',
- find_program('validate_zip.py'),
- args: [zip_output])
+#add_test('validate_zip',
+# find_program('validate_zip.py'),
+# args: [zip_output])