mirror of
https://github.com/skeeto/w64devkit.git
synced 2026-10-23 06:08:56 +03:00
cmake: normalize breakpoint paths
The breakpoint manager normalizes incoming setBreakpoints paths with GetActualCaseForPath, but SourceFileLoaded and GetBreakpoints use paths from CollapseFullPath, which preserves whatever case the caller provided. On case-insensitive filesystems these can differ for the same file, so the unordered_map keys do not match and breakpoints are silently missed. On macOS (HFS+) GetActualCaseForPath is a no-op, so the setBreakpoints path passes through unmodified. If the DAP client sends "Test.cmake" but cmake was invoked with "cmake -P test.cmake", the breakpoint is stored under "Test.cmake" while GetBreakpoints looks up "test.cmake". On Windows GetActualCaseForPath *does* normalize, but only in HandleSetBreakpointsRequest. The breakpoint is stored under the canonical "Test.cmake", while SourceFileLoaded and GetBreakpoints still receive "test.cmake" from CollapseFullPath. The keys disagree from the opposite direction. Add a NormalizePath helper and apply it in all three public entry points so that every map key goes through the same transformation. On non-Windows it uses GetRealPath (wrapping realpath(3), which returns the actual filesystem case on macOS and resolves symlinks on Linux). On Windows the existing GetActualCaseForPath is retained.
This commit is contained in:
132
src/cmake-debugger-realpath.patch
Normal file
132
src/cmake-debugger-realpath.patch
Normal file
@@ -0,0 +1,132 @@
|
||||
debugger: normalize breakpoint paths on case-insensitive filesystems
|
||||
|
||||
The breakpoint manager normalizes incoming setBreakpoints paths with
|
||||
GetActualCaseForPath, but SourceFileLoaded and GetBreakpoints use
|
||||
paths from CollapseFullPath, which preserves whatever case the caller
|
||||
provided. On case-insensitive filesystems these can differ for the
|
||||
same file, so the unordered_map keys do not match and breakpoints are
|
||||
silently missed.
|
||||
|
||||
On macOS (HFS+) GetActualCaseForPath is a no-op, so the setBreakpoints
|
||||
path passes through unmodified. If the DAP client sends "Test.cmake"
|
||||
but cmake was invoked with "cmake -P test.cmake", the breakpoint is
|
||||
stored under "Test.cmake" while GetBreakpoints looks up "test.cmake".
|
||||
|
||||
On Windows GetActualCaseForPath *does* normalize, but only in
|
||||
HandleSetBreakpointsRequest. The breakpoint is stored under the
|
||||
canonical "Test.cmake", while SourceFileLoaded and GetBreakpoints
|
||||
still receive "test.cmake" from CollapseFullPath. The keys disagree
|
||||
from the opposite direction.
|
||||
|
||||
Add a NormalizePath helper and apply it in all three public entry
|
||||
points so that every map key goes through the same transformation.
|
||||
On non-Windows it uses GetRealPath (wrapping realpath(3), which
|
||||
returns the actual filesystem case on macOS and resolves symlinks on
|
||||
Linux). On Windows the existing GetActualCaseForPath is retained.
|
||||
|
||||
--- a/Source/cmDebuggerBreakpointManager.cxx
|
||||
+++ b/Source/cmDebuggerBreakpointManager.cxx
|
||||
@@ -17,6 +17,22 @@
|
||||
|
||||
namespace cmDebugger {
|
||||
|
||||
+// Resolve a source path to its canonical form so that breakpoint map
|
||||
+// keys match regardless of the case used by the DAP client or by
|
||||
+// CollapseFullPath. On macOS (case-insensitive HFS+) realpath()
|
||||
+// returns the actual filesystem case; on Linux it resolves symlinks
|
||||
+// (case is already significant); on Windows GetActualCaseForPath
|
||||
+// walks path components with FindFirstFileW.
|
||||
+static std::string NormalizePath(std::string const& sourcePath)
|
||||
+{
|
||||
+#ifdef _WIN32
|
||||
+ return cmSystemTools::GetActualCaseForPath(sourcePath);
|
||||
+#else
|
||||
+ auto real = cmSystemTools::GetRealPath(sourcePath);
|
||||
+ return real.empty() ? sourcePath : real;
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
cmDebuggerBreakpointManager::cmDebuggerBreakpointManager(
|
||||
dap::Session* dapSession)
|
||||
: DapSession(dapSession)
|
||||
@@ -73,8 +89,7 @@
|
||||
|
||||
dap::SetBreakpointsResponse response;
|
||||
|
||||
- auto sourcePath =
|
||||
- cmSystemTools::GetActualCaseForPath(request.source.path.value());
|
||||
+ auto sourcePath = NormalizePath(request.source.path.value());
|
||||
dap::array<dap::SourceBreakpoint> const defaultValue{};
|
||||
auto const& breakpoints = request.breakpoints.value(defaultValue);
|
||||
|
||||
@@ -126,42 +141,46 @@
|
||||
std::string const& sourcePath,
|
||||
std::vector<cmListFileFunction> const& functions)
|
||||
{
|
||||
+ auto normalizedPath = NormalizePath(sourcePath);
|
||||
+
|
||||
std::unique_lock<std::mutex> lock(Mutex);
|
||||
- if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) {
|
||||
+ if (ListFileFunctionLines.find(normalizedPath) !=
|
||||
+ ListFileFunctionLines.end()) {
|
||||
// this is not expected.
|
||||
return;
|
||||
}
|
||||
|
||||
for (cmListFileFunction const& func : functions) {
|
||||
- ListFileFunctionLines[sourcePath].emplace_back(
|
||||
+ ListFileFunctionLines[normalizedPath].emplace_back(
|
||||
cmDebuggerFunctionLocation{ func.Line(), func.LineEnd() });
|
||||
}
|
||||
|
||||
- if (ListFilePendingValidations.find(sourcePath) ==
|
||||
+ if (ListFilePendingValidations.find(normalizedPath) ==
|
||||
ListFilePendingValidations.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
- ListFilePendingValidations.erase(sourcePath);
|
||||
+ ListFilePendingValidations.erase(normalizedPath);
|
||||
|
||||
- for (size_t i = 0; i < Breakpoints[sourcePath].size(); i++) {
|
||||
+ for (size_t i = 0; i < Breakpoints[normalizedPath].size(); i++) {
|
||||
dap::BreakpointEvent breakpointEvent;
|
||||
- breakpointEvent.breakpoint.id = Breakpoints[sourcePath][i].GetId();
|
||||
- breakpointEvent.breakpoint.line = Breakpoints[sourcePath][i].GetLine();
|
||||
+ breakpointEvent.breakpoint.id = Breakpoints[normalizedPath][i].GetId();
|
||||
+ breakpointEvent.breakpoint.line =
|
||||
+ Breakpoints[normalizedPath][i].GetLine();
|
||||
auto source = dap::Source();
|
||||
- source.path = sourcePath;
|
||||
+ source.path = normalizedPath;
|
||||
breakpointEvent.breakpoint.source = source;
|
||||
int64_t correctedLine = CalibrateBreakpointLine(
|
||||
- sourcePath, Breakpoints[sourcePath][i].GetLine());
|
||||
- if (correctedLine != Breakpoints[sourcePath][i].GetLine()) {
|
||||
- Breakpoints[sourcePath][i].ChangeLine(correctedLine);
|
||||
+ normalizedPath, Breakpoints[normalizedPath][i].GetLine());
|
||||
+ if (correctedLine != Breakpoints[normalizedPath][i].GetLine()) {
|
||||
+ Breakpoints[normalizedPath][i].ChangeLine(correctedLine);
|
||||
}
|
||||
breakpointEvent.reason = "changed";
|
||||
breakpointEvent.breakpoint.verified = (correctedLine > 0);
|
||||
if (breakpointEvent.breakpoint.verified) {
|
||||
breakpointEvent.breakpoint.line = correctedLine;
|
||||
} else {
|
||||
- Breakpoints[sourcePath][i].Invalid();
|
||||
+ Breakpoints[normalizedPath][i].Invalid();
|
||||
}
|
||||
|
||||
DapSession->send(breakpointEvent);
|
||||
@@ -171,8 +190,10 @@
|
||||
std::vector<int64_t> cmDebuggerBreakpointManager::GetBreakpoints(
|
||||
std::string const& sourcePath, int64_t line)
|
||||
{
|
||||
+ auto normalizedPath = NormalizePath(sourcePath);
|
||||
+
|
||||
std::unique_lock<std::mutex> lock(Mutex);
|
||||
- auto const& all = Breakpoints[sourcePath];
|
||||
+ auto const& all = Breakpoints[normalizedPath];
|
||||
std::vector<int64_t> breakpoints;
|
||||
if (all.empty()) {
|
||||
return breakpoints;
|
||||
Reference in New Issue
Block a user