summaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2025-01-09 12:54:05 +0100
committerEli Schwartz <eschwartz93@gmail.com>2025-03-09 13:05:08 -0400
commite3dfcf6cba97f00a51d34f0feb67fe88d098fff5 (patch)
tree09ce5d2a291d79c328c17fd8dacb748c0a97a037 /mesonbuild
parent528696300afd3236e173242e1e09d1ac80dfec81 (diff)
downloadmeson-e3dfcf6cba97f00a51d34f0feb67fe88d098fff5.tar.gz
interpreter: memoize result of validate_within_subproject
This saves about 1% of execution time; path manipulation is expensive. About 1% more could be saved by avoiding usage of "foo in bar.parents", which is an expensive way to say "bar.startswith(foo)". Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/interpreter/interpreter.py51
1 files changed, 31 insertions, 20 deletions
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index a118b3c59..dd91474eb 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -292,6 +292,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.sanity_check_ast()
self.builtin.update({'meson': MesonMain(self.build, self)})
self.processed_buildfiles: T.Set[str] = set()
+ self.validated_cache: T.Set[str] = set()
self.project_args_frozen = False
self.global_args_frozen = False # implies self.project_args_frozen
self.subprojects: T.Dict[str, SubprojectHolder] = {}
@@ -3129,27 +3130,37 @@ class Interpreter(InterpreterBase, HoldableObject):
# subproject files, as long as they are scheduled to be installed.
if validate_installable_file(norm):
return
- norm = Path(os.path.abspath(Path(srcdir, subdir, fname)))
- if os.path.isdir(norm):
- inputtype = 'directory'
- else:
- inputtype = 'file'
- if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents:
- return
- if srcdir not in norm.parents:
- # Grabbing files outside the source tree is ok.
- # This is for vendor stuff like:
- #
- # /opt/vendorsdk/src/file_with_license_restrictions.c
- return
- project_root = Path(srcdir, self.root_subdir)
- subproject_dir = project_root / self.subproject_dir
- if norm == project_root:
+
+ def do_validate_within_subproject(norm: Path) -> None:
+ if os.path.isdir(norm):
+ inputtype = 'directory'
+ else:
+ inputtype = 'file'
+ if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents:
+ return
+ if srcdir not in norm.parents:
+ # Grabbing files outside the source tree is ok.
+ # This is for vendor stuff like:
+ #
+ # /opt/vendorsdk/src/file_with_license_restrictions.c
+ return
+ project_root = Path(srcdir, self.root_subdir)
+ subproject_dir = project_root / self.subproject_dir
+ if norm == project_root:
+ return
+ if project_root not in norm.parents:
+ raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.')
+ if subproject_dir == norm or subproject_dir in norm.parents:
+ raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.')
+
+ fname = os.path.join(subdir, fname)
+ if fname in self.validated_cache:
return
- if project_root not in norm.parents:
- raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.')
- if subproject_dir == norm or subproject_dir in norm.parents:
- raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.')
+
+ # Use os.path.abspath() to eliminate .. segments, but do not resolve symlinks
+ norm = Path(os.path.abspath(Path(srcdir, fname)))
+ do_validate_within_subproject(norm)
+ self.validated_cache.add(fname)
@T.overload
def source_strings_to_files(self, sources: T.List['mesonlib.FileOrString'], strict: bool = True) -> T.List['mesonlib.File']: ...