Files
swift-mirror/utils/PathSanitizingFileCheck
Henrik G. Olsson 98fe7c0d1d Check if dirname exists
We only want to path normalize a string (and replace backslashes with
forward slashes if on Windows) if it's actually a path. But checking if
there's a path with that name doesn't work if the path is a substring of
an actual path. Additionally check whether the dirname points to
something on the file system. If it does, it's likely meant to be
interpreted as a path.
2025-10-13 16:51:05 -07:00

132 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python3
# utils/PathSanitizingFileCheck -*- python -*-
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
import argparse
import io
import os
import platform
import re
import subprocess
import sys
# LLVM Lit performs realpath with the config path, so all paths are relative
# to the real path. Paths that come from CMake (like cmake_binary_dir and
# swift_src_root), might not do real path. Use realpath to normalize. Because
# this normalizes Windows paths to use backslashes, we have to replace them
# back to forward slashes.
def normalize_if_path(s):
# Check dirname for cases like a file named `%t.out.txt`
# There won't be a `%t` path, but we still want to match this path substring.
if not os.path.exists(s) and not os.path.exists(os.path.dirname(s)):
return s
if platform.system() == "Windows":
return os.path.abspath(s).replace('\\', '/')
else:
return os.path.realpath(s)
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""
PathSanitizingFileCheck is a wrapper around LLVM's FileCheck. In addition
to all FileCheck features, PathSanitizingFileCheck can replace given
strings in the input with other strings. This feature is used to replace
paths to the source and build directories with path-independent
constants.""")
parser.add_argument(
"--sanitize",
help="replace the given string with another string",
metavar="REPLACEMENT=SOURCE",
action="append",
dest="sanitize_strings",
default=[])
parser.add_argument(
"--use-filecheck",
help="path to LLVM FileCheck executable",
metavar="PATH",
action="store",
dest="file_check_path",
default="FileCheck")
parser.add_argument(
"--enable-windows-compatibility",
help="Enable Windows path compatibility, which checks against both "
"forward slashes and backward slashes.",
action="store_true")
parser.add_argument(
"--enable-yaml-compatibility",
help="Enable YAML path compatibility. Since YAML double escapes "
"backward slashes, we need to check for them escaped. Only "
"available if Windows compatibility is enabled.",
action="store_true")
parser.add_argument(
"--dry-run",
help="Apply the replacements to the input and print the result "
"to standard output",
action="store_true")
parser.add_argument(
"--ignore-runtime-warnings",
help="Ignore warnings from the Swift runtime",
action="store_true")
args, unknown_args = parser.parse_known_args()
if args.enable_windows_compatibility:
if args.enable_yaml_compatibility:
slashes_re = r'(/|\\\\|\\\\\\\\)'
else:
slashes_re = r'(/|\\\\)'
else:
slashes_re = r'/'
stdin = io.open(sys.stdin.fileno(), 'r', encoding='utf-8', errors='ignore').read()
for s in sorted(args.sanitize_strings, key=len, reverse=True):
replacement, pattern = s.split('=', 1)
# Since we want to use pattern as a regex in some platforms, we need
# to escape it first, and then replace the escaped slash
# literal (r'\\/') for our platform-dependent slash regex.
stdin = re.sub(re.sub(r'\\/' if sys.version_info[0] < 3 else r'/',
slashes_re, re.escape(normalize_if_path(pattern))),
replacement, stdin)
# Because we force the backtracer on in the tests, we can get runtime
# warnings about privileged programs. Suppress those, and also the
# warning it might emit if backtracing isn't supported on the test platform.
# Additionally, suppress warnings about unknown backtracer options, since
# we might want to add new ones to the lit tests and we should ignore
# messages from the system copy of the runtime in that case.
if args.ignore_runtime_warnings:
stdin = re.sub(r'^swift runtime: (backtrace-on-crash is not '
r'supported|unknown) .*\n', "", stdin, flags=re.M)
if args.dry_run:
print(stdin)
return 0
else:
p = subprocess.Popen(
[args.file_check_path] + unknown_args, stdin=subprocess.PIPE)
stdout, stderr = p.communicate(stdin.encode('utf-8'))
if stdout is not None:
print(stdout)
if stderr is not None:
print(stderr, file=sys.stderr)
return p.wait()
if __name__ == '__main__':
exit(main())