hans updated this revision to Diff 60220.
hans added a comment.

Adding the version that caches directory contents.

This has the problem the it doesn't play with modules, the rewriter, and 
possibly others, because they write to the file system without any way for the 
vfs to find out. I've tried to find a good way to fix that, but didn't come up 
with anything. One way would be to maintain a global counter like 
llvm::sys::fileSystemChangeCount which when changed would invalidate the maps, 
but I don't know if that would be acceptable.


http://reviews.llvm.org/D21113

Files:
  include/clang/Basic/VirtualFileSystem.h
  include/clang/Driver/Options.td
  include/clang/Lex/HeaderSearchOptions.h
  lib/Basic/VirtualFileSystem.cpp
  lib/Driver/Tools.cpp
  lib/Frontend/CompilerInvocation.cpp
  test/Driver/cl-options.c
  test/Frontend/Inputs/case-insensitive-paths.h
  test/Frontend/case-insensitive-paths.c
  unittests/Basic/VirtualFileSystemTest.cpp

Index: unittests/Basic/VirtualFileSystemTest.cpp
===================================================================
--- unittests/Basic/VirtualFileSystemTest.cpp
+++ unittests/Basic/VirtualFileSystemTest.cpp
@@ -107,8 +107,15 @@
 
   vfs::directory_iterator dir_begin(const Twine &Dir,
                                     std::error_code &EC) override {
-    return vfs::directory_iterator(
+    auto I = vfs::directory_iterator(
         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
+
+    // Even if there is no entry for /foo, /foo/bar may exist, so only set the
+    // error code if /foo returns an empty iterator.
+    if (I == vfs::directory_iterator())
+      EC = status(Dir).getError();
+
+    return I;
   }
 
   void addEntry(StringRef Path, const vfs::Status &Status) {
@@ -1164,3 +1171,52 @@
   }
   EXPECT_EQ(I, E);
 }
+
+class CaseInsensitiveFileSystemTest : public ::testing::Test {
+private:
+  IntrusiveRefCntPtr<DummyFileSystem> Base;
+
+protected:
+  IntrusiveRefCntPtr<clang::vfs::FileSystem> FS;
+
+  CaseInsensitiveFileSystemTest()
+      : Base(new DummyFileSystem()),
+        FS(new clang::vfs::CaseInsensitiveFileSystem(Base)) {
+    Base->addRegularFile("/foo");
+    Base->addDirectory("/bar");
+    Base->addRegularFile("/bar/baz");
+  }
+};
+
+TEST_F(CaseInsensitiveFileSystemTest, Basic) {
+  // Not just accepting anything.
+  auto Status = FS->status("/F00");
+  ASSERT_EQ(llvm::errc::no_such_file_or_directory, Status.getError());
+
+  // Case-insensitive file is found.
+  Status = FS->status("/FoO");
+  ASSERT_FALSE(Status.getError());
+
+  // Case-insensitive dir works too.
+  Status = FS->status("/bAr/baZ");
+  ASSERT_FALSE(Status.getError());
+
+  // Test openFileForRead.
+  auto File = FS->openFileForRead("/F00");
+  ASSERT_EQ(llvm::errc::no_such_file_or_directory, File.getError());
+  File = FS->openFileForRead("/Foo");
+  ASSERT_FALSE(File.getError());
+  File = FS->openFileForRead("/Bar/Baz");
+  ASSERT_FALSE(File.getError());
+
+  // Test directory listing.
+  std::error_code EC;
+  auto Dir = FS->dir_begin("/b4r", EC);
+  ASSERT_EQ(llvm::errc::no_such_file_or_directory, EC);
+  Dir = FS->dir_begin("/bAr", EC);
+  ASSERT_FALSE(EC);
+  ASSERT_EQ("/bar/baz", Dir->getName());
+  Dir.increment(EC);
+  ASSERT_FALSE(EC);
+  ASSERT_EQ(vfs::directory_iterator(), Dir);
+}
Index: test/Frontend/case-insensitive-paths.c
===================================================================
--- /dev/null
+++ test/Frontend/case-insensitive-paths.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fsyntax-only -fcase-insensitive-paths -verify %s
+// RUN: %clang_cc1 -fsyntax-only --show-includes -fcase-insensitive-paths %s | FileCheck %s
+
+#include "InpUts/CasE-InsensitivE-Paths.h" // expected-no-diagnostics
+
+// Make sure the real filename is used when printing header dependencies.
+// CHECK: including file: {{.*}}case-insensitive-paths.h
Index: test/Driver/cl-options.c
===================================================================
--- test/Driver/cl-options.c
+++ test/Driver/cl-options.c
@@ -466,6 +466,7 @@
 // RUN:     -mllvm -disable-llvm-optzns \
 // RUN:     -Wunused-variable \
 // RUN:     -fmacro-backtrace-limit=0 \
+// RUN:     -fcase-insensitive-paths \
 // RUN:     -Werror /Zs -- %s 2>&1
 
 
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1447,6 +1447,8 @@
 
   for (const Arg *A : Args.filtered(OPT_ivfsoverlay))
     Opts.AddVFSOverlayFile(A->getValue());
+
+  Opts.CaseInsensitive = Args.hasArg(OPT_fcase_insensitive_paths);
 }
 
 void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
@@ -2538,12 +2540,8 @@
   GraveYard[Idx] = Ptr;
 }
 
-IntrusiveRefCntPtr<vfs::FileSystem>
-createVFSFromCompilerInvocation(const CompilerInvocation &CI,
-                                DiagnosticsEngine &Diags) {
-  if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty())
-    return vfs::getRealFileSystem();
-
+static IntrusiveRefCntPtr<vfs::FileSystem>
+getOverlayFS(const CompilerInvocation &CI, DiagnosticsEngine &Diags) {
   IntrusiveRefCntPtr<vfs::OverlayFileSystem>
     Overlay(new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
   // earlier vfs files are on the bottom
@@ -2565,4 +2563,20 @@
   }
   return Overlay;
 }
+
+IntrusiveRefCntPtr<vfs::FileSystem>
+createVFSFromCompilerInvocation(const CompilerInvocation &CI,
+                                DiagnosticsEngine &Diags) {
+  IntrusiveRefCntPtr<vfs::FileSystem> FS;
+
+  if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty())
+    FS = vfs::getRealFileSystem();
+  else
+    FS = getOverlayFS(CI, Diags);
+
+  if (CI.getHeaderSearchOpts().CaseInsensitive)
+    FS = new vfs::CaseInsensitiveFileSystem(FS);
+
+  return FS;
+}
 } // end namespace clang
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -570,6 +570,9 @@
   // Add CUDA include arguments, if needed.
   if (types::isCuda(Inputs[0].getType()))
     getToolChain().AddCudaIncludeArgs(Args, CmdArgs);
+
+  if (Args.hasArg(options::OPT_fcase_insensitive_paths))
+    CmdArgs.push_back("-fcase-insensitive-paths");
 }
 
 // FIXME: Move to target hook.
Index: lib/Basic/VirtualFileSystem.cpp
===================================================================
--- lib/Basic/VirtualFileSystem.cpp
+++ lib/Basic/VirtualFileSystem.cpp
@@ -392,6 +392,110 @@
       std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC));
 }
 
+//===-----------------------------------------------------------------------===/
+// CaseInsensitiveFileSystem implementation
+//===-----------------------------------------------------------------------===/
+
+bool CaseInsensitiveFileSystem::exclude(StringRef Dir, StringRef File) {
+  if (!Maps.count(Dir)) {
+    // We have no map for this Dir, but see if we can exclude the file by
+    // excluding Dir from its parent.
+    StringRef Parent = llvm::sys::path::parent_path(Dir);
+    if (!Parent.empty() &&
+        exclude(Parent, llvm::sys::path::filename(Dir))) {
+      return true;
+    }
+
+    return false;
+  }
+
+  return !Maps[Dir].count(File.lower());
+}
+
+std::error_code CaseInsensitiveFileSystem::findCaseInsensitivePath(
+    StringRef Path, SmallVectorImpl<char> &FoundPath) {
+  StringRef FileName = llvm::sys::path::filename(Path);
+  StringRef Dir = llvm::sys::path::parent_path(Path);
+
+  if (Dir.empty())
+    Dir = ".";
+
+  if (exclude(Dir, FileName))
+    return llvm::errc::no_such_file_or_directory;
+
+  if (Maps.count(Dir)) {
+    // If we have a map for this Dir and File wasn't excluded above, it must
+    // exist.
+    llvm::sys::path::append(FoundPath, Dir, Maps[Dir][FileName.lower()]);
+    return std::error_code();
+  }
+
+  std::error_code EC;
+  directory_iterator I = Base->dir_begin(Dir, EC);
+  if (EC == errc::no_such_file_or_directory) {
+    // If the dir doesn't exist, try to find it and try again.
+    SmallVector<char, 512> NewDir;
+    if (llvm::sys::path::parent_path(Dir).empty() ||
+        (EC = findCaseInsensitivePath(Dir, NewDir))) {
+      // Insert a dummy map value to mark the dir as non-existent.
+      Maps.lookup(Dir);
+      return EC;
+    }
+    llvm::sys::path::append(NewDir, FileName);
+    return findCaseInsensitivePath(StringRef(NewDir.data(), NewDir.size()),
+                                   FoundPath);
+  }
+
+  // These special entries always exist, but won't show up in the listing below.
+  Maps[Dir]["."] = ".";
+  Maps[Dir][".."] = "..";
+
+  directory_iterator E;
+  for (; !EC && I != E; I.increment(EC)) {
+    StringRef DirEntry = llvm::sys::path::filename(I->getName());
+    Maps[Dir][DirEntry.lower()] = DirEntry;
+  }
+  if (EC) {
+    // If there were problems, scrap the whole map as it may not be complete.
+    Maps.erase(Dir);
+    return EC;
+  }
+
+  auto MI = Maps[Dir].find(FileName.lower());
+  if (MI != Maps[Dir].end()) {
+    llvm::sys::path::append(FoundPath, Dir, MI->second);
+    return std::error_code();
+  }
+
+  return llvm::errc::no_such_file_or_directory;
+}
+
+llvm::ErrorOr<Status> CaseInsensitiveFileSystem::status(const Twine &Path) {
+  SmallVector<char, 512> NewPath;
+  if (std::error_code EC = findCaseInsensitivePath(Path.str(), NewPath))
+    return EC;
+
+  return Base->status(NewPath);
+}
+
+llvm::ErrorOr<std::unique_ptr<File>>
+CaseInsensitiveFileSystem::openFileForRead(const Twine &Path) {
+  SmallVector<char, 512> NewPath;
+  if (std::error_code EC = findCaseInsensitivePath(Path.str(), NewPath))
+    return EC;
+
+  return Base->openFileForRead(NewPath);
+}
+
+directory_iterator CaseInsensitiveFileSystem::dir_begin(const Twine &Path,
+                                                        std::error_code &EC) {
+  SmallVector<char, 512> NewPath;
+  if ((EC = findCaseInsensitivePath(Path.str(), NewPath)))
+    return directory_iterator();
+
+  return Base->dir_begin(NewPath, EC);
+}
+
 namespace clang {
 namespace vfs {
 namespace detail {
Index: include/clang/Lex/HeaderSearchOptions.h
===================================================================
--- include/clang/Lex/HeaderSearchOptions.h
+++ include/clang/Lex/HeaderSearchOptions.h
@@ -161,6 +161,9 @@
   /// Whether header search information should be output as for -v.
   unsigned Verbose : 1;
 
+  /// Whether header search should be case-insensitive.
+  unsigned CaseInsensitive : 1;
+
   /// \brief If true, skip verifying input files used by modules if the
   /// module was already verified during this build session (see
   /// \c BuildSessionTimestamp).
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -525,6 +525,8 @@
 def fdiagnostics_color_EQ : Joined<["-"], "fdiagnostics-color=">, Group<f_Group>;
 def fansi_escape_codes : Flag<["-"], "fansi-escape-codes">, Group<f_Group>,
   Flags<[CoreOption, CC1Option]>, HelpText<"Use ANSI escape codes for diagnostics">;
+def fcase_insensitive_paths : Flag<["-"], "fcase-insensitive-paths">, Group<f_Group>,
+  Flags<[CC1Option, CoreOption]>, HelpText<"Treat file paths as case-insensitive">;
 def fcomment_block_commands : CommaJoined<["-"], "fcomment-block-commands=">, Group<f_clang_Group>, Flags<[CC1Option]>,
   HelpText<"Treat each comma separated argument in <arg> as a documentation comment block command">,
   MetaVarName<"<arg>">;
Index: include/clang/Basic/VirtualFileSystem.h
===================================================================
--- include/clang/Basic/VirtualFileSystem.h
+++ include/clang/Basic/VirtualFileSystem.h
@@ -272,6 +272,37 @@
   iterator overlays_end() { return FSList.rend(); }
 };
 
+class CaseInsensitiveFileSystem : public FileSystem {
+  IntrusiveRefCntPtr<FileSystem> Base;
+
+  /// Try to find Path by means of case-insensitive lookup. Stores the result in
+  /// FoundPath on success, or returns an error code otherwise.
+  std::error_code findCaseInsensitivePath(StringRef Path,
+                                          SmallVectorImpl<char> &FoundPath);
+
+  /// Attempt to exclude the possibility that File exists in Dir based on
+  /// previous information.
+  bool exclude(StringRef Dir, StringRef File);
+
+  /// Map from directory to map from lowercase to real-case filename.
+  llvm::StringMap<llvm::StringMap<std::string>> Maps;
+
+public:
+  CaseInsensitiveFileSystem(IntrusiveRefCntPtr<FileSystem> Base) : Base(Base) {}
+
+  llvm::ErrorOr<Status> status(const Twine &Path) override;
+  llvm::ErrorOr<std::unique_ptr<File>>
+  openFileForRead(const Twine &Path) override;
+  directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override;
+  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
+    return Base->getCurrentWorkingDirectory();
+  }
+  std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
+    Maps.clear();
+    return Base->setCurrentWorkingDirectory(Path);
+  }
+};
+
 namespace detail {
 class InMemoryDirectory;
 } // end namespace detail
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to