diff --git a/clang/test/VFS/Inputs/root-relative-overlay.yaml b/clang/test/VFS/Inputs/root-relative-overlay.yaml new file mode 100644 --- /dev/null +++ b/clang/test/VFS/Inputs/root-relative-overlay.yaml @@ -0,0 +1,11 @@ +{ + 'version': 0, + 'case-sensitive': false, + 'overlay-relative': OVERLAY_DIR, + 'root-relative': 'overlay-dir', + 'roots': [ + { 'name': 'not_real.h', 'type': 'file', + 'external-contents': 'EXTERNAL_DIR/actual_header.h' + }, + ] +} diff --git a/clang/test/VFS/relative-path.c b/clang/test/VFS/relative-path.c --- a/clang/test/VFS/relative-path.c +++ b/clang/test/VFS/relative-path.c @@ -3,6 +3,13 @@ // RUN: sed -e "s@INPUT_DIR@%{/S:regex_replacement}/Inputs@g" -e "s@OUT_DIR@%{/t:regex_replacement}@g" %S/Inputs/vfsoverlay.yaml > %t.yaml // RUN: %clang_cc1 -Werror -I . -ivfsoverlay %t.yaml -fsyntax-only %s +// RUN: cp %S/Inputs/actual_header.h %t/actual_header.h +// RUN: sed -e "s@OVERLAY_DIR@true@g" -e "s@EXTERNAL_DIR@.@g" %S/Inputs/root-relative-overlay.yaml > %t/root-relative-overlay.yaml +// RUN: %clang_cc1 -Werror -I %t -ivfsoverlay %t/root-relative-overlay.yaml -fsyntax-only %s + +// RUN: sed -e "s@OVERLAY_DIR@false@g" -e "s@EXTERNAL_DIR@%{/t:regex_replacement}@g" %S/Inputs/root-relative-overlay.yaml > %t/root-relative-overlay2.yaml +// RUN: %clang_cc1 -Werror -I %t -ivfsoverlay %t/root-relative-overlay2.yaml -fsyntax-only %s + #include "not_real.h" void foo(void) { diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h --- a/llvm/include/llvm/Support/VirtualFileSystem.h +++ b/llvm/include/llvm/Support/VirtualFileSystem.h @@ -651,17 +651,28 @@ /// \endverbatim /// /// The roots may be absolute or relative. If relative they will be made -/// absolute against the current working directory. +/// absolute against either current working directory or the directory where +/// the Overlay YAML file is located, depending on the 'root-relative' +/// configuration. /// /// All configuration options are optional. /// 'case-sensitive': /// 'use-external-names': +/// 'root-relative': /// 'overlay-relative': /// 'fallthrough': /// 'redirecting-with': /// +/// To clarify, 'root-relative' option will prepend the current working +/// directory, or the overlay directory to the 'roots->name' field only if +/// 'roots->name' is a relative path. On the other hand, when 'overlay-relative' +/// is set to 'true', external paths will always be prepended with the overlay +/// directory, even if external paths are not relative paths. The +/// 'root-relative' option has no interaction with the 'overlay-relative' +/// option. +/// /// Virtual directories that list their contents are represented as /// \verbatim /// { @@ -745,6 +756,15 @@ RedirectOnly }; + /// The type of relative path used by Roots. + enum class RootRelativeKind { + /// The roots are relative to the current working directory. + CWD, + /// The roots are relative to the directory where the Overlay YAML file + // locates. + OverlayDir + }; + /// A single file or directory in the VFS. class Entry { EntryKind Kind; @@ -910,10 +930,11 @@ /// The file system to use for external references. IntrusiveRefCntPtr ExternalFS; - /// If IsRelativeOverlay is set, this represents the directory - /// path that should be prefixed to each 'external-contents' entry - /// when reading from YAML files. - std::string ExternalContentsPrefixDir; + /// This represents the directory path that the YAML file is located. + /// This will be prefixed to each 'external-contents' if IsRelativeOverlay + /// is set. This will also be prefixed to each 'roots->name' if RootRelative + /// is set to RootRelativeKind::OverlayDir and the path is relative. + std::string OverlayFileDir; /// @name Configuration /// @{ @@ -923,7 +944,7 @@ /// Currently, case-insensitive matching only works correctly with ASCII. bool CaseSensitive = is_style_posix(sys::path::Style::native); - /// IsRelativeOverlay marks whether a ExternalContentsPrefixDir path must + /// IsRelativeOverlay marks whether a OverlayFileDir path must /// be prefixed in every 'external-contents' when reading from YAML files. bool IsRelativeOverlay = false; @@ -934,6 +955,10 @@ /// Determines the lookups to perform, as well as their order. See /// \c RedirectKind for details. RedirectKind Redirection = RedirectKind::Fallthrough; + + /// Determine the prefix directory if the roots are relative paths. See + /// \c RootRelativeKind for details. + RootRelativeKind RootRelative = RootRelativeKind::CWD; /// @} RedirectingFileSystem(IntrusiveRefCntPtr ExternalFS); @@ -982,11 +1007,14 @@ std::error_code makeAbsolute(SmallVectorImpl &Path) const override; + std::error_code makeAbsolute(StringRef WorkingDir, + SmallVectorImpl &Path) const; + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; - void setExternalContentsPrefixDir(StringRef PrefixDir); + void setOverlayFileDir(StringRef PrefixDir); - StringRef getExternalContentsPrefixDir() const; + StringRef getOverlayFileDir() const; /// Sets the redirection kind to \c Fallthrough if true or \c RedirectOnly /// otherwise. Will removed in the future, use \c setRedirection instead. diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -1356,22 +1356,32 @@ if (!WorkingDir) return WorkingDir.getError(); + return makeAbsolute(WorkingDir.get(), Path); +} + +std::error_code +RedirectingFileSystem::makeAbsolute(StringRef WorkingDir, + SmallVectorImpl &Path) const { // We can't use sys::fs::make_absolute because that assumes the path style // is native and there is no way to override that. Since we know WorkingDir // is absolute, we can use it to determine which style we actually have and // append Path ourselves. + if (!sys::path::is_absolute(WorkingDir, sys::path::Style::posix) && + !sys::path::is_absolute(WorkingDir, + sys::path::Style::windows_backslash)) { + return std::error_code(); + } sys::path::Style style = sys::path::Style::windows_backslash; - if (sys::path::is_absolute(WorkingDir.get(), sys::path::Style::posix)) { + if (sys::path::is_absolute(WorkingDir, sys::path::Style::posix)) { style = sys::path::Style::posix; } else { // Distinguish between windows_backslash and windows_slash; getExistingStyle // returns posix for a path with windows_slash. - if (getExistingStyle(WorkingDir.get()) != - sys::path::Style::windows_backslash) + if (getExistingStyle(WorkingDir) != sys::path::Style::windows_backslash) style = sys::path::Style::windows_slash; } - std::string Result = WorkingDir.get(); + std::string Result = std::string(WorkingDir); StringRef Dir(Result); if (!Dir.endswith(sys::path::get_separator(style))) { Result += sys::path::get_separator(style); @@ -1482,12 +1492,12 @@ return Combined; } -void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { - ExternalContentsPrefixDir = PrefixDir.str(); +void RedirectingFileSystem::setOverlayFileDir(StringRef Dir) { + OverlayFileDir = Dir.str(); } -StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const { - return ExternalContentsPrefixDir; +StringRef RedirectingFileSystem::getOverlayFileDir() const { + return OverlayFileDir; } void RedirectingFileSystem::setFallthrough(bool Fallthrough) { @@ -1621,6 +1631,20 @@ return None; } + Optional + parseRootRelativeKind(yaml::Node *N) { + SmallString<12> Storage; + StringRef Value; + if (!parseScalarString(N, Value, Storage)) + return None; + if (Value.equals_insensitive("cwd")) { + return RedirectingFileSystem::RootRelativeKind::CWD; + } else if (Value.equals_insensitive("overlay-dir")) { + return RedirectingFileSystem::RootRelativeKind::OverlayDir; + } + return None; + } + struct KeyStatus { bool Required; bool Seen = false; @@ -1828,7 +1852,7 @@ SmallString<256> FullPath; if (FS->IsRelativeOverlay) { - FullPath = FS->getExternalContentsPrefixDir(); + FullPath = FS->getOverlayFileDir(); assert(!FullPath.empty() && "External contents prefix directory must exist"); llvm::sys::path::append(FullPath, Value); @@ -1884,10 +1908,25 @@ } else if (sys::path::is_absolute(Name, sys::path::Style::windows_backslash)) { path_style = sys::path::Style::windows_backslash; + } else if (FS->RootRelative == + RedirectingFileSystem::RootRelativeKind::OverlayDir) { + SmallString<256> FullPath = FS->getOverlayFileDir(); + assert(!FullPath.empty() && "Overlay file directory must exist"); + auto EC = FS->makeAbsolute(FS->getOverlayFileDir(), Name); + if (EC) { + error( + NameValueNode, + "entry with relative path at the root level is not discoverable"); + return nullptr; + } + Name = canonicalize(Name); + path_style = sys::path::is_absolute(Name, sys::path::Style::posix) + ? sys::path::Style::posix + : sys::path::Style::windows_backslash; } else { // Relative VFS root entries are made absolute to the current working // directory, then we can determine the path style from that. - auto EC = sys::fs::make_absolute(Name); + auto EC = FS->makeAbsolute(Name); if (EC) { assert(NameValueNode && "Name presence should be checked earlier"); error( @@ -1962,6 +2001,7 @@ KeyStatusPair("version", true), KeyStatusPair("case-sensitive", false), KeyStatusPair("use-external-names", false), + KeyStatusPair("root-relative", false), KeyStatusPair("overlay-relative", false), KeyStatusPair("fallthrough", false), KeyStatusPair("redirecting-with", false), @@ -2051,6 +2091,13 @@ error(I.getValue(), "expected valid redirect kind"); return false; } + } else if (Key == "root-relative") { + if (auto Kind = parseRootRelativeKind(I.getValue())) { + FS->RootRelative = *Kind; + } else { + error(I.getValue(), "expected valid root-relative kind"); + return false; + } } else { llvm_unreachable("key missing from Keys"); } @@ -2100,13 +2147,13 @@ // Example: // -ivfsoverlay dummy.cache/vfs/vfs.yaml // yields: - // FS->ExternalContentsPrefixDir => //dummy.cache/vfs + // FS->OverlayFileDir => //dummy.cache/vfs // SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); assert(!EC && "Overlay dir final path must be absolute"); (void)EC; - FS->setExternalContentsPrefixDir(OverlayAbsDir); + FS->setOverlayFileDir(OverlayAbsDir); } if (!P.parse(Root, FS.get())) diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -1452,18 +1452,20 @@ std::unique_ptr getFromYAMLRawString(StringRef Content, - IntrusiveRefCntPtr ExternalFS) { + IntrusiveRefCntPtr ExternalFS, + StringRef YAMLFilePath = "") { std::unique_ptr Buffer = MemoryBuffer::getMemBuffer(Content); - return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, - ExternalFS); + return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, YAMLFilePath, + this, ExternalFS); } std::unique_ptr getFromYAMLString( StringRef Content, - IntrusiveRefCntPtr ExternalFS = new DummyFileSystem()) { + IntrusiveRefCntPtr ExternalFS = new DummyFileSystem(), + StringRef YAMLFilePath = "") { std::string VersionPlusContent("{\n 'version':0,\n"); VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); - return getFromYAMLRawString(VersionPlusContent, ExternalFS); + return getFromYAMLRawString(VersionPlusContent, ExternalFS, YAMLFilePath); } // This is intended as a "XFAIL" for windows hosts. @@ -1849,6 +1851,45 @@ EXPECT_EQ(0, NumDiagnostics); } +TEST_F(VFSFromYAMLTest, RootRelativeTest) { + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + Lower->addDirectory("//root/foo/bar"); + Lower->addRegularFile("//root/foo/bar/a"); + IntrusiveRefCntPtr FS = + getFromYAMLString("{\n" + " 'case-sensitive': false,\n" + " 'root-relative': 'overlay-dir',\n" + " 'roots': [\n" + " { 'name': 'b', 'type': 'file',\n" + " 'external-contents': '//root/foo/bar/a'\n" + " }\n" + " ]\n" + "}", + Lower, "//root/foo/bar/overlay"); + + ASSERT_NE(FS.get(), nullptr); + ErrorOr S = FS->status("//root/foo/bar/b"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("//root/foo/bar/a", S->getName()); + + IntrusiveRefCntPtr FSOverlayRelative = + getFromYAMLString("{\n" + " 'case-sensitive': false,\n" + " 'overlay-relative': true,\n" + " 'root-relative': 'overlay-dir',\n" + " 'roots': [\n" + " { 'name': 'b', 'type': 'file',\n" + " 'external-contents': 'a'\n" + " }\n" + " ]\n" + "}", + Lower, "//root/foo/bar/overlay"); + ASSERT_NE(FSOverlayRelative.get(), nullptr); + ErrorOr SOverlayRelative = FS->status("//root/foo/bar/b"); + ASSERT_FALSE(SOverlayRelative.getError()); + EXPECT_EQ("//root/foo/bar/a", SOverlayRelative->getName()); +} + TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) { IntrusiveRefCntPtr BaseFS( new vfs::InMemoryFileSystem);