Skip to content

Commit 010ce0a

Browse files
Ignore hidden directories in CsProjGenerator (fixes #3110) (#3116)
* Ignore hidden directories in CsProjGenerator (fixes #3110) * Address review feedback: use DirectoryEnumerationOptions and add #if NETSTANDARD2_0 checks * address maintainer feedback: remove redundant checks
1 parent 4cb0703 commit 010ce0a

2 files changed

Lines changed: 42 additions & 12 deletions

File tree

src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -389,23 +389,30 @@ public override int GetHashCode()
389389
=> HashCode.Combine(TargetFrameworkMoniker, RuntimeFrameworkVersion, CliPath, PackagesPath);
390390
}
391391

392-
file static class Helpers
392+
internal static class Helpers
393393
{
394-
private static readonly HashSet<string> IgnoredDirectoryNames = new(StringComparer.Ordinal)
394+
private static readonly HashSet<string> IgnoredDirectoryNames = new(StringComparer.OrdinalIgnoreCase)
395395
{
396-
".git",
397-
".vs",
398396
"bin",
399397
"obj",
400398
};
401399

402-
private static readonly HashSet<string> ProjectExtensions = new(StringComparer.Ordinal)
400+
private static readonly HashSet<string> ProjectExtensions = new(StringComparer.OrdinalIgnoreCase)
403401
{
404402
".csproj",
405403
".fsproj",
406404
".vbproj"
407405
};
408406

407+
private static bool ShouldIgnoreDirectory(DirectoryInfo directory)
408+
=> IgnoredDirectoryNames.Contains(directory.Name)
409+
|| directory.Name.StartsWith(".", StringComparison.Ordinal)
410+
#if NETSTANDARD2_0
411+
|| directory.Attributes.HasFlag(FileAttributes.Hidden)
412+
|| directory.Attributes.HasFlag(FileAttributes.ReparsePoint)
413+
#endif
414+
;
415+
409416
public static FileInfo FindProjectFile(DirectoryInfo rootDirectory, string projectName)
410417
{
411418
var projectFiles = EnumerateProjectFiles(rootDirectory, projectName);
@@ -448,13 +455,8 @@ private static IEnumerable<FileInfo> EnumerateProjectFiles(DirectoryInfo rootDir
448455
// 2. Handle sub directories.
449456
foreach (var dir in subDirectories)
450457
{
451-
if (IgnoredDirectoryNames.Contains(dir.Name))
458+
if (ShouldIgnoreDirectory(dir))
452459
continue;
453-
#if NETSTANDARD2_0
454-
// Ignore reparse point / symlink to avoid infinite loops
455-
if (dir.Attributes.HasFlag(FileAttributes.ReparsePoint))
456-
continue;
457-
#endif
458460
stack.Push(dir);
459461
}
460462
}
@@ -493,7 +495,7 @@ private static IEnumerable<DirectoryInfo> GetSubDirectories(DirectoryInfo curren
493495
{
494496
RecurseSubdirectories = false,
495497
IgnoreInaccessible = true,
496-
AttributesToSkip = FileAttributes.ReparsePoint
498+
AttributesToSkip = FileAttributes.ReparsePoint | FileAttributes.Hidden
497499
};
498500

499501
private static IEnumerable<FileInfo> EnumerateProjectFiles(DirectoryInfo directory)

tests/BenchmarkDotNet.Tests/CsProjGeneratorTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,34 @@ public void TestAssemblyFilePathIsUsedWhenTheAssemblyLocationIsNotEmpty()
237237
Assert.Equal(expectedPath, binariesPath);
238238
}
239239

240+
[Fact]
241+
public void FindProjectFileIgnoresDotAndHiddenDirectories()
242+
{
243+
var temporaryDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
244+
Directory.CreateDirectory(temporaryDirectory);
245+
246+
try
247+
{
248+
var rootProjectDir = Path.Combine(temporaryDirectory, "XXX.Benchmark");
249+
Directory.CreateDirectory(rootProjectDir);
250+
var rootProjectFile = Path.Combine(rootProjectDir, "XXX.Benchmark.csproj");
251+
File.WriteAllText(rootProjectFile, "<Project />");
252+
253+
var hiddenProjectDir = Path.Combine(temporaryDirectory, ".claude", "worktrees", "XXX.Benchmark");
254+
Directory.CreateDirectory(hiddenProjectDir);
255+
var hiddenProjectFile = Path.Combine(hiddenProjectDir, "XXX.Benchmark.csproj");
256+
File.WriteAllText(hiddenProjectFile, "<Project />");
257+
258+
var foundProject = BenchmarkDotNet.Toolchains.CsProj.Helpers.FindProjectFile(new DirectoryInfo(temporaryDirectory), "XXX.Benchmark");
259+
260+
Assert.Equal(Path.GetFullPath(rootProjectFile), Path.GetFullPath(foundProject.FullName));
261+
}
262+
finally
263+
{
264+
Directory.Delete(temporaryDirectory, recursive: true);
265+
}
266+
}
267+
240268
private class SteamLoadedBuildPartition : CsProjGenerator
241269
{
242270
internal string ResolvePathForBinaries(BuildPartition buildPartition, string programName)

0 commit comments

Comments
 (0)