diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2025-01-09 12:54:05 +0100 |
|---|---|---|
| committer | Eli Schwartz <eschwartz93@gmail.com> | 2025-03-09 13:05:08 -0400 |
| commit | e3dfcf6cba97f00a51d34f0feb67fe88d098fff5 (patch) | |
| tree | 09ce5d2a291d79c328c17fd8dacb748c0a97a037 /mesonbuild | |
| parent | 528696300afd3236e173242e1e09d1ac80dfec81 (diff) | |
| download | meson-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.py | 51 |
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']: ... |
