diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -355,11 +355,13 @@ /// @name Driver Steps /// @{ - /// ParseDriverMode - Look for and handle the driver mode option in Args. + /// Look for and handle the VFS mode option in Args. + void ParseVFSMode(ArrayRef Args); + + /// Look for and handle the driver mode option in Args. void ParseDriverMode(StringRef ProgramName, ArrayRef Args); - /// ParseArgStrings - Parse the given list of strings into an - /// ArgList. + /// Parse the given list of strings into an ArgList. llvm::opt::InputArgList ParseArgStrings(ArrayRef Args, bool IsClCompatMode, bool &ContainsError); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2007,6 +2007,9 @@ Flags<[CC1Option]>; def ivfsoverlay : JoinedOrSeparate<["-"], "ivfsoverlay">, Group, Flags<[CC1Option]>, HelpText<"Overlay the virtual filesystem described by file over the real file system">; +def ivfsmode : Joined<["-"], "ivfsmode=">, Group, Flags<[CC1Option, HelpHidden]>, + HelpText<"Use the virtual file system in 'real' mode, or 'physical' mode. In 'real' mode the working directory is linked to the process' working directory. In 'physical' mode it has its own working directory, independent of (but initially equal to) that of the process.">, + Values<"real,physical">; def imultilib : Separate<["-"], "imultilib">, Group; def keep__private__externs : Flag<["-"], "keep_private_externs">; def l : JoinedOrSeparate<["-"], "l">, Flags<[LinkerInput, RenderJoined]>, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -131,10 +131,6 @@ CCCGenericGCCName(""), Saver(Alloc), CheckInputsExist(true), GenReproducer(false), SuppressMissingInputWarning(false) { - // Provide a sane fallback if no VFS is specified. - if (!this->VFS) - this->VFS = llvm::vfs::createPhysicalFileSystem().release(); - Name = llvm::sys::path::filename(ClangExecutable); Dir = llvm::sys::path::parent_path(ClangExecutable); InstalledDir = Dir; // Provide a sensible default installed dir. @@ -150,6 +146,42 @@ ResourceDir = GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR); } +void Driver::ParseVFSMode(ArrayRef Args) { + const std::string OptionPrefix = + getOpts().getOption(options::OPT_ivfsmode).getPrefixedName(); + enum class VFSMode { Unknown, Real, Physical } Mode = VFSMode::Unknown; + for (const char *ArgPtr : Args) { + if (!ArgPtr) + continue; + const StringRef Arg = ArgPtr; + if (!Arg.startswith(OptionPrefix)) + continue; + const StringRef Value = Arg.drop_front(OptionPrefix.size()); + VFSMode M = llvm::StringSwitch(Value) + .Case("real", VFSMode::Real) + .Case("physical", VFSMode::Physical) + .Default(VFSMode::Unknown); + if (M == VFSMode::Unknown) { + Diag(diag::err_drv_unsupported_option_argument) << OptionPrefix << Value; + break; + } + Mode = M; + } + + switch (Mode) { + case VFSMode::Unknown: + if (!this->VFS) + this->VFS = llvm::vfs::createPhysicalFileSystem().release(); + break; + case VFSMode::Physical: + this->VFS = llvm::vfs::createPhysicalFileSystem().release(); + break; + case VFSMode::Real: + this->VFS = llvm::vfs::getRealFileSystem().get(); + break; + } +} + void Driver::ParseDriverMode(StringRef ProgramName, ArrayRef Args) { if (ClangNameParts.isEmpty()) @@ -946,6 +978,10 @@ } } + // We might try accessing the VFS fairly early, so make sure we have the + // right one. + ParseVFSMode(ArgList.slice(1)); + // We look for the driver mode option early, because the mode can affect // how other options are parsed. ParseDriverMode(ClangExecutable, ArgList.slice(1)); diff --git a/clang/test/Driver/vfsmode.py b/clang/test/Driver/vfsmode.py new file mode 100644 --- /dev/null +++ b/clang/test/Driver/vfsmode.py @@ -0,0 +1,49 @@ +# Check that we can change the VFS mode between 'real' and 'physical', which +# affects the maximum path length that clang can deal with. + +# UNSUPPORTED: system-windows + +# RUN: rm -rf xxx* +# RUN: python %s > %t +# RUN: cat %t | xargs not %clang 2>&1 | FileCheck %s --check-prefix=PHYSICAL +# RUN: cat %t | xargs not %clang -ivfsmode=physical 2>&1 | FileCheck %s --check-prefix=PHYSICAL +# RUN: cat %t | xargs %clang -ivfsmode=real + +# PHYSICAL: error: no such file or directory: + +import os +import subprocess + +current = os.path.realpath(os.curdir) +name_max = int(subprocess.check_output("getconf NAME_MAX %s" % current, shell=True)) +path_max = int(subprocess.check_output("getconf PATH_MAX %s" % current, shell=True)) +assert name_max > 0, 'we want to test NAME_MAX properties, knowing its value is required' +assert path_max > 0, 'we want to test PATH_MAX properties, knowing its value is required' + +filename = 'o.c' +code = 'int main() { return 0; }\n' + +relative = '' +absolute = current + +# Create directories which almost, but not quite, exhaust PATH_MAX. +target_len = path_max - 1 +while len(absolute) < target_len: + new_name_len = min(target_len - len(absolute) - 1, name_max) + absolute = os.path.join(absolute, 'x' * new_name_len) + relative = os.path.join(relative, 'x' * new_name_len) + os.mkdir(relative) + assert os.path.exists(absolute), 'creating a directory must succeed' + assert os.path.exists(relative), 'creating a directory must succeed' + +# Create a file whose relative path doesn't exceed PATH_MAX, but whose absolute +# path does. +file_relative_path = os.path.join(relative, filename) +with open(file_relative_path, 'w') as f: + f.write(code) + +assert os.path.exists(file_relative_path), 'the relative path should work because it is still smaller than PATH_MAX' +assert not os.path.exists(os.path.join(absolute, filename)), 'the absolute path should exceed PATH_MAX' + +# Print out the relative path so lit can pass it to clang. +print file_relative_path