| //===--- IndexUnitWriter.cpp - Index unit serialization -------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Index/IndexUnitWriter.h" |
| #include "IndexDataStoreUtils.h" |
| #include "clang/Basic/FileManager.h" |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/Bitcode/BitstreamWriter.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace clang; |
| using namespace clang::index; |
| using namespace clang::index::store; |
| using namespace llvm; |
| |
| |
| class IndexUnitWriter::PathStorage { |
| std::string WorkDir; |
| std::string SysrootPath; |
| SmallString<512> PathsBuf; |
| StringMap<DirBitPath, BumpPtrAllocator> Dirs; |
| std::vector<FileBitPath> FileBitPaths; |
| DenseMap<const FileEntry *, size_t> FileToIndex; |
| |
| public: |
| PathStorage(StringRef workDir, StringRef sysrootPath) { |
| WorkDir = workDir; |
| if (sysrootPath == "/") |
| sysrootPath = StringRef(); |
| SysrootPath = sysrootPath; |
| } |
| |
| StringRef getPathsBuffer() const { return PathsBuf.str(); } |
| |
| ArrayRef<FileBitPath> getBitPaths() const { return FileBitPaths; } |
| |
| int getPathIndex(const FileEntry *FE) { |
| if (!FE) |
| return -1; |
| auto Pair = FileToIndex.insert(std::make_pair(FE, FileBitPaths.size())); |
| bool IsNew = Pair.second; |
| size_t Index = Pair.first->getSecond(); |
| |
| if (IsNew) { |
| StringRef Filename = sys::path::filename(FE->getName()); |
| DirBitPath Dir = getDirBitPath(sys::path::parent_path(FE->getName())); |
| FileBitPaths.emplace_back(Dir.PrefixKind, Dir.Dir, |
| BitPathComponent(getPathOffset(Filename), |
| Filename.size())); |
| } |
| return Index; |
| } |
| |
| size_t getPathOffset(StringRef Path) { |
| if (Path.empty()) |
| return 0; |
| size_t offset = PathsBuf.size(); |
| PathsBuf += Path; |
| return offset; |
| } |
| |
| private: |
| DirBitPath getDirBitPath(StringRef dirStr) { |
| auto pair = Dirs.insert(std::make_pair(dirStr, DirBitPath())); |
| bool isNew = pair.second; |
| auto &dirPath = pair.first->second; |
| |
| if (isNew) { |
| if (isPathInDir(SysrootPath, dirStr)) { |
| dirPath.PrefixKind = UNIT_PATH_PREFIX_SYSROOT; |
| dirStr = dirStr.drop_front(SysrootPath.size()); |
| while (!dirStr.empty() && dirStr[0] == '/') |
| dirStr = dirStr.drop_front(); |
| } else if (isPathInDir(WorkDir, dirStr)) { |
| dirPath.PrefixKind = UNIT_PATH_PREFIX_WORKDIR; |
| dirStr = dirStr.drop_front(WorkDir.size()); |
| while (!dirStr.empty() && dirStr[0] == '/') |
| dirStr = dirStr.drop_front(); |
| } |
| dirPath.Dir.Offset = getPathOffset(dirStr); |
| dirPath.Dir.Size = dirStr.size(); |
| } |
| return dirPath; |
| } |
| |
| static bool isPathInDir(StringRef dir, StringRef path) { |
| if (dir.empty() || !path.startswith(dir)) |
| return false; |
| StringRef rest = path.drop_front(dir.size()); |
| return !rest.empty() && sys::path::is_separator(rest.front()); |
| } |
| }; |
| |
| IndexUnitWriter::IndexUnitWriter(FileManager &FileMgr, |
| StringRef StorePath, |
| StringRef ProviderIdentifier, |
| StringRef ProviderVersion, |
| StringRef OutputFile, |
| StringRef ModuleName, |
| const FileEntry *MainFile, |
| bool IsSystem, |
| bool IsModuleUnit, |
| bool IsDebugCompilation, |
| StringRef TargetTriple, |
| StringRef SysrootPath, |
| writer::ModuleInfoWriterCallback GetInfoForModule) |
| : FileMgr(FileMgr) { |
| this->UnitsPath = StorePath; |
| store::appendUnitSubDir(this->UnitsPath); |
| this->ProviderIdentifier = ProviderIdentifier; |
| this->ProviderVersion = ProviderVersion; |
| this->OutputFile = OutputFile; |
| this->ModuleName = ModuleName; |
| this->MainFile = MainFile; |
| this->IsSystemUnit = IsSystem; |
| this->IsModuleUnit = IsModuleUnit; |
| this->IsDebugCompilation = IsDebugCompilation; |
| this->TargetTriple = TargetTriple; |
| this->SysrootPath = SysrootPath; |
| this->GetInfoForModuleFn = GetInfoForModule; |
| } |
| |
| IndexUnitWriter::~IndexUnitWriter() {} |
| |
| int IndexUnitWriter::addModule(writer::OpaqueModule Mod) { |
| if (!Mod) |
| return -1; |
| |
| auto Pair = IndexByModule.insert(std::make_pair(Mod, Modules.size())); |
| bool WasInserted = Pair.second; |
| if (WasInserted) { |
| Modules.push_back(Mod); |
| } |
| return Pair.first->second; |
| } |
| |
| int IndexUnitWriter::addFileDependency(const FileEntry *File, bool IsSystem, |
| writer::OpaqueModule Mod) { |
| assert(File); |
| auto Pair = IndexByFile.insert(std::make_pair(File, Files.size())); |
| bool WasInserted = Pair.second; |
| if (WasInserted) { |
| Files.push_back(FileEntryData{File, IsSystem, addModule(Mod), {}}); |
| } |
| return Pair.first->second; |
| } |
| |
| void IndexUnitWriter::addRecordFile(StringRef RecordFile, const FileEntry *File, |
| bool IsSystem, writer::OpaqueModule Mod) { |
| int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1; |
| Records.push_back(RecordOrUnitData{RecordFile, Dep, addModule(Mod), IsSystem}); |
| } |
| |
| void IndexUnitWriter::addASTFileDependency(const FileEntry *File, bool IsSystem, |
| writer::OpaqueModule Mod, |
| bool withoutUnitName) { |
| assert(File); |
| if (!SeenASTFiles.insert(File).second) |
| return; |
| |
| SmallString<64> UnitName; |
| if (!withoutUnitName) |
| getUnitNameForOutputFile(File->getName(), UnitName); |
| addUnitDependency(UnitName.str(), File, IsSystem, Mod); |
| } |
| |
| void IndexUnitWriter::addUnitDependency(StringRef UnitFile, |
| const FileEntry *File, bool IsSystem, |
| writer::OpaqueModule Mod) { |
| int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1; |
| ASTFileUnits.emplace_back(RecordOrUnitData{UnitFile, Dep, addModule(Mod), IsSystem}); |
| } |
| |
| bool IndexUnitWriter::addInclude(const FileEntry *Source, unsigned Line, |
| const FileEntry *Target) { |
| // FIXME: This will ignore includes of headers that resolve to module imports |
| // because the 'target' header has not been added as a file dependency earlier |
| // so it is missing from \c IndexByFile. |
| |
| auto It = IndexByFile.find(Source); |
| if (It == IndexByFile.end()) |
| return false; |
| int SourceIndex = It->getSecond(); |
| It = IndexByFile.find(Target); |
| if (It == IndexByFile.end()) |
| return false; |
| int TargetIndex = It->getSecond(); |
| Files[SourceIndex].Includes.emplace_back(FileInclude{TargetIndex, Line}); |
| return true; |
| }; |
| |
| void IndexUnitWriter::getUnitNameForOutputFile(StringRef FilePath, |
| SmallVectorImpl<char> &Str) { |
| SmallString<256> AbsPath(FilePath); |
| FileMgr.makeAbsolutePath(AbsPath); |
| return getUnitNameForAbsoluteOutputFile(AbsPath, Str); |
| } |
| |
| void IndexUnitWriter::getUnitPathForOutputFile(StringRef FilePath, |
| SmallVectorImpl<char> &Str) { |
| Str.append(UnitsPath.begin(), UnitsPath.end()); |
| Str.push_back('/'); |
| return getUnitNameForOutputFile(FilePath, Str); |
| } |
| |
| Optional<bool> IndexUnitWriter::isUnitUpToDateForOutputFile(StringRef FilePath, |
| Optional<StringRef> TimeCompareFilePath, |
| std::string &Error) { |
| SmallString<256> UnitPath; |
| getUnitPathForOutputFile(FilePath, UnitPath); |
| |
| llvm::sys::fs::file_status UnitStat; |
| if (std::error_code EC = llvm::sys::fs::status(UnitPath.c_str(), UnitStat)) { |
| if (EC != llvm::errc::no_such_file_or_directory) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "could not access path '" << UnitPath |
| << "': " << EC.message(); |
| return None; |
| } |
| return false; |
| } |
| |
| if (!TimeCompareFilePath.hasValue()) |
| return true; |
| |
| llvm::sys::fs::file_status CompareStat; |
| if (std::error_code EC = llvm::sys::fs::status(*TimeCompareFilePath, CompareStat)) { |
| if (EC != llvm::errc::no_such_file_or_directory) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "could not access path '" << *TimeCompareFilePath |
| << "': " << EC.message(); |
| return None; |
| } |
| return true; |
| } |
| |
| // Return true (unit is up-to-date) if the file to compare is older than the |
| // unit file. |
| return CompareStat.getLastModificationTime() <= UnitStat.getLastModificationTime(); |
| } |
| |
| void IndexUnitWriter::getUnitNameForAbsoluteOutputFile(StringRef FilePath, |
| SmallVectorImpl<char> &Str) { |
| StringRef Fname = sys::path::filename(FilePath); |
| Str.append(Fname.begin(), Fname.end()); |
| Str.push_back('-'); |
| llvm::hash_code PathHashVal = llvm::hash_value(FilePath); |
| llvm::APInt(64, PathHashVal).toString(Str, 36, /*Signed=*/false); |
| } |
| |
| static void writeBlockInfo(BitstreamWriter &Stream) { |
| RecordData Record; |
| |
| Stream.EnterBlockInfoBlock(); |
| #define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record) |
| #define RECORD(X) emitRecordID(X, #X, Stream, Record) |
| |
| BLOCK(UNIT_VERSION_BLOCK); |
| RECORD(UNIT_VERSION); |
| |
| BLOCK(UNIT_INFO_BLOCK); |
| RECORD(UNIT_INFO); |
| |
| BLOCK(UNIT_DEPENDENCIES_BLOCK); |
| RECORD(UNIT_DEPENDENCY); |
| |
| BLOCK(UNIT_INCLUDES_BLOCK); |
| RECORD(UNIT_INCLUDE); |
| |
| BLOCK(UNIT_PATHS_BLOCK); |
| RECORD(UNIT_PATH); |
| RECORD(UNIT_PATH_BUFFER); |
| |
| BLOCK(UNIT_MODULES_BLOCK); |
| RECORD(UNIT_MODULE); |
| RECORD(UNIT_MODULE_BUFFER); |
| |
| #undef RECORD |
| #undef BLOCK |
| Stream.ExitBlock(); |
| } |
| |
| static void writeVersionInfo(BitstreamWriter &Stream) { |
| using namespace llvm::sys; |
| |
| Stream.EnterSubblock(UNIT_VERSION_BLOCK_ID, 3); |
| |
| auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
| Abbrev->Add(BitCodeAbbrevOp(UNIT_VERSION)); |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version |
| unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); |
| |
| RecordData Record; |
| Record.push_back(UNIT_VERSION); |
| Record.push_back(STORE_FORMAT_VERSION); |
| Stream.EmitRecordWithAbbrev(AbbrevCode, Record); |
| |
| Stream.ExitBlock(); |
| } |
| |
| bool IndexUnitWriter::write(std::string &Error) { |
| using namespace llvm::sys; |
| |
| // Determine the working directory. |
| SmallString<128> CWDPath; |
| if (!FileMgr.getFileSystemOpts().WorkingDir.empty()) { |
| CWDPath = FileMgr.getFileSystemOpts().WorkingDir; |
| if (!path::is_absolute(CWDPath)) { |
| fs::make_absolute(CWDPath); |
| } |
| } else { |
| std::error_code EC = sys::fs::current_path(CWDPath); |
| if (EC) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "failed to determine current working directory: " << EC.message(); |
| return true; |
| } |
| } |
| WorkDir = CWDPath.str(); |
| |
| SmallString<512> Buffer; |
| BitstreamWriter Stream(Buffer); |
| Stream.Emit('I', 8); |
| Stream.Emit('D', 8); |
| Stream.Emit('X', 8); |
| Stream.Emit('U', 8); |
| |
| PathStorage PathStore(WorkDir, SysrootPath); |
| |
| writeBlockInfo(Stream); |
| writeVersionInfo(Stream); |
| writeUnitInfo(Stream, PathStore); |
| writeDependencies(Stream, PathStore); |
| writeIncludes(Stream, PathStore); |
| writePaths(Stream, PathStore); |
| writeModules(Stream); |
| |
| SmallString<256> UnitPath; |
| getUnitPathForOutputFile(OutputFile, UnitPath); |
| |
| SmallString<128> TempPath; |
| TempPath = path::parent_path(UnitsPath); |
| TempPath += '/'; |
| TempPath += path::filename(UnitPath); |
| TempPath += "-%%%%%%%%"; |
| int TempFD; |
| if (llvm::sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "failed to create temporary file: " << TempPath; |
| return true; |
| } |
| |
| raw_fd_ostream OS(TempFD, /*shouldClose=*/true); |
| OS.write(Buffer.data(), Buffer.size()); |
| OS.close(); |
| |
| std::error_code EC = fs::rename(/*from=*/TempPath.c_str(), /*to=*/UnitPath.c_str()); |
| if (EC) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "failed to rename '" << TempPath << "' to '" << UnitPath << "': " << EC.message(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void IndexUnitWriter::writeUnitInfo(llvm::BitstreamWriter &Stream, |
| PathStorage &PathStore) { |
| Stream.EnterSubblock(UNIT_INFO_BLOCK_ID, 3); |
| |
| auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
| Abbrev->Add(BitCodeAbbrevOp(UNIT_INFO)); |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystemUnit |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // WorkDir offset |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // WorkDir size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // OutputFile offset |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // OutputFile size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Sysroot offset |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Sysroot size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Main path id |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsDebugCompilation |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsModuleUnit |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Module name size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderIdentifier size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderVersion size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderDataVersion |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module name + ProviderIdentifier + ProviderVersion + target triple |
| unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); |
| |
| RecordData Record; |
| Record.push_back(UNIT_INFO); |
| Record.push_back(IsSystemUnit); |
| Record.push_back(PathStore.getPathOffset(WorkDir)); |
| Record.push_back(WorkDir.size()); |
| Record.push_back(PathStore.getPathOffset(OutputFile)); |
| Record.push_back(OutputFile.size()); |
| Record.push_back(PathStore.getPathOffset(SysrootPath)); |
| Record.push_back(SysrootPath.size()); |
| Record.push_back(PathStore.getPathIndex(MainFile) + 1); // Make 1-based with 0=invalid |
| Record.push_back(IsDebugCompilation); |
| Record.push_back(IsModuleUnit); |
| Record.push_back(ModuleName.size()); |
| Record.push_back(ProviderIdentifier.size()); |
| Record.push_back(ProviderVersion.size()); |
| // ProviderDataVersion is reserved. Not sure it is a good to idea to have |
| // clients consider the specifics of a 'provider data version', but reserving |
| // to avoid store format version change in case there is a use case in the |
| // future. |
| Record.push_back(0); // ProviderDataVersion |
| SmallString<128> InfoStrings; |
| InfoStrings += ModuleName; |
| InfoStrings += ProviderIdentifier; |
| InfoStrings += ProviderVersion; |
| InfoStrings += TargetTriple; |
| Stream.EmitRecordWithBlob(AbbrevCode, Record, InfoStrings); |
| |
| Stream.ExitBlock(); |
| } |
| |
| void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream, |
| PathStorage &PathStore) { |
| std::vector<bool> FileUsedForRecordOrUnit; |
| FileUsedForRecordOrUnit.resize(Files.size()); |
| |
| Stream.EnterSubblock(UNIT_DEPENDENCIES_BLOCK_ID, 3); |
| |
| auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
| Abbrev->Add(BitCodeAbbrevOp(UNIT_DEPENDENCY)); |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitDependencyKindBitNum)); // Dependency kind |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // PathIndex (1-based, 0 = none) |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // ModuleIndex (1-based, 0 = none) |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // time_t |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // file size |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name |
| unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); |
| |
| RecordData Record; |
| |
| auto addRecordOrUnitData = [&](UnitDependencyKind K, const RecordOrUnitData &Data) { |
| Record.push_back(UNIT_DEPENDENCY); |
| Record.push_back(K); |
| Record.push_back(Data.IsSystem); |
| if (Data.FileIndex != -1) { |
| Record.push_back(PathStore.getPathIndex(Files[Data.FileIndex].File) + 1); |
| FileUsedForRecordOrUnit[Data.FileIndex] = true; |
| } else { |
| Record.push_back(0); |
| } |
| if (Data.ModuleIndex != -1) { |
| Record.push_back(Data.ModuleIndex + 1); |
| } else { |
| Record.push_back(0); |
| } |
| if (Data.FileIndex != -1) { |
| Record.push_back(Files[Data.FileIndex].File->getModificationTime()); |
| Record.push_back(Files[Data.FileIndex].File->getSize()); |
| } else { |
| Record.push_back(0); |
| Record.push_back(0); |
| } |
| Stream.EmitRecordWithBlob(AbbrevCode, Record, Data.Name); |
| }; |
| |
| for (auto &ASTData : ASTFileUnits) { |
| Record.clear(); |
| addRecordOrUnitData(UNIT_DEPEND_KIND_UNIT, ASTData); |
| } |
| for (auto &recordData : Records) { |
| Record.clear(); |
| addRecordOrUnitData(UNIT_DEPEND_KIND_RECORD, recordData); |
| } |
| size_t FileIndex = 0; |
| for (auto &File : Files) { |
| if (FileUsedForRecordOrUnit[FileIndex++]) |
| continue; |
| Record.clear(); |
| Record.push_back(UNIT_DEPENDENCY); |
| Record.push_back(UNIT_DEPEND_KIND_FILE); |
| Record.push_back(File.IsSystem); |
| Record.push_back(PathStore.getPathIndex(File.File) + 1); |
| if (File.ModuleIndex != -1) { |
| Record.push_back(File.ModuleIndex + 1); |
| } else { |
| Record.push_back(0); |
| } |
| Record.push_back(File.File->getModificationTime()); |
| Record.push_back(File.File->getSize()); |
| Stream.EmitRecordWithBlob(AbbrevCode, Record, StringRef()); |
| } |
| |
| Stream.ExitBlock(); |
| } |
| |
| void IndexUnitWriter::writeIncludes(llvm::BitstreamWriter &Stream, |
| PathStorage &PathStore) { |
| Stream.EnterSubblock(UNIT_INCLUDES_BLOCK_ID, 3); |
| |
| auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
| Abbrev->Add(BitCodeAbbrevOp(UNIT_INCLUDE)); |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // source path index (1-based, 0 = no path) |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // source include line |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // target path index (1-based, 0 = no path) |
| unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); |
| |
| RecordData Record; |
| |
| for (auto &Including : Files) { |
| for(auto &Included: Including.Includes) { |
| Record.clear(); |
| Record.push_back(UNIT_INCLUDE); |
| Record.push_back(PathStore.getPathIndex(Including.File) + 1); |
| Record.push_back(Included.Line); |
| Record.push_back(PathStore.getPathIndex(Files[Included.Index].File) + 1); |
| Stream.EmitRecordWithAbbrev(AbbrevCode, Record); |
| } |
| } |
| Stream.ExitBlock(); |
| } |
| |
| void IndexUnitWriter::writePaths(llvm::BitstreamWriter &Stream, |
| PathStorage &PathStore) { |
| Stream.EnterSubblock(UNIT_PATHS_BLOCK_ID, 3); |
| |
| auto PathAbbrev = std::make_shared<BitCodeAbbrev>(); |
| PathAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH)); |
| PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitFilePathPrefixKindBitNum)); // Path prefix kind |
| PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // DirPath offset |
| PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // DirPath size |
| PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Filename offset |
| PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Filename size |
| unsigned PathAbbrevCode = Stream.EmitAbbrev(std::move(PathAbbrev)); |
| |
| auto PathBufferAbbrev = std::make_shared<BitCodeAbbrev>(); |
| PathBufferAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH_BUFFER)); |
| PathBufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Paths buffer |
| unsigned PathBufferAbbrevCode = Stream.EmitAbbrev(PathBufferAbbrev); |
| |
| RecordData Record; |
| for(auto &BitPath: PathStore.getBitPaths()) { |
| Record.push_back(UNIT_PATH); |
| Record.push_back(BitPath.PrefixKind); |
| Record.push_back(BitPath.Dir.Offset); |
| Record.push_back(BitPath.Dir.Size); |
| Record.push_back(BitPath.Filename.Offset); |
| Record.push_back(BitPath.Filename.Size); |
| Stream.EmitRecordWithAbbrev(PathAbbrevCode, Record); |
| Record.clear(); |
| } |
| |
| Record.push_back(UNIT_PATH_BUFFER); |
| Stream.EmitRecordWithBlob(PathBufferAbbrevCode, Record, PathStore.getPathsBuffer()); |
| |
| Stream.ExitBlock(); |
| } |
| |
| void IndexUnitWriter::writeModules(llvm::BitstreamWriter &Stream) { |
| Stream.EnterSubblock(UNIT_MODULES_BLOCK_ID, 3); |
| |
| auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
| Abbrev->Add(BitCodeAbbrevOp(UNIT_MODULE)); |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 9)); // Module name offset |
| Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name size |
| unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); |
| |
| auto BufferAbbrev = std::make_shared<BitCodeAbbrev>(); |
| BufferAbbrev->Add(BitCodeAbbrevOp(UNIT_MODULE_BUFFER)); |
| BufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module names buffer |
| unsigned BufferAbbrevCode = Stream.EmitAbbrev(BufferAbbrev); |
| |
| SmallString<512> ModuleNamesBuf; |
| |
| RecordData Record; |
| for (auto &Mod : Modules) { |
| SmallString<64> ModuleName; |
| StringRef name = GetInfoForModuleFn(Mod, ModuleName).Name; |
| size_t offset = ModuleNamesBuf.size(); |
| ModuleNamesBuf += name; |
| |
| Record.push_back(UNIT_MODULE); |
| Record.push_back(offset); |
| Record.push_back(name.size()); |
| Stream.EmitRecordWithAbbrev(AbbrevCode, Record); |
| Record.clear(); |
| } |
| |
| Record.push_back(UNIT_MODULE_BUFFER); |
| Stream.EmitRecordWithBlob(BufferAbbrevCode, Record, ModuleNamesBuf.str()); |
| |
| Stream.ExitBlock(); |
| } |
| |
| bool IndexUnitWriter::initIndexDirectory(StringRef StorePath, |
| std::string &Error) { |
| using namespace llvm::sys; |
| SmallString<128> SubPath = StorePath; |
| store::appendRecordSubDir(SubPath); |
| std::error_code EC = fs::create_directories(SubPath); |
| if (EC) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "failed to create directory '" << SubPath << "': " << EC.message(); |
| return true; |
| } |
| |
| SubPath = StorePath; |
| store::appendUnitSubDir(SubPath); |
| EC = fs::create_directory(SubPath); |
| if (EC) { |
| llvm::raw_string_ostream Err(Error); |
| Err << "failed to create directory '" << SubPath << "': " << EC.message(); |
| return true; |
| } |
| |
| return false; |
| } |