Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -751,6 +751,8 @@ HelpText<"Disable the module hash">; def fmodules_hash_content : Flag<["-"], "fmodules-hash-content">, HelpText<"Enable hashing the content of a module file">; +def fno_absolute_module_directory : Flag<["-"], "fno-absolute-module-directory">, + HelpText<"Do not force module home directory path to be absolute.">; def c_isystem : JoinedOrSeparate<["-"], "c-isystem">, MetaVarName<"">, HelpText<"Add directory to the C SYSTEM include search path">; def objc_isystem : JoinedOrSeparate<["-"], "objc-isystem">, Index: include/clang/Lex/HeaderSearchOptions.h =================================================================== --- include/clang/Lex/HeaderSearchOptions.h +++ include/clang/Lex/HeaderSearchOptions.h @@ -203,6 +203,9 @@ unsigned ModulesHashContent : 1; + /// Do not absolutify the path to the module directory. + unsigned NoAbsoluteModuleDirectory : 1; + HeaderSearchOptions(StringRef _Sysroot = "/") : Sysroot(_Sysroot), ModuleFormat("raw"), DisableModuleHash(false), ImplicitModuleMaps(false), ModuleMapFileHomeIsCwd(false), @@ -210,7 +213,8 @@ UseStandardCXXIncludes(true), UseLibcxx(false), Verbose(false), ModulesValidateOncePerBuildSession(false), ModulesValidateSystemHeaders(false), UseDebugInfo(false), - ModulesValidateDiagnosticOptions(true), ModulesHashContent(false) {} + ModulesValidateDiagnosticOptions(true), ModulesHashContent(false), + NoAbsoluteModuleDirectory(false) {} /// AddPath - Add the \p Path path to the specified \p Group list. void AddPath(StringRef Path, frontend::IncludeDirGroup Group, Index: include/clang/Serialization/ASTReader.h =================================================================== --- include/clang/Serialization/ASTReader.h +++ include/clang/Serialization/ASTReader.h @@ -2238,6 +2238,7 @@ // Skip a path static void SkipPath(const RecordData &Record, unsigned &Idx) { SkipString(Record, Idx); + Idx++; // Relative } /// Read a version tuple. Index: include/clang/Serialization/ASTWriter.h =================================================================== --- include/clang/Serialization/ASTWriter.h +++ include/clang/Serialization/ASTWriter.h @@ -640,13 +640,14 @@ /// Convert a path from this build process into one that is appropriate /// for emission in the module file. - bool PreparePathForOutput(SmallVectorImpl &Path); + bool PreparePathForOutput(SmallVectorImpl &Path, + bool &IsRelativeModuleDirectory); /// Add a path to the given record. void AddPath(StringRef Path, RecordDataImpl &Record); /// Emit the current record with the given path as a blob. - void EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, + void EmitRecordWithPath(unsigned Abbrev, RecordDataImpl &Record, StringRef Path); /// Add a version tuple to the given record Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1770,6 +1770,7 @@ Opts.AddPrebuiltModulePath(A->getValue()); Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); Opts.ModulesHashContent = Args.hasArg(OPT_fmodules_hash_content); + Opts.NoAbsoluteModuleDirectory = Args.hasArg(OPT_fno_absolute_module_directory); Opts.ModulesValidateDiagnosticOptions = !Args.hasArg(OPT_fmodules_disable_diagnostic_validation); Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps); Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -1702,6 +1702,7 @@ HeaderFileInfoTrait::GetInternalKey(const FileEntry *FE) { internal_key_type ikey = {FE->getSize(), M.HasTimestamps ? FE->getModificationTime() : 0, + /*IsRelativeModuleDirectory*/ false, FE->getName(), /*Imported*/ false}; return ikey; } @@ -1720,7 +1721,8 @@ return FileMgr.getFile(Key.Filename); std::string Resolved = Key.Filename; - Reader.ResolveImportedPath(M, Resolved); + if (Key.IsRelativeModuleDirectory) + Reader.ResolveImportedPath(M, Resolved); return FileMgr.getFile(Resolved); }; @@ -1745,6 +1747,7 @@ internal_key_type ikey; ikey.Size = off_t(endian::readNext(d)); ikey.ModTime = time_t(endian::readNext(d)); + ikey.IsRelativeModuleDirectory = bool(endian::readNext(d)); ikey.Filename = (const char *)d; ikey.Imported = true; return ikey; @@ -1793,7 +1796,7 @@ Reader.getPreprocessor().getHeaderSearchInfo().getModuleMap(); std::string Filename = key.Filename; - if (key.Imported) + if (key.Imported && key.IsRelativeModuleDirectory) Reader.ResolveImportedPath(M, Filename); // FIXME: This is not always the right filename-as-written, but we're not // going to use this information to rebuild the module, so it doesn't make @@ -2088,8 +2091,11 @@ R.Overridden = static_cast(Record[3]); R.Transient = static_cast(Record[4]); R.TopLevelModuleMap = static_cast(Record[5]); + bool IsRelativeModuleDirectory = static_cast(Record[6]); R.Filename = Blob; - ResolveImportedPath(F, R.Filename); + if (IsRelativeModuleDirectory) { + ResolveImportedPath(F, R.Filename); + } return R; } @@ -2594,12 +2600,15 @@ break; } - case ORIGINAL_FILE: + case ORIGINAL_FILE: { F.OriginalSourceFileID = FileID::get(Record[0]); + bool IsRelativeModuleDirectory = static_cast(Record[1]); F.ActualOriginalSourceFileName = Blob; F.OriginalSourceFileName = F.ActualOriginalSourceFileName; - ResolveImportedPath(F, F.OriginalSourceFileName); + if (IsRelativeModuleDirectory) + ResolveImportedPath(F, F.OriginalSourceFileName); break; + } case ORIGINAL_FILE_ID: F.OriginalSourceFileID = FileID::get(Record[0]); @@ -4842,8 +4851,10 @@ switch ((InputFileRecordTypes)Cursor.readRecord(Code, Record, &Blob)) { case INPUT_FILE: bool Overridden = static_cast(Record[3]); + bool IsRelativeModuleDirectory = static_cast(Record[4]); std::string Filename = Blob; - ResolveImportedPath(Filename, ModuleDir); + if (IsRelativeModuleDirectory) + ResolveImportedPath(Filename, ModuleDir); shouldContinue = Listener.visitInputFile( Filename, isSystemFile, Overridden, /*IsExplicitModule*/false); break; @@ -9086,7 +9097,9 @@ std::string ASTReader::ReadPath(ModuleFile &F, const RecordData &Record, unsigned &Idx) { std::string Filename = ReadString(Record, Idx); - ResolveImportedPath(F, Filename); + bool IsRelativeModuleDirectory = Record[Idx++]; + if (IsRelativeModuleDirectory) + ResolveImportedPath(F, Filename); return Filename; } Index: lib/Serialization/ASTReaderInternals.h =================================================================== --- lib/Serialization/ASTReaderInternals.h +++ lib/Serialization/ASTReaderInternals.h @@ -254,6 +254,7 @@ struct internal_key_type { off_t Size; time_t ModTime; + bool IsRelativeModuleDirectory; StringRef Filename; bool Imported; }; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -1327,9 +1327,14 @@ /// /// \return \c true if the path was changed. static bool cleanPathForOutput(FileManager &FileMgr, - SmallVectorImpl &Path) { - bool Changed = FileMgr.makeAbsolutePath(Path); - return Changed | llvm::sys::path::remove_dots(Path); + SmallVectorImpl &Path, + bool MakeAbsolute = true) { + bool Changed = false; + if (MakeAbsolute) { + Changed |= FileMgr.makeAbsolutePath(Path); + } + Changed |= llvm::sys::path::remove_dots(Path); + return Changed; } /// Adjusts the given filename to only write out the portion of the @@ -1493,7 +1498,10 @@ if (WritingModule && WritingModule->Directory) { SmallString<128> BaseDir(WritingModule->Directory->getName()); - cleanPathForOutput(Context.getSourceManager().getFileManager(), BaseDir); + cleanPathForOutput(Context.getSourceManager().getFileManager(), BaseDir, + !PP.getHeaderSearchInfo() + .getHeaderSearchOpts() + .NoAbsoluteModuleDirectory); // If the home of the module is the current working directory, then we // want to pick up the cwd of the build process loading the module, not @@ -1708,6 +1716,7 @@ auto FileAbbrev = std::make_shared(); FileAbbrev->Add(BitCodeAbbrevOp(ORIGINAL_FILE)); FileAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File ID + FileAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Relative FileAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name unsigned FileAbbrevCode = Stream.EmitAbbrev(std::move(FileAbbrev)); @@ -1772,6 +1781,7 @@ IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Overridden IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Transient IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Module map + IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Relative IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name unsigned IFAbbrevCode = Stream.EmitAbbrev(std::move(IFAbbrev)); @@ -1822,7 +1832,7 @@ // Emit size/modification time for this file. // And whether this file was overridden. - RecordData::value_type Record[] = { + RecordData Record = { INPUT_FILE, InputFileOffsets.size(), (uint64_t)Entry.File->getSize(), @@ -1937,6 +1947,7 @@ StringRef Filename; off_t Size; time_t ModTime; + bool IsRelativeModuleDirectory; }; using key_type_ref = const key_type &; @@ -1965,7 +1976,7 @@ using namespace llvm::support; endian::Writer LE(Out, little); - unsigned KeyLen = key.Filename.size() + 1 + 8 + 8; + unsigned KeyLen = key.Filename.size() + 1 + 8 + 8 + 1; LE.write(KeyLen); unsigned DataLen = 1 + 2 + 4 + 4; for (auto ModInfo : Data.KnownHeaders) @@ -1985,6 +1996,8 @@ KeyLen -= 8; LE.write(key.ModTime); KeyLen -= 8; + LE.write(key.IsRelativeModuleDirectory); + KeyLen -= 1; Out.write(key.Filename.data(), KeyLen); } @@ -2091,13 +2104,15 @@ // Form the effective relative pathname for the file. SmallString<128> Filename(M->Directory->getName()); llvm::sys::path::append(Filename, U.FileName); - PreparePathForOutput(Filename); + bool IsRelativeModuleDirectory = false; + PreparePathForOutput(Filename, IsRelativeModuleDirectory); StringRef FilenameDup = strdup(Filename.c_str()); SavedStrings.push_back(FilenameDup.data()); HeaderFileInfoTrait::key_type Key = { - FilenameDup, *U.Size, IncludeTimestamps ? *U.ModTime : 0 + FilenameDup, *U.Size, IncludeTimestamps ? *U.ModTime : 0, + IsRelativeModuleDirectory, }; HeaderFileInfoTrait::data_type Data = { Empty, {}, {M, ModuleMap::headerKindToRole(U.Kind)} @@ -2137,7 +2152,8 @@ // Massage the file path into an appropriate form. StringRef Filename = File->getName(); SmallString<128> FilenameTmp(Filename); - if (PreparePathForOutput(FilenameTmp)) { + bool IsRelativeModuleDirectory = false; + if (PreparePathForOutput(FilenameTmp, IsRelativeModuleDirectory)) { // If we performed any translation on the file name at all, we need to // save this string, since the generator will refer to it later. Filename = StringRef(strdup(FilenameTmp.c_str())); @@ -2145,7 +2161,8 @@ } HeaderFileInfoTrait::key_type Key = { - Filename, File->getSize(), getTimestampForOutput(File) + Filename, File->getSize(), getTimestampForOutput(File), + IsRelativeModuleDirectory, }; HeaderFileInfoTrait::data_type Data = { *HFI, HS.getModuleMap().findAllModulesForHeader(File), {} @@ -4504,11 +4521,13 @@ Record.insert(Record.end(), Str.begin(), Str.end()); } -bool ASTWriter::PreparePathForOutput(SmallVectorImpl &Path) { +bool ASTWriter::PreparePathForOutput(SmallVectorImpl &Path, + bool &IsRelativeModuleDirectory) { assert(Context && "should have context when outputting path"); bool Changed = - cleanPathForOutput(Context->getSourceManager().getFileManager(), Path); + cleanPathForOutput(Context->getSourceManager().getFileManager(), Path, + llvm::sys::path::is_absolute(BaseDirectory)); // Remove a prefix to make the path relative, if relevant. const char *PathBegin = Path.data(); @@ -4517,6 +4536,9 @@ if (PathPtr != PathBegin) { Path.erase(Path.begin(), Path.begin() + (PathPtr - PathBegin)); Changed = true; + IsRelativeModuleDirectory = true; + } else { + IsRelativeModuleDirectory = false; } return Changed; @@ -4524,14 +4546,18 @@ void ASTWriter::AddPath(StringRef Path, RecordDataImpl &Record) { SmallString<128> FilePath(Path); - PreparePathForOutput(FilePath); + bool IsRelativeModuleDirectory = false; + PreparePathForOutput(FilePath, IsRelativeModuleDirectory); AddString(FilePath, Record); + Record.push_back(IsRelativeModuleDirectory); } -void ASTWriter::EmitRecordWithPath(unsigned Abbrev, RecordDataRef Record, +void ASTWriter::EmitRecordWithPath(unsigned Abbrev, RecordDataImpl &Record, StringRef Path) { SmallString<128> FilePath(Path); - PreparePathForOutput(FilePath); + bool IsRelativeModuleDirectory = false; + PreparePathForOutput(FilePath, IsRelativeModuleDirectory); + Record.push_back(IsRelativeModuleDirectory); Stream.EmitRecordWithBlob(Abbrev, Record, FilePath); } Index: lib/Serialization/GlobalModuleIndex.cpp =================================================================== --- lib/Serialization/GlobalModuleIndex.cpp +++ lib/Serialization/GlobalModuleIndex.cpp @@ -628,6 +628,7 @@ SmallString<128> ImportedFile(Record.begin() + Idx, Record.begin() + Idx + Length); Idx += Length; + Idx++; // Relative // Find the imported module file. const FileEntry *DependsOnFile Index: test/Modules/relocatable-modules.modulemap =================================================================== --- /dev/null +++ test/Modules/relocatable-modules.modulemap @@ -0,0 +1,29 @@ +// Build two otherwise identical modules in two different directories and +// verify that using `-fno-absolute-module-directory` makes them identical. +// +// RUN: rm -rf %t +// +// RUN: mkdir -p %t/p1 +// RUN: cd %t/p1 +// RUN: mkdir -p main other +// RUN: grep "" %s > main/a.modulemap +// RUN: grep "" %s > main/a.h +// RUN: grep "" %s > other/b.h +// RUN: %clang_cc1 -x c++ -fmodules -emit-module -fno-absolute-module-directory \ +// RUN: -fmodule-name="a" -Imain -I. -o - main/a.modulemap > a.pcm +// +// RUN: mkdir -p %t/p2 +// RUN: cd %t/p2 +// RUN: mkdir -p main other +// RUN: grep "" %s > main/a.modulemap +// RUN: grep "" %s > main/a.h +// RUN: grep "" %s > other/b.h +// RUN: %clang_cc1 -x c++ -fmodules -emit-module -fno-absolute-module-directory \ +// RUN: -fmodule-name="a" -Imain -I. -o - main/a.modulemap > a.pcm +// +// RUN: diff %t/p1/a.pcm %t/p2/a.pcm + +module "a" { // +} // + +#include "b.h" //