[Lldb-commits] [lldb] [lldb][Windows] Use extended path prefix for rmtree (PR #209409)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 14 05:00:41 PDT 2026
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 <c_zablit at apple.com>
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 <c_zablit at apple.com>
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.
More information about the lldb-commits
mailing list