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
This commit is contained in:
parent
dfc2b75ee2
commit
dda5e8cadb
|
@ -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…
Reference in New Issue