]> granicus.if.org Git - clang/commitdiff
Allow multi-component paths in VFS file nodes
authorBen Langmuir <blangmuir@apple.com>
Tue, 25 Feb 2014 04:34:14 +0000 (04:34 +0000)
committerBen Langmuir <blangmuir@apple.com>
Tue, 25 Feb 2014 04:34:14 +0000 (04:34 +0000)
This allows the 'name' field to contain a path, like

{ 'type': 'directory',
  'name': '/path/to/dir',
  'contents': [ ... ] }

which not only simplifies reading and writing these files (for humans),
but makes it possible to easily modify locations via textual
replacement, which would not have worked in the old scheme.

E.g. sed s:<ROOT>:<NEW ROOT>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@202109 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Basic/VirtualFileSystem.cpp
unittests/Basic/VirtualFileSystemTest.cpp

index 6aef524abaefea3dd78822d11c5f48ce52b2daa8..881e1663953ffbf64dc090ab71b7efa62c856471 100644 (file)
@@ -227,9 +227,6 @@ class Entry {
 
 public:
   virtual ~Entry();
-#if LLVM_HAS_RVALUE_REFERENCES
-  Entry(EntryKind K, std::string Name) : Kind(K), Name(std::move(Name)) {}
-#endif
   Entry(EntryKind K, StringRef Name) : Kind(K), Name(Name) {}
   StringRef getName() const { return Name; }
   EntryKind getKind() const { return Kind; }
@@ -242,8 +239,8 @@ class DirectoryEntry : public Entry {
 public:
   virtual ~DirectoryEntry();
 #if LLVM_HAS_RVALUE_REFERENCES
-  DirectoryEntry(std::string Name, std::vector<Entry *> Contents, Status S)
-      : Entry(EK_Directory, std::move(Name)), Contents(std::move(Contents)),
+  DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S)
+      : Entry(EK_Directory, Name), Contents(std::move(Contents)),
         S(std::move(S)) {}
 #endif
   DirectoryEntry(StringRef Name, ArrayRef<Entry *> Contents, const Status &S)
@@ -259,11 +256,6 @@ class FileEntry : public Entry {
   std::string ExternalContentsPath;
 
 public:
-#if LLVM_HAS_RVALUE_REFERENCES
-  FileEntry(std::string Name, std::string ExternalContentsPath)
-      : Entry(EK_File, std::move(Name)),
-        ExternalContentsPath(std::move(ExternalContentsPath)) {}
-#endif
   FileEntry(StringRef Name, StringRef ExternalContentsPath)
       : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath) {}
   StringRef getExternalContentsPath() const { return ExternalContentsPath; }
@@ -318,8 +310,9 @@ public:
 ///
 /// and inherit their attributes from the external contents.
 ///
-/// In both cases, the 'name' field must be a single path component (containing
-/// no separators).
+/// In both cases, the 'name' field may contain multiple path components (e.g.
+/// /path/to/file). However, any directory that contains more than one child
+/// must be uniquely represented by a directory entry.
 class VFSFromYAML : public vfs::FileSystem {
   std::vector<Entry *> Roots; ///< The root(s) of the virtual file system.
   /// \brief The file system to use for external references.
@@ -482,10 +475,6 @@ class VFSFromYAMLParser {
         if (!parseScalarString(I->getValue(), Value, Buffer))
           return NULL;
         Name = Value;
-        if (sys::path::has_parent_path(Name)) {
-          error(I->getValue(), "unexpected path separator in name");
-          return NULL;
-        }
       } else if (Key == "type") {
         if (!parseScalarString(I->getValue(), Value, Buffer))
           return NULL;
@@ -546,16 +535,38 @@ class VFSFromYAMLParser {
     if (!checkMissingKeys(N, Keys))
       return NULL;
 
+    // Remove trailing slash(es)
+    StringRef Trimmed(Name);
+    while (Trimmed.size() > 1 && sys::path::is_separator(Trimmed.back()))
+      Trimmed = Trimmed.slice(0, Trimmed.size()-1);
+    // Get the last component
+    StringRef LastComponent = sys::path::filename(Trimmed);
+
+    Entry *Result = 0;
     switch (Kind) {
     case EK_File:
-      return new FileEntry(llvm_move(Name), llvm_move(ExternalContentsPath));
+      Result = new FileEntry(LastComponent, llvm_move(ExternalContentsPath));
+      break;
     case EK_Directory:
-      return new DirectoryEntry(
-          llvm_move(Name), llvm_move(EntryArrayContents),
+      Result = new DirectoryEntry(LastComponent, llvm_move(EntryArrayContents),
+          Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
+                 0, file_type::directory_file, sys::fs::all_all));
+      break;
+    }
+
+    StringRef Parent = sys::path::parent_path(Trimmed);
+    if (Parent.empty())
+      return Result;
+
+    // if 'name' contains multiple components, create implicit directory entries
+    for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),
+                                     E = sys::path::rend(Parent);
+         I != E; ++I) {
+      Result = new DirectoryEntry(*I, Result,
           Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
                  0, file_type::directory_file, sys::fs::all_all));
     }
-    llvm_unreachable("unknown EntryKind in switch");
+    return Result;
   }
 
 public:
index de09cfbd52017d7c3e13539fce1ec53b32f8f262..334b1a45ddf78dc821e29305864603bb5bf8d5af 100644 (file)
@@ -466,3 +466,64 @@ TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
   EXPECT_EQ(NULL, FS.getPtr());
   EXPECT_EQ(24, NumDiagnostics);
 }
+
+TEST_F(VFSFromYAMLTest, MultiComponentPath) {
+  IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
+  Lower->addRegularFile("/other");
+
+  // file in roots
+  IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
+      "{ 'roots': [\n"
+      "  { 'type': 'file', 'name': '/path/to/file',\n"
+      "    'external-contents': '/other' }]\n"
+      "}", Lower);
+  ASSERT_TRUE(NULL != FS.getPtr());
+  EXPECT_EQ(errc::success, FS->status("/path/to/file").getError());
+  EXPECT_EQ(errc::success, FS->status("/path/to").getError());
+  EXPECT_EQ(errc::success, FS->status("/path").getError());
+  EXPECT_EQ(errc::success, FS->status("/").getError());
+
+  // at the start
+  FS = getFromYAMLString(
+      "{ 'roots': [\n"
+      "  { 'type': 'directory', 'name': '/path/to',\n"
+      "    'contents': [ { 'type': 'file', 'name': 'file',\n"
+      "                    'external-contents': '/other' }]}]\n"
+      "}", Lower);
+  ASSERT_TRUE(NULL != FS.getPtr());
+  EXPECT_EQ(errc::success, FS->status("/path/to/file").getError());
+  EXPECT_EQ(errc::success, FS->status("/path/to").getError());
+  EXPECT_EQ(errc::success, FS->status("/path").getError());
+  EXPECT_EQ(errc::success, FS->status("/").getError());
+
+  // at the end
+  FS = getFromYAMLString(
+      "{ 'roots': [\n"
+      "  { 'type': 'directory', 'name': '/',\n"
+      "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
+      "                    'external-contents': '/other' }]}]\n"
+      "}", Lower);
+  ASSERT_TRUE(NULL != FS.getPtr());
+  EXPECT_EQ(errc::success, FS->status("/path/to/file").getError());
+  EXPECT_EQ(errc::success, FS->status("/path/to").getError());
+  EXPECT_EQ(errc::success, FS->status("/path").getError());
+  EXPECT_EQ(errc::success, FS->status("/").getError());
+}
+
+TEST_F(VFSFromYAMLTest, TrailingSlashes) {
+  IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
+  Lower->addRegularFile("/other");
+
+  // file in roots
+  IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
+      "{ 'roots': [\n"
+      "  { 'type': 'directory', 'name': '/path/to////',\n"
+      "    'contents': [ { 'type': 'file', 'name': 'file',\n"
+      "                    'external-contents': '/other' }]}]\n"
+      "}", Lower);
+  ASSERT_TRUE(NULL != FS.getPtr());
+  EXPECT_EQ(errc::success, FS->status("/path/to/file").getError());
+  EXPECT_EQ(errc::success, FS->status("/path/to").getError());
+  EXPECT_EQ(errc::success, FS->status("/path").getError());
+  EXPECT_EQ(errc::success, FS->status("/").getError());
+}