diff --git a/Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs b/Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs
index ce74fc2293..d76c52e550 100644
--- a/Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs
+++ b/Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs
@@ -12,6 +12,8 @@
using io.github.hatayama.UnityCliLoop.Domain;
using io.github.hatayama.UnityCliLoop.Infrastructure;
+using static io.github.hatayama.UnityCliLoop.Infrastructure.ThirdPartyToolMigrationFileServiceConstants;
+
namespace io.github.hatayama.UnityCliLoop.Tests.Editor
{
///
@@ -70,6 +72,162 @@ public void ThirdPartyToolMigrationSourceFileCache_WhenFileIsReadTwice_UsesCache
Assert.That(readCount, Is.EqualTo(1));
}
+ [Test]
+ [Platform(Include = "Win")]
+ public void ThirdPartyToolMigrationSourceFileCache_WhenWindowsPathExceedsLegacyLimit_ReadsSource()
+ {
+ // Verifies that migration preview can inspect source files beyond the legacy Windows path limit.
+ string projectRoot = CreateProjectRoot();
+ try
+ {
+ string longDirectoryPath = CreateLongDirectoryPath(projectRoot);
+ string filePath = Path.Combine(longDirectoryPath, "LongPathTool.cs");
+ Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
+ File.WriteAllText(
+ ToWindowsExtendedLengthPath(filePath),
+ "public sealed class LongPathTool {}");
+
+ Assert.That(filePath.Length, Is.GreaterThanOrEqualTo(WindowsLegacyMaxPathLength));
+ ThirdPartyToolMigrationSourceFileCache cache = new();
+
+ string source = cache.ReadAllText(filePath);
+
+ Assert.That(source, Does.Contain("LongPathTool"));
+ }
+ finally
+ {
+ Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
+ }
+ }
+
+ [Test]
+ [Platform(Include = "Win")]
+ public async Task ApplyMigrationAsync_WhenWindowsPathExceedsLegacyLimit_MigratesSource()
+ {
+ // Verifies that migration apply can rewrite source files beyond the legacy Windows path limit.
+ string projectRoot = CreateProjectRoot();
+ try
+ {
+ string longDirectoryPath = CreateLongDirectoryPath(projectRoot);
+ string filePath = Path.Combine(longDirectoryPath, "LongPathTool.cs");
+ Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
+ File.WriteAllText(
+ ToWindowsExtendedLengthPath(filePath),
+ @"using io.github.hatayama.uLoopMCP;
+
+[McpTool]
+public sealed class LongPathTool : AbstractUnityTool
+{
+}
+
+public sealed class LongPathSchema : BaseToolSchema
+{
+}
+
+public sealed class LongPathResponse : BaseToolResponse
+{
+}");
+
+ ThirdPartyToolMigrationFileService service = new();
+
+ ThirdPartyToolMigrationResult result =
+ await service.ApplyMigrationAsync(
+ projectRoot,
+ new Progress(),
+ CancellationToken.None);
+ string migratedSource = File.ReadAllText(ToWindowsExtendedLengthPath(filePath));
+
+ Assert.That(result.FilePaths.Contains(filePath), Is.True);
+ Assert.That(migratedSource, Does.Contain("UnityCliLoopTool"));
+ }
+ finally
+ {
+ Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
+ }
+ }
+
+ [Test]
+ [Platform(Include = "Win")]
+ public async Task ApplyMigrationAsync_WhenWindowsDirectoryPathExceedsLegacyLimit_MigratesSource()
+ {
+ // Verifies that migration apply can discover source files in directories beyond the legacy Windows path limit.
+ string projectRoot = CreateProjectRoot();
+ try
+ {
+ string longDirectoryPath = CreateVeryLongDirectoryPath(projectRoot);
+ string filePath = Path.Combine(longDirectoryPath, "LongDirectoryTool.cs");
+ Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
+ File.WriteAllText(
+ ToWindowsExtendedLengthPath(filePath),
+ @"using io.github.hatayama.uLoopMCP;
+
+[McpTool]
+public sealed class LongDirectoryTool : AbstractUnityTool
+{
+}
+
+public sealed class LongDirectorySchema : BaseToolSchema
+{
+}
+
+public sealed class LongDirectoryResponse : BaseToolResponse
+{
+}");
+
+ ThirdPartyToolMigrationFileService service = new();
+
+ ThirdPartyToolMigrationResult result =
+ await service.ApplyMigrationAsync(
+ projectRoot,
+ new Progress(),
+ CancellationToken.None);
+ string migratedSource = File.ReadAllText(ToWindowsExtendedLengthPath(filePath));
+
+ Assert.That(longDirectoryPath.Length, Is.GreaterThanOrEqualTo(WindowsLegacyMaxPathLength));
+ Assert.That(result.FilePaths.Contains(filePath), Is.True);
+ Assert.That(
+ migratedSource,
+ Does.Contain("UnityCliLoopTool"));
+ }
+ finally
+ {
+ Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
+ }
+ }
+
+ [Test]
+ [Platform(Include = "Win")]
+ public void MigrationProjectFingerprint_WhenWindowsPathExceedsLegacyLimit_DetectsContentChange()
+ {
+ // Verifies that long-path source edits invalidate cached migration plans.
+ string projectRoot = CreateProjectRoot();
+ try
+ {
+ string longDirectoryPath = CreateLongDirectoryPath(projectRoot);
+ string filePath = Path.Combine(longDirectoryPath, "LongPathTool.cs");
+ string originalSource = "public sealed class AlphaTool {}";
+ string changedSource = "public sealed class BravoTool {}";
+ DateTime originalLastWriteTimeUtc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
+ File.WriteAllText(ToWindowsExtendedLengthPath(filePath), originalSource);
+ File.SetLastWriteTimeUtc(ToWindowsExtendedLengthPath(filePath), originalLastWriteTimeUtc);
+ ProjectFileInventory firstInventory = ProjectFileInventory.Create(projectRoot);
+ MigrationProjectFingerprint fingerprint =
+ MigrationProjectFingerprint.CaptureFromInventory(firstInventory);
+
+ File.WriteAllText(ToWindowsExtendedLengthPath(filePath), changedSource);
+ File.SetLastWriteTimeUtc(ToWindowsExtendedLengthPath(filePath), originalLastWriteTimeUtc);
+ ProjectFileInventory changedInventory = ProjectFileInventory.Create(projectRoot);
+
+ Assert.That(changedSource.Length, Is.EqualTo(originalSource.Length));
+ Assert.That(fingerprint.Matches(changedInventory), Is.False);
+ }
+ finally
+ {
+ Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
+ }
+ }
+
[Test]
public void MigrationProjectFingerprint_WhenCandidateFileChanges_DoesNotMatch()
{
@@ -5472,6 +5630,54 @@ private static string CreateProjectRoot()
return projectRoot;
}
+ private static string CreateLongDirectoryPath(string projectRoot)
+ {
+ Assert.That(projectRoot, Is.Not.Empty);
+
+ string directoryPath = Path.Combine(projectRoot, "Assets");
+ int segmentIndex = 0;
+ while (Path.Combine(directoryPath, "LongPathTool.cs").Length < WindowsLegacyMaxPathLength)
+ {
+ directoryPath = Path.Combine(directoryPath, "LongPathSegment" + segmentIndex.ToString("D2"));
+ segmentIndex++;
+ }
+
+ return directoryPath;
+ }
+
+ private static string CreateVeryLongDirectoryPath(string projectRoot)
+ {
+ Assert.That(projectRoot, Is.Not.Empty);
+
+ string directoryPath = Path.Combine(projectRoot, "Assets");
+ int segmentIndex = 0;
+ while (directoryPath.Length < WindowsLegacyMaxPathLength)
+ {
+ directoryPath = Path.Combine(directoryPath, "LongDirectorySegment" + segmentIndex.ToString("D2"));
+ segmentIndex++;
+ }
+
+ return directoryPath;
+ }
+
+ private static string ToWindowsExtendedLengthPath(string path)
+ {
+ Assert.That(path, Is.Not.Empty);
+
+ string fullPath = Path.GetFullPath(path);
+ if (fullPath.StartsWith(WindowsExtendedLengthPathPrefix, StringComparison.Ordinal))
+ {
+ return fullPath;
+ }
+
+ if (fullPath.StartsWith(WindowsUncPathPrefix, StringComparison.Ordinal))
+ {
+ return WindowsExtendedLengthUncPathPrefix + fullPath.Substring(WindowsUncPathPrefix.Length);
+ }
+
+ return WindowsExtendedLengthPathPrefix + fullPath;
+ }
+
private sealed class RecordingMigrationProgress : IProgress
{
private readonly List _reports;
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMetaGuidReader.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMetaGuidReader.cs
index 07e6f5f6a6..8492407f41 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMetaGuidReader.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMetaGuidReader.cs
@@ -15,12 +15,12 @@ internal static string ReadAsmdefGuidReferenceFromAsmdefPath(string asmdefFilePa
Debug.Assert(!string.IsNullOrEmpty(asmdefFilePath), "asmdefFilePath must not be null or empty");
string metaPath = asmdefFilePath + ".meta";
- if (!File.Exists(metaPath))
+ if (!ThirdPartyToolMigrationFileAccess.Exists(metaPath))
{
return string.Empty;
}
- return ReadAsmdefGuidReferenceFromMetaFile(metaPath, File.ReadLines);
+ return ReadAsmdefGuidReferenceFromMetaFile(metaPath, ThirdPartyToolMigrationFileAccess.ReadLines);
}
internal static string ReadAsmdefGuidReferenceFromMetaFile(
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMigrationRequirementResolver.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMigrationRequirementResolver.cs
index f9e7192014..74f1346cf7 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMigrationRequirementResolver.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefMigrationRequirementResolver.cs
@@ -30,7 +30,7 @@ internal static bool ContainsAsmdefMigrationTarget(
out requiresApplicationReference,
out requiresDomainReference,
out requiresFirstPartyScreenshotReference);
- string source = File.ReadAllText(asmdefFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(asmdefFilePath);
if (!hasAssemblyMigrationRequirement &&
!ThirdPartyToolMigrationRules.ContainsLegacyMigrationCandidateText(source))
{
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyReferenceResolver.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyReferenceResolver.cs
index 62ebc5ff59..140448fc2e 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyReferenceResolver.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyReferenceResolver.cs
@@ -269,7 +269,10 @@ internal static async Task> CreateAsmdefDirectoryMapA
internal static bool TryReadJsonObjectFromFile(string filePath, out JObject jsonObject)
{
- return TryReadJsonObjectForMigration(filePath, File.ReadAllText, out jsonObject);
+ return TryReadJsonObjectForMigration(
+ filePath,
+ ThirdPartyToolMigrationFileAccess.ReadAllText,
+ out jsonObject);
}
internal static bool TryReadJsonObjectFromCache(
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs
index 10e04c4063..aa6b05061d 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs
@@ -60,7 +60,7 @@ internal static MigrationAssemblyUsage FindMigrationAssemblyUsage(
foreach (string csharpFilePath in csharpFilePaths)
{
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
sourceByCSharpFilePath.Add(csharpFilePath, source);
if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
{
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastAssemblyRequirementCollector.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastAssemblyRequirementCollector.cs
index 2c74c93001..f228ade261 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastAssemblyRequirementCollector.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastAssemblyRequirementCollector.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -166,7 +165,7 @@ internal static async Task CollectFastAssemblyReferenceRequirementsAsync(
return;
}
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
@@ -250,7 +249,7 @@ internal static async Task CollectFastAssemblyScopedCurrentToolContractsRequirem
return;
}
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
@@ -317,7 +316,7 @@ internal static async Task CollectFastAssemblyScopedCurrentDomainRequirementsAsy
return;
}
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
@@ -391,7 +390,7 @@ internal static async Task CollectFastFirstPartyScreenshotRequirementsAsyn
return false;
}
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastSourceTargetDetector.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastSourceTargetDetector.cs
index 2ed103c8dc..ae74a5cd05 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastSourceTargetDetector.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFastSourceTargetDetector.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -89,7 +88,7 @@ internal static async Task ContainsFastCSharpSourceMigrationTargetAsync(
return false;
}
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileServiceConstants.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileServiceConstants.cs
index 2a3bdb7414..e7194b6b5f 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileServiceConstants.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileServiceConstants.cs
@@ -5,6 +5,10 @@ namespace io.github.hatayama.UnityCliLoop.Infrastructure
///
internal static class ThirdPartyToolMigrationFileServiceConstants
{
+ internal const int WindowsLegacyMaxPathLength = 260;
+ internal const string WindowsExtendedLengthPathPrefix = @"\\?\";
+ internal const string WindowsExtendedLengthUncPathPrefix = @"\\?\UNC\";
+ internal const string WindowsUncPathPrefix = @"\\";
internal const string ImplicitEditorAssemblyDirectoryName = "__UnityCliLoopImplicitEditorAssembly";
internal const string ImplicitRuntimeAssemblyDirectoryName = "__UnityCliLoopImplicitRuntimeAssembly";
internal const string ImplicitFirstPassEditorAssemblyDirectoryName =
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileWriter.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileWriter.cs
index c332c3654a..9cd2875ecd 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileWriter.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileWriter.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -15,16 +16,16 @@ internal static void Write(string filePath, string content)
Debug.Assert(content != null, "content must not be null");
string tempFilePath = CreateUniqueSidecarPath(filePath, ".tmp");
- File.WriteAllText(tempFilePath, content);
- if (!File.Exists(filePath))
+ ThirdPartyToolMigrationFileAccess.WriteAllText(tempFilePath, content);
+ if (!ThirdPartyToolMigrationFileAccess.Exists(filePath))
{
- File.Move(tempFilePath, filePath);
+ ThirdPartyToolMigrationFileAccess.Move(tempFilePath, filePath);
return;
}
string backupFilePath = CreateUniqueSidecarPath(filePath, ".bak");
- File.Replace(tempFilePath, filePath, backupFilePath);
- File.Delete(backupFilePath);
+ ThirdPartyToolMigrationFileAccess.Replace(tempFilePath, filePath, backupFilePath);
+ ThirdPartyToolMigrationFileAccess.Delete(backupFilePath);
}
internal static string CreateUniqueSidecarPath(string filePath, string extension)
@@ -35,7 +36,7 @@ internal static string CreateUniqueSidecarPath(string filePath, string extension
string directory = Path.GetDirectoryName(filePath) ?? string.Empty;
string fileName = Path.GetFileName(filePath);
string sidecarPath = Path.Combine(directory, $"{fileName}.{Guid.NewGuid():N}{extension}");
- while (File.Exists(sidecarPath))
+ while (ThirdPartyToolMigrationFileAccess.Exists(sidecarPath))
{
sidecarPath = Path.Combine(directory, $"{fileName}.{Guid.NewGuid():N}{extension}");
}
@@ -43,4 +44,124 @@ internal static string CreateUniqueSidecarPath(string filePath, string extension
return sidecarPath;
}
}
+
+ ///
+ /// Routes migration file IO through Windows extended-length paths when needed.
+ ///
+ internal static class ThirdPartyToolMigrationFileAccess
+ {
+ internal static string ReadAllText(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ return File.ReadAllText(GetFileSystemPath(filePath));
+ }
+
+ internal static void WriteAllText(string filePath, string content)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+ Debug.Assert(content != null, "content must not be null");
+
+ File.WriteAllText(GetFileSystemPath(filePath), content);
+ }
+
+ internal static IEnumerable ReadLines(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ return File.ReadLines(GetFileSystemPath(filePath));
+ }
+
+ internal static bool Exists(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ return File.Exists(GetFileSystemPath(filePath));
+ }
+
+ internal static long GetLength(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ FileInfo fileInfo = new(GetFileSystemPath(filePath));
+ return fileInfo.Length;
+ }
+
+ internal static DateTime GetLastWriteTimeUtc(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ return File.GetLastWriteTimeUtc(GetFileSystemPath(filePath));
+ }
+
+ internal static FileStream OpenRead(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ return File.OpenRead(GetFileSystemPath(filePath));
+ }
+
+ internal static void Move(string sourceFilePath, string destinationFilePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(sourceFilePath), "sourceFilePath must not be null or empty");
+ Debug.Assert(
+ !string.IsNullOrEmpty(destinationFilePath),
+ "destinationFilePath must not be null or empty");
+
+ File.Move(GetFileSystemPath(sourceFilePath), GetFileSystemPath(destinationFilePath));
+ }
+
+ internal static void Replace(
+ string sourceFilePath,
+ string destinationFilePath,
+ string destinationBackupFilePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(sourceFilePath), "sourceFilePath must not be null or empty");
+ Debug.Assert(
+ !string.IsNullOrEmpty(destinationFilePath),
+ "destinationFilePath must not be null or empty");
+ Debug.Assert(
+ !string.IsNullOrEmpty(destinationBackupFilePath),
+ "destinationBackupFilePath must not be null or empty");
+
+ File.Replace(
+ GetFileSystemPath(sourceFilePath),
+ GetFileSystemPath(destinationFilePath),
+ GetFileSystemPath(destinationBackupFilePath));
+ }
+
+ internal static void Delete(string filePath)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
+
+ File.Delete(GetFileSystemPath(filePath));
+ }
+
+ private static string GetFileSystemPath(string path)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(path), "path must not be null or empty");
+
+ string fullPath = Path.GetFullPath(path);
+ if (Path.DirectorySeparatorChar != '\\' ||
+ fullPath.Length < ThirdPartyToolMigrationFileServiceConstants.WindowsLegacyMaxPathLength ||
+ fullPath.StartsWith(
+ ThirdPartyToolMigrationFileServiceConstants.WindowsExtendedLengthPathPrefix,
+ StringComparison.Ordinal))
+ {
+ return fullPath;
+ }
+
+ // Windows Mono still needs the extended-length prefix for file IO beyond legacy MAX_PATH.
+ if (fullPath.StartsWith(
+ ThirdPartyToolMigrationFileServiceConstants.WindowsUncPathPrefix,
+ StringComparison.Ordinal))
+ {
+ return ThirdPartyToolMigrationFileServiceConstants.WindowsExtendedLengthUncPathPrefix +
+ fullPath.Substring(
+ ThirdPartyToolMigrationFileServiceConstants.WindowsUncPathPrefix.Length);
+ }
+
+ return ThirdPartyToolMigrationFileServiceConstants.WindowsExtendedLengthPathPrefix + fullPath;
+ }
+ }
}
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationModels.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationModels.cs
index 0e2be23e9f..ebaca4997b 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationModels.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationModels.cs
@@ -162,8 +162,7 @@ public static MigrationFileFingerprint Capture(string filePath)
{
Debug.Assert(!string.IsNullOrEmpty(filePath), "filePath must not be null or empty");
- FileInfo fileInfo = new(filePath);
- if (!fileInfo.Exists)
+ if (!ThirdPartyToolMigrationFileAccess.Exists(filePath))
{
return new MigrationFileFingerprint(
filePath,
@@ -176,8 +175,8 @@ public static MigrationFileFingerprint Capture(string filePath)
return new MigrationFileFingerprint(
filePath,
true,
- fileInfo.Length,
- fileInfo.LastWriteTimeUtc.Ticks,
+ ThirdPartyToolMigrationFileAccess.GetLength(filePath),
+ ThirdPartyToolMigrationFileAccess.GetLastWriteTimeUtc(filePath).Ticks,
CaptureContentHash(filePath));
}
@@ -210,7 +209,7 @@ private static ulong CaptureReadableContentHash(string filePath)
byte[] buffer = new byte[ContentHashBufferSize];
ulong contentHash = ContentHashOffsetBasis;
- using FileStream stream = File.OpenRead(filePath);
+ using FileStream stream = ThirdPartyToolMigrationFileAccess.OpenRead(filePath);
while (true)
{
int readByteCount = stream.Read(buffer, 0, buffer.Length);
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPlanBuilder.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPlanBuilder.cs
index a02d7a073f..580b9ec1c1 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPlanBuilder.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPlanBuilder.cs
@@ -42,7 +42,7 @@ internal static MigrationPlan Create(string projectRoot)
foreach (string csharpFilePath in inventory.CSharpFilePaths)
{
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source) &&
!ThirdPartyToolMigrationRules.ContainsLegacyTypeAliasReference(source, legacyToolInfoAliases))
{
@@ -140,7 +140,7 @@ internal static MigrationPlan Create(string projectRoot)
assemblyUsage,
changes,
removedPlayerLoopTimingSignaturesByAssemblyDirectory,
- File.ReadAllText);
+ ThirdPartyToolMigrationFileAccess.ReadAllText);
foreach (string asmdefFilePath in inventory.AsmdefFilePaths)
{
@@ -158,7 +158,7 @@ internal static MigrationPlan Create(string projectRoot)
out requiresApplicationReference,
out requiresDomainReference,
out requiresFirstPartyScreenshotReference);
- string source = File.ReadAllText(asmdefFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(asmdefFilePath);
if (!hasAssemblyMigrationRequirement &&
!ThirdPartyToolMigrationRules.ContainsLegacyMigrationCandidateText(source))
{
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs
index 6ac6875c8b..52cfbd8e86 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs
@@ -186,7 +186,7 @@ private static (bool WasRead, string Source) TryReadSourceText(string filePath)
try
{
- return (true, File.ReadAllText(filePath));
+ return (true, ThirdPartyToolMigrationFileAccess.ReadAllText(filePath));
}
catch (Exception ex) when (IsSkippablePreflightReadException(ex))
{
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationSourceFileCache.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationSourceFileCache.cs
index d145f2bf3d..392995c263 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationSourceFileCache.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationSourceFileCache.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
namespace io.github.hatayama.UnityCliLoop.Infrastructure
{
@@ -14,7 +13,7 @@ internal sealed class ThirdPartyToolMigrationSourceFileCache
private readonly Dictionary _sources = new(StringComparer.Ordinal);
internal ThirdPartyToolMigrationSourceFileCache()
- : this(File.ReadAllText)
+ : this(ThirdPartyToolMigrationFileAccess.ReadAllText)
{
}
diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs
index e73daee4cb..b09b1af6b9 100644
--- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs
+++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs
@@ -84,7 +84,7 @@ internal static async Task HasMigrationTargetAsync(string projectRoot, Can
return false;
}
- string source = File.ReadAllText(csharpFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
if (ContainsFastCSharpMigrationTarget(source))
{
return true;
@@ -320,7 +320,7 @@ await CollectFastAssemblyScopedCurrentDomainRequirementsAsync(
return false;
}
- string source = File.ReadAllText(asmdefFilePath);
+ string source = ThirdPartyToolMigrationFileAccess.ReadAllText(asmdefFilePath);
if (ContainsFastAsmdefMigrationTarget(source))
{
return true;