https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/209409
>From fa2a80b744944f03b760bb071cb659ebc98fa3b3 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Tue, 14 Jul 2026 10:18:34 +0100 Subject: [PATCH 1/2] [lldb][Windows] Use extended path prefix for rmtree --- lldb/packages/Python/lldbsuite/test/lldbtest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index a9c229be1a771..2fc8601f93974 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -846,7 +846,12 @@ def _ignore_enoent(func, path, exc_info): return raise exc_info[1] - shutil.rmtree(bdir, onerror=_ignore_enoent) + # Delete via the \\?\ extended length path prefix so rmtree can + # remove build artifacts whose full path exceeds MAX_PATH (260). + rmtree_target = bdir + if sys.platform == "win32": + rmtree_target = "\\\\?\\" + bdir + shutil.rmtree(rmtree_target, onerror=_ignore_enoent) lldbutil.mkdir_p(bdir) def getBuildArtifact(self, name="a.out"): >From 3025dbfe0bedf77d1d2ebef49bf9093b91db4f00 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Tue, 14 Jul 2026 13:00:28 +0100 Subject: [PATCH 2/2] fixup! [lldb][Windows] Use extended path prefix for rmtree --- .../Python/lldbsuite/test/lldbtest.py | 28 ++++--------------- .../Python/lldbsuite/test/lldbutil.py | 24 ++++++++++++++++ .../API/driver/longpath/TestLongPathDriver.py | 3 +- .../functionalities/longpath/TestLongPath.py | 2 +- .../longpath/TestDAP_launch_longPath.py | 3 +- lldb/test/Shell/helper/toolchain.py | 7 ----- 6 files changed, 35 insertions(+), 32 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py index 2fc8601f93974..fbc06af87a246 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -824,13 +824,6 @@ def makeBuildDir(self): """Create the test-specific working directory, optionally deleting any previous contents.""" bdir = self.getBuildDir() - if sys.platform == "win32" and len(bdir) > 256: - import warnings - - warnings.warn( - "Test build directory path exceeds 256 characters (Windows " - "MAX_PATH limit): {}".format(bdir) - ) if os.path.isdir(bdir) and not self.SHARED_BUILD_TESTCASE: # Tolerate files vanishing mid-walk. Clang's implicit module # build leaves behind `*.pcm.lock` lockfiles whose lifetime is @@ -846,25 +839,16 @@ def _ignore_enoent(func, path, exc_info): return raise exc_info[1] - # Delete via the \\?\ extended length path prefix so rmtree can - # remove build artifacts whose full path exceeds MAX_PATH (260). - rmtree_target = bdir - if sys.platform == "win32": - rmtree_target = "\\\\?\\" + bdir - shutil.rmtree(rmtree_target, onerror=_ignore_enoent) + # Delete via the \\?\ extended-length path form so rmtree can remove + # build artifacts whose full path exceeds MAX_PATH (260) on Windows. + shutil.rmtree( + lldbutil.get_extended_windows_path(bdir), onerror=_ignore_enoent + ) lldbutil.mkdir_p(bdir) def getBuildArtifact(self, name="a.out"): """Return absolute path to an artifact in the test's build directory.""" - artifact_path = os.path.join(self.getBuildDir(), name) - if sys.platform == "win32" and len(artifact_path) > 256: - import warnings - - warnings.warn( - "Test artifact path exceeds 256 characters (Windows " - "MAX_PATH limit): {}".format(artifact_path) - ) - return artifact_path + return os.path.join(self.getBuildDir(), name) def getSourcePath(self, name): """Return absolute path to a file in the test's source directory.""" diff --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py index 91f872bce3827..8fd2c18deba79 100644 --- a/lldb/packages/Python/lldbsuite/test/lldbutil.py +++ b/lldb/packages/Python/lldbsuite/test/lldbutil.py @@ -59,6 +59,30 @@ def mkdir_p(path): raise OSError(errno.ENOTDIR, "%s is not a directory" % path) +def get_extended_windows_path(path): + r"""Return ``path`` in the Windows extended-length ``\\?\`` form so it can be + handed to the Win32 API (and therefore to ``os``/``shutil``) even when it is + longer than MAX_PATH (260 characters). On non-Windows platforms ``path`` is + returned unchanged. + + The ``\\?\`` prefix disables all path normalization normally performed by + Win32, so the path must be fully qualified, use backslash separators and + contain no ``.``/``..`` components. ``os.path.abspath`` guarantees all three. + """ + if sys.platform != "win32": + return path + if path.startswith("\\\\?\\"): + return path + assert os.path.isabs(path), ( + "cannot form a \\\\?\\ extended-length path from relative path: %s" % path + ) + abs_path = os.path.abspath(path) + # UNC shares (\\server\share) use the \\?\UNC\ spelling. + if abs_path.startswith("\\\\"): + return "\\\\?\\UNC\\" + abs_path[2:] + return "\\\\?\\" + abs_path + + # ============================ # Dealing with SDK and triples # ============================ diff --git a/lldb/test/API/driver/longpath/TestLongPathDriver.py b/lldb/test/API/driver/longpath/TestLongPathDriver.py index 280180391790f..c74229b860367 100644 --- a/lldb/test/API/driver/longpath/TestLongPathDriver.py +++ b/lldb/test/API/driver/longpath/TestLongPathDriver.py @@ -10,6 +10,7 @@ import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil MAX_PATH = 260 @@ -19,7 +20,7 @@ class DriverLongPathTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True def _long_path(self, path): - return "\\\\?\\" + os.path.abspath(path) + return lldbutil.get_extended_windows_path(path) def _make_long_dir(self): components = [self.getBuildArtifact("deep")] + ["d" * 80] * 3 diff --git a/lldb/test/API/functionalities/longpath/TestLongPath.py b/lldb/test/API/functionalities/longpath/TestLongPath.py index 590d17f82945c..28e0df4859316 100644 --- a/lldb/test/API/functionalities/longpath/TestLongPath.py +++ b/lldb/test/API/functionalities/longpath/TestLongPath.py @@ -19,7 +19,7 @@ class LongPathTargetTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True def _long_path(self, path): - return "\\\\?\\" + os.path.abspath(path) + return lldbutil.get_extended_windows_path(path) def _normalize(self, path): if path.startswith("\\\\?\\"): diff --git a/lldb/test/API/tools/lldb-dap/longpath/TestDAP_launch_longPath.py b/lldb/test/API/tools/lldb-dap/longpath/TestDAP_launch_longPath.py index ebd0afe0f3483..d7c86b5a9a286 100644 --- a/lldb/test/API/tools/lldb-dap/longpath/TestDAP_launch_longPath.py +++ b/lldb/test/API/tools/lldb-dap/longpath/TestDAP_launch_longPath.py @@ -8,6 +8,7 @@ import lldbdap_testcase from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil MAX_PATH = 260 @@ -15,7 +16,7 @@ @skipUnlessWindows class TestDAP_launch_longPath(lldbdap_testcase.DAPTestCaseBase): def _long_path(self, path): - return "\\\\?\\" + os.path.abspath(path) + return lldbutil.get_extended_windows_path(path) def _normalize(self, path): if path.startswith("\\\\?\\"): diff --git a/lldb/test/Shell/helper/toolchain.py b/lldb/test/Shell/helper/toolchain.py index c00985a8ef615..df20a4ae7af5e 100644 --- a/lldb/test/Shell/helper/toolchain.py +++ b/lldb/test/Shell/helper/toolchain.py @@ -55,13 +55,6 @@ def __init__( super().__init__(execute_external, extra_substitutions, preamble_commands) def execute(self, test, litConfig): - exec_path = test.getExecPath() - if platform.system() == "Windows" and len(exec_path) > 256: - litConfig.warning( - "Test path exceeds 256 characters (Windows MAX_PATH limit): " - + exec_path - ) - # Run each Shell test in a separate directory (on remote). # Find directory change command in %lldb substitution. _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
