Skip to content

Commit 68ab8d1

Browse files
committed
interpreter: materialize Files from generated sources
Instead of keeping them as strings, lets be smarter. This code attempts to do the following: - detect that a string is a path into the build directory - match that string to the output of a target or configure_file - convert the string to a file - give a helpful warning and/or deprecation message - error if the string cannot be matched This allows us to get rid of the strict check to `source_strings_to_files`, since we only allowed that in one case (inside BuildTarget), and moves to fully deprecating that now that the only known case has been removed (vala gir)
1 parent 02d1cfd commit 68ab8d1

File tree

8 files changed

+116
-27
lines changed

8 files changed

+116
-27
lines changed

mesonbuild/interpreter/interpreter.py

+62-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: Apache-2.0
22
# Copyright 2012-2021 The Meson development team
3-
# Copyright © 2023 Intel Corporation
3+
# Copyright © 2023-2024 Intel Corporation
44

55
from __future__ import annotations
66

@@ -2038,7 +2038,7 @@ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str],
20382038
# string in the meantime.
20392039
FeatureNew.single_use('custom_target() with no name argument', '0.60.0', self.subproject, location=node)
20402040
name = ''
2041-
inputs = self.source_strings_to_files(kwargs['input'], strict=False)
2041+
inputs = self.source_strings_to_files(self.materialize_generated_sources(kwargs['input']))
20422042
command = kwargs['command']
20432043
if command and isinstance(command[0], str):
20442044
command[0] = self.find_program_impl([command[0]])
@@ -3078,21 +3078,18 @@ def validate_installable_file(fpath: Path) -> bool:
30783078
raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.')
30793079

30803080
@T.overload
3081-
def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = True) -> T.List['mesonlib.File']: ...
3082-
3083-
@T.overload
3084-
def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = False) -> T.List['mesonlib.FileOrString']: ... # noqa: F811
3081+
def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString']) -> T.List['mesonlib.File']: ...
30853082

30863083
@T.overload
30873084
def source_strings_to_files(self, sources: T.List[T.Union[mesonlib.FileOrString, build.GeneratedTypes]]) -> T.List[T.Union[mesonlib.File, build.GeneratedTypes]]: ... # noqa: F811
30883085

30893086
@T.overload
3090-
def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool = True) -> T.List['SourceOutputs']: ... # noqa: F811
3087+
def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: ... # noqa: F811
30913088

30923089
@T.overload
3093-
def source_strings_to_files(self, sources: T.List[SourcesVarargsType], strict: bool = True) -> T.List['SourceOutputs']: ... # noqa: F811
3090+
def source_strings_to_files(self, sources: T.List[SourcesVarargsType]) -> T.List['SourceOutputs']: ... # noqa: F811
30943091

3095-
def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool = True) -> T.List['SourceOutputs']: # noqa: F811
3092+
def source_strings_to_files(self, sources: T.List['SourceInputs']) -> T.List['SourceOutputs']: # noqa: F811
30963093
"""Lower inputs to a list of Targets and Files, replacing any strings.
30973094
30983095
:param sources: A raw (Meson DSL) list of inputs (targets, files, and
@@ -3106,13 +3103,8 @@ def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool
31063103
results: T.List['SourceOutputs'] = []
31073104
for s in sources:
31083105
if isinstance(s, str):
3109-
if not strict and s.startswith(self.environment.get_build_dir()):
3110-
results.append(s)
3111-
mlog.warning(f'Source item {s!r} cannot be converted to File object, because it is a generated file. '
3112-
'This will become a hard error in the future.', location=self.current_node)
3113-
else:
3114-
self.validate_within_subproject(self.subdir, s)
3115-
results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s))
3106+
self.validate_within_subproject(self.subdir, s)
3107+
results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s))
31163108
elif isinstance(s, mesonlib.File):
31173109
results.append(s)
31183110
elif isinstance(s, (build.GeneratedList, build.BuildTarget,
@@ -3124,6 +3116,60 @@ def source_strings_to_files(self, sources: T.List['SourceInputs'], strict: bool
31243116
'string or File-type object')
31253117
return results
31263118

3119+
def materialize_generated_sources(self, sources: T.Sequence[SourceInputs]) -> T.List[SourceInputs]:
3120+
"""Attempt to materialize Files for generated sources passed as strings.
3121+
3122+
This attempts to match a file passed as a string to a Target to the
3123+
output of a Target or configure_file call, and then create a File, while
3124+
providing warnings and deprecations.
3125+
3126+
We have to do this for backwards compatbility for Vala .gir files, and
3127+
while we're here we should just do it in general.
3128+
"""
3129+
if isinstance(sources, str):
3130+
sources = [sources]
3131+
res: T.List[SourceInputs] = []
3132+
for s in sources:
3133+
if not isinstance(s, str):
3134+
res.append(s)
3135+
continue
3136+
if not s.startswith(self.environment.build_dir):
3137+
res.append(s)
3138+
continue
3139+
# Convert the absolute path into a relative path
3140+
source = os.path.relpath(s, self.environment.build_dir)
3141+
3142+
if source in self.configure_file_outputs:
3143+
res.append(mesonlib.File.from_built_relative(source))
3144+
FeatureDeprecated.single_use('building paths to the output of a configure_file', '1.4.0', self.subproject,
3145+
'Use the returned value from configure_file() instead', self.current_node)
3146+
continue
3147+
3148+
subdir, source = os.path.split(source)
3149+
3150+
self.validate_within_subproject(self.subdir, source)
3151+
t = mesonlib.first(self.build.targets.values(), lambda b: subdir == b.subdir and source in b.outputs)
3152+
if t:
3153+
res.append(mesonlib.File.from_built_file(subdir, source))
3154+
if s.endswith('.gir'):
3155+
FeatureDeprecated.single_use('building paths to Vala generated gir files', '1.4.0', self.subproject,
3156+
'Use target.get_gir() instead', self.current_node)
3157+
else:
3158+
mlog.deprecation('Building a string path to a generated file that is part of', t.name,
3159+
'Use the target directly as as source, or, for custom_targets, index into the output.',
3160+
'If you cannot get the desired output for some reason, please file an issue.',
3161+
location=self.current_node)
3162+
continue
3163+
3164+
res.append(mesonlib.File.from_absolute_file(s, built=True))
3165+
mlog.warning(f'Attempted to build a path to a file, {s!r}, in the build directory which is not',
3166+
'a declared output of a target or configure_file. This is likely to lead to',
3167+
'race conditions and other intermittant build failures. You should use the outputs',
3168+
f'of the target creating {s!r}. If you cannot get a reference to the desired output',
3169+
'for some reason, please file an issue.', location=self.current_node)
3170+
3171+
return res
3172+
31273173
@staticmethod
31283174
def validate_forbidden_targets(name: str) -> None:
31293175
if name.startswith('meson-internal__'):

mesonbuild/utils/universal.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,8 @@ def from_built_relative(relative: str) -> 'File':
403403
return File(True, dirpart, fnamepart)
404404

405405
@staticmethod
406-
def from_absolute_file(fname: str) -> 'File':
407-
return File(False, '', fname)
406+
def from_absolute_file(fname: str, *, built: bool = False) -> 'File':
407+
return File(built, '', fname)
408408

409409
@lru_cache(maxsize=None)
410410
def rel_to_builddir(self, build_to_src: str) -> str:

test cases/common/49 custom target/meson.build

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
project('custom target')
1+
project('custom target', meson_version : '>= 1.4')
22

33
python = find_program('python3', required : false)
44
if not python.found()
@@ -20,6 +20,13 @@ install : true,
2020
install_dir : 'subdir'
2121
)
2222

23+
custom_target(
24+
'warn',
25+
input : meson.current_build_dir() / 'data.dat',
26+
output : 'data.copy',
27+
command : [python, '-c', 'import shutil, sys; shutil.copyfile(sys.argv[1], sys.argv[2])', '@INPUT@', '@OUTPUT@'],
28+
)
29+
2330
has_not_changed = false
2431
if is_disabler(mytarget)
2532
has_not_changed = true
@@ -60,6 +67,14 @@ install : true,
6067
install_dir : 'subdir'
6168
)
6269

70+
custom_target(
71+
'uses-non-existant-input',
72+
input : meson.current_build_dir() / 'non-existant-file',
73+
output : 'non-existant-output',
74+
command : [python, '-c', 'raise RuntimeError("Dont actually try to run this, mkay?")', '@INPUT@', '@OUTPUT@'],
75+
build_by_default : false,
76+
)
77+
6378
assert(is_disabler(mytarget_disabler), 'Disabled custom target is not a disabler.')
6479

6580
if mytarget_disabler.found()

test cases/common/49 custom target/test.json

+9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
{
2+
"stdout": [
3+
{
4+
"line": "test cases/common/49 custom target/meson.build:23: DEPRECATION: Building a string path to a generated file that is part of bindat Use the target directly as as source, or, for custom_targets, index into the output. If you cannot get the desired output for some reason, please file an issue."
5+
},
6+
{
7+
"line": "test cases/common/49 custom target/meson.build:70: WARNING: Attempted to build a path to a file, '.*/non-existant-file', in the build directory which is not a declared output of a target or configure_file. This is likely to lead to race conditions and other intermittant build failures. You should use the outputs of the target creating '.*/non-existant-file'. If you cannot get a reference to the desired output for some reason, please file an issue.",
8+
"match": "re"
9+
}
10+
],
211
"installed": [
312
{"type": "file", "file": "usr/subdir/data.dat"}
413
]
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
project('conf file in custom target')
1+
project('conf file in custom target', meson_version : '>= 1.4')
22

33
subdir('inc')
44
subdir('src')

test cases/common/69 configure file in custom target/src/meson.build

+10-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ compiler = configure_file(input : 'mycompiler.py',
1515
copy: true)
1616

1717
custom_target('thing2',
18-
output : 'final2.dat',
19-
input : cfile,
20-
command : [py3, compiler, '@INPUT@', '@OUTPUT@'])
18+
output : 'final2.dat',
19+
input : cfile,
20+
command : [py3, compiler, '@INPUT@', '@OUTPUT@'])
21+
22+
custom_target(
23+
'thing2-warn',
24+
output : 'final2-warn.dat',
25+
input : meson.current_build_dir() / 'mycompiler2.py',
26+
command : [py3, compiler, '@INPUT@', '@OUTPUT@']
27+
)

test cases/vala/9 gir/meson.build

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
project('foo', 'c', 'vala')
1+
project('foo', 'c', 'vala', meson_version : '>= 1.4.0')
22

33
glib = dependency('glib-2.0')
44
gobject = dependency('gobject-2.0')
@@ -10,6 +10,12 @@ foo = shared_library('foo', 'foo.vala',
1010
vala_gir: 'Foo-1.0.gir',
1111
dependencies: [glib, gobject])
1212

13+
custom_target('warn-typelib',
14+
command: [g_ir_compiler, '--output', '@OUTPUT@', '@INPUT@'],
15+
input: meson.current_build_dir() / 'Foo-1.0.gir',
16+
output: 'Warn-1.0.typelib',
17+
depends: foo)
18+
1319
custom_target('foo-typelib',
1420
command: [g_ir_compiler, '--output', '@OUTPUT@', '@INPUT@'],
1521
input: foo.get_gir(),

test cases/vala/9 gir/test.json

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
{
2+
"stdout": [
3+
{
4+
"line": "test cases/vala/9 gir/meson.build:13: WARNING: Project targets '>= 1.4.0' but uses feature deprecated since '1.4.0': building paths to Vala generated gir files. Use target.get_gir() instead"
5+
}
6+
],
27
"installed": [
3-
{"type": "expr", "platform": "gcc", "file": "usr/lib/?libfoo.so"},
4-
{"type": "file", "platform": "cygwin", "file": "usr/lib/libfoo.dll.a"},
5-
{"type": "file", "file": "usr/share/gir-1.0/Foo-1.0.gir"}
8+
{ "type": "shared_lib", "platform": "gcc", "file": "usr/lib/foo" },
9+
{ "type": "shared_lib", "platform": "msvc", "file": "usr/bin/foo" },
10+
{ "type": "implib", "file": "usr/lib/foo" },
11+
{ "type": "file", "file": "usr/share/gir-1.0/Foo-1.0.gir" }
612
]
713
}

0 commit comments

Comments
 (0)