Allow CustomTarget's to be indexed

This allows a CustomTarget to be indexed, and the resulting indexed
value (a CustomTargetIndex type), to be used as a source in other
targets. This will confer a dependency on the original target, but only
inserts the source file returning by index the original target's
outputs. This can allow a CustomTarget that creates both a header and a
code file to have it's outputs split, for example.

Fixes #1470
pull/2386/head
Dylan Baker 8 years ago committed by Jussi Pakkanen
parent dfc2b75ee2
commit dda5e8cadb
  1. 5
      docs/markdown/Reference-manual.md
  2. 21
      docs/markdown/snippets/custom-target-index.md
  3. 2
      mesonbuild/backend/backends.py
  4. 11
      mesonbuild/backend/ninjabackend.py
  5. 4
      mesonbuild/backend/vs2010backend.py
  6. 34
      mesonbuild/build.py
  7. 16
      mesonbuild/interpreter.py
  8. 10
      mesonbuild/interpreterbase.py
  9. 9
      mesonbuild/modules/gnome.py
  10. 49
      test cases/common/161 index customtarget/gen_sources.py
  11. 20
      test cases/common/161 index customtarget/lib.c
  12. 32
      test cases/common/161 index customtarget/meson.build
  13. 22
      test cases/common/161 index customtarget/subdir/foo.c
  14. 19
      test cases/common/161 index customtarget/subdir/meson.build
  15. 24
      test cases/failing/60 assign custom target index/meson.build

@ -1567,6 +1567,11 @@ contains a target with the following methods:
this and will also allow Meson to setup inter-target dependencies
correctly. Please file a bug if that doesn't work for you.
- `[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 will make that
target depend on this custom target, but the only source added will be the
one that corresponds to the index of the custom target's output argument.
### `dependency` object
This object is returned by [`dependency()`](#dependency) and contains

@ -0,0 +1,21 @@
# Can index CustomTaget objects
The `CustomTarget` object can now be indexed like an array. The resulting
object can be used as a source file for other Targets, this will create a
dependency on the original `CustomTarget`, but will only insert the generated
file corresponding to the index value of the `CustomTarget`'s `output` keyword.
c = CustomTarget(
...
output : ['out.h', 'out.c'],
)
lib1 = static_library(
'lib1',
[lib1_sources, c[0]],
...
)
exec = executable(
'executable',
c[1],
link_with : lib1,
)

@ -174,7 +174,7 @@ class Backend:
Returns the full path of the generated source relative to the build root
"""
# CustomTarget generators output to the build dir of the CustomTarget
if isinstance(gensrc, build.CustomTarget):
if isinstance(gensrc, (build.CustomTarget, build.CustomTargetIndex)):
return os.path.join(self.get_target_dir(gensrc), src)
# GeneratedList generators output to the private build directory of the
# target that the GeneratedList is used in

@ -245,7 +245,7 @@ int dummy;
header_deps = []
# XXX: Why don't we add deps to CustomTarget headers here?
for genlist in target.get_generated_sources():
if isinstance(genlist, build.CustomTarget):
if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
continue
for src in genlist.get_outputs():
if self.environment.is_header(src):
@ -1761,10 +1761,11 @@ rule FORTRAN_DEP_HACK
outfile.write('\n')
def generate_generator_list_rules(self, target, outfile):
# CustomTargets have already written their rules,
# so write rules for GeneratedLists here
# CustomTargets have already written their rules and
# CustomTargetIndexes don't actually get generated, so write rules for
# GeneratedLists here
for genlist in target.get_generated_sources():
if isinstance(genlist, build.CustomTarget):
if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
continue
self.generate_genlist_for_target(genlist, target, outfile)
@ -2013,7 +2014,7 @@ rule FORTRAN_DEP_HACK
# Generator output goes into the target private dir which is
# already in the include paths list. Only custom targets have their
# own target build dir.
if not isinstance(i, build.CustomTarget):
if not isinstance(i, (build.CustomTarget, build.CustomTargetIndex)):
continue
idir = self.get_target_dir(i)
if idir not in custom_target_include_dirs:

@ -91,7 +91,7 @@ class Vs2010Backend(backends.Backend):
source_target_dir = self.get_target_source_dir(target)
down = self.target_to_build_root(target)
for genlist in target.get_generated_sources():
if isinstance(genlist, build.CustomTarget):
if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
for i in genlist.get_outputs():
# Path to the generated source from the current vcxproj dir via the build root
ipath = os.path.join(down, self.get_target_dir(genlist), i)
@ -201,6 +201,8 @@ class Vs2010Backend(backends.Backend):
for gendep in target.get_generated_sources():
if isinstance(gendep, build.CustomTarget):
all_deps[gendep.get_id()] = gendep
elif isinstance(gendep, build.CustomTargetIndex):
all_deps[gendep.target.get_id()] = gendep.target
else:
gen_exe = gendep.generator.get_exe()
if isinstance(gen_exe, build.Executable):

@ -425,7 +425,7 @@ class BuildTarget(Target):
if s not in added_sources:
self.sources.append(s)
added_sources[s] = True
elif isinstance(s, (GeneratedList, CustomTarget)):
elif isinstance(s, (GeneratedList, CustomTarget, CustomTargetIndex)):
self.generated.append(s)
else:
msg = 'Bad source of type {!r} in target {!r}.'.format(type(s).__name__, self.name)
@ -1676,6 +1676,15 @@ class CustomTarget(Target):
def type_suffix(self):
return "@cus"
def __getitem__(self, index):
return CustomTargetIndex(self, self.outputs[index])
def __setitem__(self, index, value):
raise NotImplementedError
def __delitem__(self, index):
raise NotImplementedError
class RunTarget(Target):
def __init__(self, name, command, args, dependencies, subdir):
super().__init__(name, subdir, False)
@ -1735,6 +1744,29 @@ class Jar(BuildTarget):
pass
class CustomTargetIndex:
"""A special opaque object returned by indexing a CustomTaget. This object
exists in meson, but acts as a proxy in the backends, making targets depend
on the CustomTarget it's derived from, but only adding one source file to
the sources.
"""
def __init__(self, target, output):
self.target = target
self.output = output
def __repr__(self):
return '<CustomTargetIndex: {!r}[{}]>'.format(
self.target, self.target.output.index(self.output))
def get_outputs(self):
return [self.output]
def get_subdir(self):
return self.target.get_subdir()
class ConfigureFile:
def __init__(self, subdir, sourcename, targetname, configuration_data):

@ -566,6 +566,11 @@ class JarHolder(BuildTargetHolder):
def __init__(self, target, interp):
super().__init__(target, interp)
class CustomTargetIndexHolder(InterpreterObject):
def __init__(self, object_to_hold):
super().__init__()
self.held_object = object_to_hold
class CustomTargetHolder(TargetHolder):
def __init__(self, object_to_hold, interp):
super().__init__()
@ -582,6 +587,15 @@ class CustomTargetHolder(TargetHolder):
def full_path_method(self, args, kwargs):
return self.interpreter.backend.get_target_filename_abs(self.held_object)
def __getitem__(self, index):
return CustomTargetIndexHolder(self.held_object[index])
def __setitem__(self, index, value):
raise InterpreterException('Cannot set a member of a CustomTarget')
def __delitem__(self, index):
raise InterpreterException('Cannot delete a member of a CustomTarget')
class RunTargetHolder(InterpreterObject):
def __init__(self, name, command, args, dependencies, subdir):
super().__init__()
@ -2774,7 +2788,7 @@ different subdirectory.
results = []
for s in sources:
if isinstance(s, (mesonlib.File, GeneratedListHolder,
CustomTargetHolder)):
CustomTargetHolder, CustomTargetIndexHolder)):
pass
elif isinstance(s, str):
s = mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)

@ -369,14 +369,16 @@ class InterpreterBase:
def evaluate_indexing(self, node):
assert(isinstance(node, mparser.IndexNode))
iobject = self.evaluate_statement(node.iobject)
if not isinstance(iobject, list):
raise InterpreterException('Tried to index a non-array object.')
if not hasattr(iobject, '__getitem__'):
raise InterpreterException(
'Tried to index an object that doesn\'t support indexing.')
index = self.evaluate_statement(node.index)
if not isinstance(index, int):
raise InterpreterException('Index value is not an integer.')
if index < -len(iobject) or index >= len(iobject):
try:
return iobject[index]
except IndexError:
raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
return iobject[index]
def function_call(self, node):
func_name = node.func_name

@ -107,12 +107,12 @@ class GnomeModule(ExtensionModule):
for (ii, dep) in enumerate(dependencies):
if hasattr(dep, 'held_object'):
dependencies[ii] = dep = dep.held_object
if not isinstance(dep, (mesonlib.File, build.CustomTarget)):
if not isinstance(dep, (mesonlib.File, build.CustomTarget, build.CustomTargetIndex)):
m = 'Unexpected dependency type {!r} for gnome.compile_resources() ' \
'"dependencies" argument.\nPlease pass the return value of ' \
'custom_target() or configure_file()'
raise MesonException(m.format(dep))
if isinstance(dep, build.CustomTarget):
if isinstance(dep, (build.CustomTarget, build.CustomTargetIndex)):
if not mesonlib.version_compare(glib_version, gresource_dep_needed_version):
m = 'The "dependencies" argument of gnome.compile_resources() can not\n' \
'be used with the current version of glib-compile-resources due to\n' \
@ -131,6 +131,7 @@ class GnomeModule(ExtensionModule):
elif isinstance(ifile, str):
ifile = os.path.join(state.subdir, ifile)
elif isinstance(ifile, (interpreter.CustomTargetHolder,
interpreter.CustomTargetIndexHolder,
interpreter.GeneratedObjectsHolder)):
m = 'Resource xml files generated at build-time cannot be used ' \
'with gnome.compile_resources() because we need to scan ' \
@ -261,7 +262,7 @@ class GnomeModule(ExtensionModule):
dep_files.append(dep)
subdirs.append(dep.subdir)
break
elif isinstance(dep, build.CustomTarget):
elif isinstance(dep, (build.CustomTarget, build.CustomTargetIndex)):
fname = None
outputs = {(o, os.path.basename(o)) for o in dep.get_outputs()}
for o, baseo in outputs:
@ -443,7 +444,7 @@ class GnomeModule(ExtensionModule):
for s in libsources:
if hasattr(s, 'held_object'):
s = s.held_object
if isinstance(s, build.CustomTarget):
if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)):
gir_filelist.write(os.path.join(state.environment.get_build_dir(),
state.backend.get_target_dir(s),
s.get_outputs()[0]) + '\n')

@ -0,0 +1,49 @@
# Copyright © 2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import textwrap
HEADER = textwrap.dedent('''\
void stringify(int foo, char * buffer);
''')
CODE = textwrap.dedent('''\
#include <stdio.h>
#ifndef WORKS
# error "This shouldn't have been included"
#endif
void stringify(int foo, char * buffer) {
sprintf(buffer, "%i", foo);
}
''')
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--header')
parser.add_argument('--code')
args = parser.parse_args()
with open(args.header, 'w') as f:
f.write(HEADER)
with open(args.code, 'w') as f:
f.write(CODE)
if __name__ == '__main__':
main()

@ -0,0 +1,20 @@
/* Copyright © 2017 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gen.h"
void func(char * buffer) {
stringify(1, buffer);
}

@ -0,0 +1,32 @@
# Copyright © 2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
project('custom_target_index', 'c', default_options : 'c_std=c89')
py_mod = import('python3')
prog_python = py_mod.find_python()
gen = custom_target(
'gen.[ch]',
input : 'gen_sources.py',
output : ['gen.c', 'gen.h'],
command : [prog_python, '@INPUT@', '--header', '@OUTPUT1@', '--code', '@OUTPUT0@'],
)
lib = static_library(
'libfoo',
['lib.c', gen[1]],
)
subdir('subdir')

@ -0,0 +1,22 @@
/* Copyright © 2017 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gen.h"
int main(void) {
char buf[50];
stringify(10, buf);
return 0;
}

@ -0,0 +1,19 @@
# Copyright © 2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
foo = executable(
'foo',
['foo.c', gen[0], gen[1]],
c_args : '-DWORKS',
)

@ -0,0 +1,24 @@
# Copyright © 2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
prog_python = import('python3').find_python()
target = custom_target(
'target',
output : ['1', '2'],
command : [prog_python, '-c',
'with open("1", "w") as f: f.write("foo"); with open("2", "w") as f: f.write("foo")'],
)
target[0] = 'foo'
Loading…
Cancel
Save