diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp | 813 |
1 files changed, 399 insertions, 414 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp index 5526487..cab6b90 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -11,6 +11,7 @@ #include "clang/Sema/Sema.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -24,6 +25,7 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" #include "clang/Frontend/Utils.h" @@ -35,6 +37,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Support/Timer.h" #include "llvm/Support/Host.h" +#include "llvm/Support/LockFileManager.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" @@ -42,18 +45,6 @@ #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Config/config.h" -// Support for FileLockManager -#include <fstream> -#include <sys/types.h> -#include <sys/stat.h> - -#if LLVM_ON_WIN32 -#include <windows.h> -#endif -#if LLVM_ON_UNIX -#include <unistd.h> -#endif - using namespace clang; CompilerInstance::CompilerInstance() @@ -97,6 +88,7 @@ void CompilerInstance::setASTConsumer(ASTConsumer *Value) { void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { CompletionConsumer.reset(Value); + getFrontendOpts().SkipFunctionBodies = Value != 0; } // Diagnostics @@ -104,7 +96,7 @@ static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts, unsigned argc, const char* const *argv, DiagnosticsEngine &Diags) { std::string ErrorInfo; - llvm::OwningPtr<raw_ostream> OS( + OwningPtr<raw_ostream> OS( new llvm::raw_fd_ostream(DiagOpts.DumpBuildInformation.c_str(), ErrorInfo)); if (!ErrorInfo.empty()) { Diags.Report(diag::err_fe_unable_to_open_logfile) @@ -153,6 +145,28 @@ static void SetUpDiagnosticLog(const DiagnosticOptions &DiagOpts, Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), Logger)); } +static void SetupSerializedDiagnostics(const DiagnosticOptions &DiagOpts, + DiagnosticsEngine &Diags, + StringRef OutputFile) { + std::string ErrorInfo; + OwningPtr<llvm::raw_fd_ostream> OS; + OS.reset(new llvm::raw_fd_ostream(OutputFile.str().c_str(), ErrorInfo, + llvm::raw_fd_ostream::F_Binary)); + + if (!ErrorInfo.empty()) { + Diags.Report(diag::warn_fe_serialized_diag_failure) + << OutputFile << ErrorInfo; + return; + } + + DiagnosticConsumer *SerializedConsumer = + clang::serialized_diags::create(OS.take(), DiagOpts); + + + Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), + SerializedConsumer)); +} + void CompilerInstance::createDiagnostics(int Argc, const char* const *Argv, DiagnosticConsumer *Client, bool ShouldOwnClient, @@ -162,15 +176,15 @@ void CompilerInstance::createDiagnostics(int Argc, const char* const *Argv, &getCodeGenOpts()); } -llvm::IntrusiveRefCntPtr<DiagnosticsEngine> +IntrusiveRefCntPtr<DiagnosticsEngine> CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, int Argc, const char* const *Argv, DiagnosticConsumer *Client, bool ShouldOwnClient, bool ShouldCloneClient, const CodeGenOptions *CodeGenOpts) { - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(new DiagnosticsEngine(DiagID)); // Create the diagnostic client for reporting errors or for @@ -194,6 +208,10 @@ CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, if (!Opts.DumpBuildInformation.empty()) SetUpBuildDumpLog(Opts, Argc, Argv, *Diags); + if (!Opts.DiagnosticSerializationFile.empty()) + SetupSerializedDiagnostics(Opts, *Diags, + Opts.DiagnosticSerializationFile); + // Configure our handling of diagnostics. ProcessWarningOptions(*Diags, Opts); @@ -223,7 +241,10 @@ void CompilerInstance::createPreprocessor() { PTHMgr = PTHManager::Create(PPOpts.TokenCache, getDiagnostics()); // Create the Preprocessor. - HeaderSearch *HeaderInfo = new HeaderSearch(getFileManager()); + HeaderSearch *HeaderInfo = new HeaderSearch(getFileManager(), + getDiagnostics(), + getLangOpts(), + &getTarget()); PP = new Preprocessor(getDiagnostics(), getLangOpts(), &getTarget(), getSourceManager(), *HeaderInfo, *this, PTHMgr, /*OwnsHeaderSearch=*/true); @@ -237,28 +258,28 @@ void CompilerInstance::createPreprocessor() { } if (PPOpts.DetailedRecord) - PP->createPreprocessingRecord( - PPOpts.DetailedRecordIncludesNestedMacroExpansions); + PP->createPreprocessingRecord(PPOpts.DetailedRecordConditionalDirectives); InitializePreprocessor(*PP, PPOpts, getHeaderSearchOpts(), getFrontendOpts()); // Set up the module path, including the hash for the // module-creation options. - llvm::SmallString<256> SpecificModuleCache( + SmallString<256> SpecificModuleCache( getHeaderSearchOpts().ModuleCachePath); if (!getHeaderSearchOpts().DisableModuleHash) llvm::sys::path::append(SpecificModuleCache, getInvocation().getModuleHash()); - PP->getHeaderSearchInfo().configureModules(SpecificModuleCache, - getPreprocessorOpts().ModuleBuildPath.empty() - ? std::string() - : getPreprocessorOpts().ModuleBuildPath.back()); + PP->getHeaderSearchInfo().setModuleCachePath(SpecificModuleCache); // Handle generating dependencies, if requested. const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); if (!DepOpts.OutputFile.empty()) AttachDependencyFileGen(*PP, DepOpts); + if (!DepOpts.DOTOutputFile.empty()) + AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, + getHeaderSearchOpts().Sysroot); + // Handle generating header include information, if requested. if (DepOpts.ShowHeaderIncludes) AttachHeaderIncludeGen(*PP); @@ -286,12 +307,14 @@ void CompilerInstance::createASTContext() { void CompilerInstance::createPCHExternalASTSource(StringRef Path, bool DisablePCHValidation, bool DisableStatCache, + bool AllowPCHWithCompilerErrors, void *DeserializationListener){ - llvm::OwningPtr<ExternalASTSource> Source; + OwningPtr<ExternalASTSource> Source; bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot, DisablePCHValidation, DisableStatCache, + AllowPCHWithCompilerErrors, getPreprocessor(), getASTContext(), DeserializationListener, Preamble)); @@ -304,14 +327,16 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path, const std::string &Sysroot, bool DisablePCHValidation, bool DisableStatCache, + bool AllowPCHWithCompilerErrors, Preprocessor &PP, ASTContext &Context, void *DeserializationListener, bool Preamble) { - llvm::OwningPtr<ASTReader> Reader; + OwningPtr<ASTReader> Reader; Reader.reset(new ASTReader(PP, Context, Sysroot.empty() ? "" : Sysroot.c_str(), - DisablePCHValidation, DisableStatCache)); + DisablePCHValidation, DisableStatCache, + AllowPCHWithCompilerErrors)); Reader->setDeserializationListener( static_cast<ASTDeserializationListener *>(DeserializationListener)); @@ -359,7 +384,7 @@ static bool EnableCodeCompletion(Preprocessor &PP, void CompilerInstance::createCodeCompletionConsumer() { const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt; if (!CompletionConsumer) { - CompletionConsumer.reset( + setCodeCompletionConsumer( createCodeCompletionConsumer(getPreprocessor(), Loc.FileName, Loc.Line, Loc.Column, getFrontendOpts().ShowMacrosInCodeCompletion, @@ -370,14 +395,14 @@ void CompilerInstance::createCodeCompletionConsumer() { return; } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName, Loc.Line, Loc.Column)) { - CompletionConsumer.reset(); + setCodeCompletionConsumer(0); return; } if (CompletionConsumer->isOutputBinary() && llvm::sys::Program::ChangeStdoutToBinary()) { getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary); - CompletionConsumer.reset(); + setCodeCompletionConsumer(0); } } @@ -424,7 +449,7 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) { bool existed; llvm::sys::fs::remove(it->TempFilename, existed); } else { - llvm::SmallString<128> NewOutFile(it->Filename); + SmallString<128> NewOutFile(it->Filename); // If '-working-directory' was passed, the output filename should be // relative to that. @@ -450,7 +475,8 @@ CompilerInstance::createDefaultOutputFile(bool Binary, StringRef InFile, StringRef Extension) { return createOutputFile(getFrontendOpts().OutputFile, Binary, - /*RemoveFileOnSignal=*/true, InFile, Extension); + /*RemoveFileOnSignal=*/true, InFile, Extension, + /*UseTemporary=*/true); } llvm::raw_fd_ostream * @@ -458,12 +484,14 @@ CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, StringRef InFile, StringRef Extension, - bool UseTemporary) { + bool UseTemporary, + bool CreateMissingDirectories) { std::string Error, OutputPathName, TempPathName; llvm::raw_fd_ostream *OS = createOutputFile(OutputPath, Error, Binary, RemoveFileOnSignal, InFile, Extension, UseTemporary, + CreateMissingDirectories, &OutputPathName, &TempPathName); if (!OS) { @@ -488,8 +516,12 @@ CompilerInstance::createOutputFile(StringRef OutputPath, StringRef InFile, StringRef Extension, bool UseTemporary, + bool CreateMissingDirectories, std::string *ResultPathName, std::string *TempPathName) { + assert((!CreateMissingDirectories || UseTemporary) && + "CreateMissingDirectories is only allowed when using temporary files"); + std::string OutFile, TempFile; if (!OutputPath.empty()) { OutFile = OutputPath; @@ -504,18 +536,26 @@ CompilerInstance::createOutputFile(StringRef OutputPath, OutFile = "-"; } - llvm::OwningPtr<llvm::raw_fd_ostream> OS; + OwningPtr<llvm::raw_fd_ostream> OS; std::string OSFile; if (UseTemporary && OutFile != "-") { - llvm::sys::Path OutPath(OutFile); - // Only create the temporary if we can actually write to OutPath, otherwise - // we want to fail early. + // Only create the temporary if the parent directory exists (or create + // missing directories is true) and we can actually write to OutPath, + // otherwise we want to fail early. + SmallString<256> AbsPath(OutputPath); + llvm::sys::fs::make_absolute(AbsPath); + llvm::sys::Path OutPath(AbsPath); + bool ParentExists = false; + if (llvm::sys::fs::exists(llvm::sys::path::parent_path(AbsPath.str()), + ParentExists)) + ParentExists = false; bool Exists; - if ((llvm::sys::fs::exists(OutPath.str(), Exists) || !Exists) || - (OutPath.isRegularFile() && OutPath.canWrite())) { + if ((CreateMissingDirectories || ParentExists) && + ((llvm::sys::fs::exists(AbsPath.str(), Exists) || !Exists) || + (OutPath.isRegularFile() && OutPath.canWrite()))) { // Create a temporary file. - llvm::SmallString<128> TempPath; + SmallString<128> TempPath; TempPath = OutFile; TempPath += "-%%%%%%%%"; int fd; @@ -550,12 +590,15 @@ CompilerInstance::createOutputFile(StringRef OutputPath, // Initialization Utilities -bool CompilerInstance::InitializeSourceManager(StringRef InputFile) { - return InitializeSourceManager(InputFile, getDiagnostics(), getFileManager(), - getSourceManager(), getFrontendOpts()); +bool CompilerInstance::InitializeSourceManager(StringRef InputFile, + SrcMgr::CharacteristicKind Kind){ + return InitializeSourceManager(InputFile, Kind, getDiagnostics(), + getFileManager(), getSourceManager(), + getFrontendOpts()); } bool CompilerInstance::InitializeSourceManager(StringRef InputFile, + SrcMgr::CharacteristicKind Kind, DiagnosticsEngine &Diags, FileManager &FileMgr, SourceManager &SourceMgr, @@ -567,9 +610,9 @@ bool CompilerInstance::InitializeSourceManager(StringRef InputFile, Diags.Report(diag::err_fe_error_reading) << InputFile; return false; } - SourceMgr.createMainFileID(File); + SourceMgr.createMainFileID(File, Kind); } else { - llvm::OwningPtr<llvm::MemoryBuffer> SB; + OwningPtr<llvm::MemoryBuffer> SB; if (llvm::MemoryBuffer::getSTDIN(SB)) { // FIXME: Give ec.message() in this diag. Diags.Report(diag::err_fe_error_reading_stdin); @@ -577,7 +620,7 @@ bool CompilerInstance::InitializeSourceManager(StringRef InputFile, } const FileEntry *File = FileMgr.getVirtualFile(SB->getBufferIdentifier(), SB->getBufferSize(), 0); - SourceMgr.createMainFileID(File); + SourceMgr.createMainFileID(File, Kind); SourceMgr.overrideFileContents(File, SB.take()); } @@ -612,7 +655,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { if (getHeaderSearchOpts().Verbose) OS << "clang -cc1 version " CLANG_VERSION_STRING << " based upon " << PACKAGE_STRING - << " hosted on " << llvm::sys::getHostTriple() << "\n"; + << " default target " << llvm::sys::getDefaultTargetTriple() << "\n"; if (getFrontendOpts().ShowTimers) createFrontendTimer(); @@ -621,18 +664,19 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { llvm::EnableStatistics(); for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) { - const std::string &InFile = getFrontendOpts().Inputs[i].second; - // Reset the ID tables if we are reusing the SourceManager. if (hasSourceManager()) getSourceManager().clearIDTables(); - if (Act.BeginSourceFile(*this, InFile, getFrontendOpts().Inputs[i].first)) { + if (Act.BeginSourceFile(*this, getFrontendOpts().Inputs[i])) { Act.Execute(); Act.EndSourceFile(); } } + // Notify the diagnostic client that all files were processed. + getDiagnostics().getClient()->finish(); + if (getDiagnosticOpts().ShowCarets) { // We can have multiple diagnostics sharing one diagnostic client. // Get the total number of warnings/errors from the client. @@ -670,320 +714,105 @@ static InputKind getSourceInputKindFromOptions(const LangOptions &LangOpts) { } namespace { - struct CompileModuleData { + struct CompileModuleMapData { CompilerInstance &Instance; - GeneratePCHAction &CreateModuleAction; + GenerateModuleAction &CreateModuleAction; }; } /// \brief Helper function that executes the module-generating action under /// a crash recovery context. -static void doCompileModule(void *UserData) { - CompileModuleData &Data = *reinterpret_cast<CompileModuleData *>(UserData); +static void doCompileMapModule(void *UserData) { + CompileModuleMapData &Data + = *reinterpret_cast<CompileModuleMapData *>(UserData); Data.Instance.ExecuteAction(Data.CreateModuleAction); } -namespace { - /// \brief Class that manages the creation of a lock file to aid - /// implicit coordination between different processes. - /// - /// The implicit coordination works by creating a ".lock" file alongside - /// the file that we're coordinating for, using the atomicity of the file - /// system to ensure that only a single process can create that ".lock" file. - /// When the lock file is removed, the owning process has finished the - /// operation. - class LockFileManager { - public: - /// \brief Describes the state of a lock file. - enum LockFileState { - /// \brief The lock file has been created and is owned by this instance - /// of the object. - LFS_Owned, - /// \brief The lock file already exists and is owned by some other - /// instance. - LFS_Shared, - /// \brief An error occurred while trying to create or find the lock - /// file. - LFS_Error - }; - - private: - llvm::SmallString<128> LockFileName; - llvm::SmallString<128> UniqueLockFileName; - - llvm::Optional<std::pair<std::string, int> > Owner; - llvm::Optional<llvm::error_code> Error; - - LockFileManager(const LockFileManager &); - LockFileManager &operator=(const LockFileManager &); - - static llvm::Optional<std::pair<std::string, int> > - readLockFile(StringRef LockFileName); - - static bool processStillExecuting(StringRef Hostname, int PID); - - public: - - LockFileManager(StringRef FileName); - ~LockFileManager(); - - /// \brief Determine the state of the lock file. - LockFileState getState() const; - - operator LockFileState() const { return getState(); } - - /// \brief For a shared lock, wait until the owner releases the lock. - void waitForUnlock(); - }; -} - -/// \brief Attempt to read the lock file with the given name, if it exists. -/// -/// \param LockFileName The name of the lock file to read. -/// -/// \returns The process ID of the process that owns this lock file -llvm::Optional<std::pair<std::string, int> > -LockFileManager::readLockFile(StringRef LockFileName) { - // Check whether the lock file exists. If not, clearly there's nothing - // to read, so we just return. - bool Exists = false; - if (llvm::sys::fs::exists(LockFileName, Exists) || !Exists) - return llvm::Optional<std::pair<std::string, int> >(); - - // Read the owning host and PID out of the lock file. If it appears that the - // owning process is dead, the lock file is invalid. - int PID = 0; - std::string Hostname; - std::ifstream Input(LockFileName.str().c_str()); - if (Input >> Hostname >> PID && PID > 0 && - processStillExecuting(Hostname, PID)) - return std::make_pair(Hostname, PID); - - // Delete the lock file. It's invalid anyway. - bool Existed; - llvm::sys::fs::remove(LockFileName, Existed); - return llvm::Optional<std::pair<std::string, int> >(); -} - -bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) { -#if LLVM_ON_UNIX - char MyHostname[256]; - MyHostname[255] = 0; - MyHostname[0] = 0; - gethostname(MyHostname, 255); - // Check whether the process is dead. If so, we're done. - if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH) - return false; -#endif - - return true; -} - -LockFileManager::LockFileManager(StringRef FileName) -{ - LockFileName = FileName; - LockFileName += ".lock"; - - // If the lock file already exists, don't bother to try to create our own - // lock file; it won't work anyway. Just figure out who owns this lock file. - if ((Owner = readLockFile(LockFileName))) - return; - - // Create a lock file that is unique to this instance. - UniqueLockFileName = LockFileName; - UniqueLockFileName += "-%%%%%%%%"; - int UniqueLockFileID; - if (llvm::error_code EC - = llvm::sys::fs::unique_file(UniqueLockFileName.str(), - UniqueLockFileID, - UniqueLockFileName, - /*makeAbsolute=*/false)) { - Error = EC; - return; - } - - // Write our process ID to our unique lock file. - { - llvm::raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); - -#if LLVM_ON_UNIX - // FIXME: move getpid() call into LLVM - char hostname[256]; - hostname[255] = 0; - hostname[0] = 0; - gethostname(hostname, 255); - Out << hostname << ' ' << getpid(); -#else - Out << "localhost 1"; -#endif - Out.close(); - - if (Out.has_error()) { - // We failed to write out PID, so make up an excuse, remove the - // unique lock file, and fail. - Error = llvm::make_error_code(llvm::errc::no_space_on_device); - bool Existed; - llvm::sys::fs::remove(UniqueLockFileName.c_str(), Existed); - return; - } - } - - // Create a hard link from the lock file name. If this succeeds, we're done. - llvm::error_code EC - = llvm::sys::fs::create_hard_link(UniqueLockFileName.str(), - LockFileName.str()); - if (EC == llvm::errc::success) - return; - - // Creating the hard link failed. - -#ifdef LLVM_ON_UNIX - // The creation of the hard link may appear to fail, but if stat'ing the - // unique file returns a link count of 2, then we can still declare success. - struct stat StatBuf; - if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 && - StatBuf.st_nlink == 2) - return; -#endif - - // Someone else managed to create the lock file first. Wipe out our unique - // lock file (it's useless now) and read the process ID from the lock file. - bool Existed; - llvm::sys::fs::remove(UniqueLockFileName.str(), Existed); - if ((Owner = readLockFile(LockFileName))) - return; - - // There is a lock file that nobody owns; try to clean it up and report - // an error. - llvm::sys::fs::remove(LockFileName.str(), Existed); - Error = EC; -} - -LockFileManager::LockFileState LockFileManager::getState() const { - if (Owner) - return LFS_Shared; - - if (Error) - return LFS_Error; - - return LFS_Owned; -} - -LockFileManager::~LockFileManager() { - if (getState() != LFS_Owned) - return; - - // Since we own the lock, remove the lock file and our own unique lock file. - bool Existed; - llvm::sys::fs::remove(LockFileName.str(), Existed); - llvm::sys::fs::remove(UniqueLockFileName.str(), Existed); -} - -void LockFileManager::waitForUnlock() { - if (getState() != LFS_Shared) - return; - -#if LLVM_ON_WIN32 - unsigned long Interval = 1; -#else - struct timespec Interval; - Interval.tv_sec = 0; - Interval.tv_nsec = 1000000; -#endif - // Don't wait more than an hour for the file to appear. - const unsigned MaxSeconds = 3600; - do { - // Sleep for the designated interval, to allow the owning process time to - // finish up and - // FIXME: Should we hook in to system APIs to get a notification when the - // lock file is deleted? -#if LLVM_ON_WIN32 - Sleep(Interval); -#else - nanosleep(&Interval, NULL); -#endif - // If the file no longer exists, we're done. - bool Exists = false; - if (!llvm::sys::fs::exists(LockFileName.str(), Exists) && !Exists) - return; - - if (!processStillExecuting((*Owner).first, (*Owner).second)) - return; - - // Exponentially increase the time we wait for the lock to be removed. -#if LLVM_ON_WIN32 - Interval *= 2; -#else - Interval.tv_sec *= 2; - Interval.tv_nsec *= 2; - if (Interval.tv_nsec >= 1000000000) { - ++Interval.tv_sec; - Interval.tv_nsec -= 1000000000; - } -#endif - } while ( -#if LLVM_ON_WIN32 - Interval < MaxSeconds * 1000 -#else - Interval.tv_sec < (time_t)MaxSeconds -#endif - ); - - // Give up. -} - -/// \brief Compile a module file for the given module name with the given -/// umbrella header, using the options provided by the importing compiler -/// instance. +/// \brief Compile a module file for the given module, using the options +/// provided by the importing compiler instance. static void compileModule(CompilerInstance &ImportingInstance, - StringRef ModuleName, - StringRef ModuleFileName, - StringRef UmbrellaHeader) { - LockFileManager Locked(ModuleFileName); + Module *Module, + StringRef ModuleFileName) { + llvm::LockFileManager Locked(ModuleFileName); switch (Locked) { - case LockFileManager::LFS_Error: + case llvm::LockFileManager::LFS_Error: return; - case LockFileManager::LFS_Owned: + case llvm::LockFileManager::LFS_Owned: // We're responsible for building the module ourselves. Do so below. break; - case LockFileManager::LFS_Shared: + case llvm::LockFileManager::LFS_Shared: // Someone else is responsible for building the module. Wait for them to // finish. Locked.waitForUnlock(); break; } + ModuleMap &ModMap + = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + // Construct a compiler invocation for creating this module. - llvm::IntrusiveRefCntPtr<CompilerInvocation> Invocation + IntrusiveRefCntPtr<CompilerInvocation> Invocation (new CompilerInvocation(ImportingInstance.getInvocation())); + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + // For any options that aren't intended to affect how a module is built, // reset them to their default values. - Invocation->getLangOpts().resetNonModularOptions(); - Invocation->getPreprocessorOpts().resetNonModularOptions(); + Invocation->getLangOpts()->resetNonModularOptions(); + PPOpts.resetNonModularOptions(); + + // Note the name of the module we're building. + Invocation->getLangOpts()->CurrentModule = Module->getTopLevelModuleName(); // Note that this module is part of the module build path, so that we // can detect cycles in the module graph. - Invocation->getPreprocessorOpts().ModuleBuildPath.push_back(ModuleName); + PPOpts.ModuleBuildPath.push_back(Module->getTopLevelModuleName()); + // If there is a module map file, build the module using the module map. // Set up the inputs/outputs so that we build the module from its umbrella // header. FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); FrontendOpts.OutputFile = ModuleFileName.str(); FrontendOpts.DisableFree = false; FrontendOpts.Inputs.clear(); - FrontendOpts.Inputs.push_back( - std::make_pair(getSourceInputKindFromOptions(Invocation->getLangOpts()), - UmbrellaHeader)); + InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts()); + + // Get or create the module map that we'll use to build this module. + SmallString<128> TempModuleMapFileName; + if (const FileEntry *ModuleMapFile + = ModMap.getContainingModuleMapFile(Module)) { + // Use the module map where this module resides. + FrontendOpts.Inputs.push_back(FrontendInputFile(ModuleMapFile->getName(), + IK)); + } else { + // Create a temporary module map file. + TempModuleMapFileName = Module->Name; + TempModuleMapFileName += "-%%%%%%%%.map"; + int FD; + if (llvm::sys::fs::unique_file(TempModuleMapFileName.str(), FD, + TempModuleMapFileName, + /*makeAbsolute=*/true) + != llvm::errc::success) { + ImportingInstance.getDiagnostics().Report(diag::err_module_map_temp_file) + << TempModuleMapFileName; + return; + } + // Print the module map to this file. + llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true); + Module->print(OS); + FrontendOpts.Inputs.push_back( + FrontendInputFile(TempModuleMapFileName.str().str(), IK)); + } + // Don't free the remapped file buffers; they are owned by our caller. + PPOpts.RetainRemappedFileBuffers = true; + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; - - assert(ImportingInstance.getInvocation().getModuleHash() == - Invocation->getModuleHash() && "Module hash mismatch!"); - + Invocation->getModuleHash() && "Module hash mismatch!"); + // Construct a compiler instance that will be used to actually create the // module. CompilerInstance Instance; @@ -992,21 +821,39 @@ static void compileModule(CompilerInstance &ImportingInstance, &ImportingInstance.getDiagnosticClient(), /*ShouldOwnClient=*/true, /*ShouldCloneClient=*/true); - + // Construct a module-generating action. - GeneratePCHAction CreateModuleAction(true); - + GenerateModuleAction CreateModuleAction; + // Execute the action to actually build the module in-place. Use a separate // thread so that we get a stack large enough. const unsigned ThreadStackSize = 8 << 20; llvm::CrashRecoveryContext CRC; - CompileModuleData Data = { Instance, CreateModuleAction }; - CRC.RunSafelyOnThread(&doCompileModule, &Data, ThreadStackSize); + CompileModuleMapData Data = { Instance, CreateModuleAction }; + CRC.RunSafelyOnThread(&doCompileMapModule, &Data, ThreadStackSize); + + // Delete the temporary module map file. + // FIXME: Even though we're executing under crash protection, it would still + // be nice to do this with RemoveFileOnSignal when we can. However, that + // doesn't make sense for all clients, so clean this up manually. + if (!TempModuleMapFileName.empty()) + llvm::sys::Path(TempModuleMapFileName).eraseFromDisk(); } -ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc, - IdentifierInfo &ModuleName, - SourceLocation ModuleNameLoc) { +Module *CompilerInstance::loadModule(SourceLocation ImportLoc, + ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + // If we've already handled this import, just return the cached result. + // This one-element cache is important to eliminate redundant diagnostics + // when both the preprocessor and parser see the same import declaration. + if (!ImportLoc.isInvalid() && LastModuleImportLoc == ImportLoc) { + // Make the named module visible. + if (LastModuleImportResult) + ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility); + return LastModuleImportResult; + } + // Determine what file we're searching from. SourceManager &SourceMgr = getSourceManager(); SourceLocation ExpandedImportLoc = SourceMgr.getExpansionLoc(ImportLoc); @@ -1015,98 +862,236 @@ ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc, if (!CurFile) CurFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); - // Search for a module with the given name. - std::string UmbrellaHeader; - std::string ModuleFileName; - const FileEntry *ModuleFile - = PP->getHeaderSearchInfo().lookupModule(ModuleName.getName(), - &ModuleFileName, - &UmbrellaHeader); - - bool BuildingModule = false; - if (!ModuleFile && !UmbrellaHeader.empty()) { - // We didn't find the module, but there is an umbrella header that - // can be used to create the module file. Create a separate compilation - // module to do so. - - // Check whether there is a cycle in the module graph. - SmallVectorImpl<std::string> &ModuleBuildPath - = getPreprocessorOpts().ModuleBuildPath; - SmallVectorImpl<std::string>::iterator Pos - = std::find(ModuleBuildPath.begin(), ModuleBuildPath.end(), - ModuleName.getName()); - if (Pos != ModuleBuildPath.end()) { - llvm::SmallString<256> CyclePath; - for (; Pos != ModuleBuildPath.end(); ++Pos) { - CyclePath += *Pos; - CyclePath += " -> "; - } - CyclePath += ModuleName.getName(); - - getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle) - << ModuleName.getName() << CyclePath; + StringRef ModuleName = Path[0].first->getName(); + SourceLocation ModuleNameLoc = Path[0].second; + + clang::Module *Module = 0; + + // If we don't already have information on this module, load the module now. + llvm::DenseMap<const IdentifierInfo *, clang::Module *>::iterator Known + = KnownModules.find(Path[0].first); + if (Known != KnownModules.end()) { + // Retrieve the cached top-level module. + Module = Known->second; + } else if (ModuleName == getLangOpts().CurrentModule) { + // This is the module we're building. + Module = PP->getHeaderSearchInfo().getModuleMap().findModule(ModuleName); + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } else { + // Search for a module with the given name. + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + std::string ModuleFileName; + if (Module) + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(Module); + else + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(ModuleName); + + if (ModuleFileName.empty()) { + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = 0; return 0; } + + const FileEntry *ModuleFile + = getFileManager().getFile(ModuleFileName, /*OpenFile=*/false, + /*CacheFailure=*/false); + bool BuildingModule = false; + if (!ModuleFile && Module) { + // The module is not cached, but we have a module map from which we can + // build the module. + + // Check whether there is a cycle in the module graph. + SmallVectorImpl<std::string> &ModuleBuildPath + = getPreprocessorOpts().ModuleBuildPath; + SmallVectorImpl<std::string>::iterator Pos + = std::find(ModuleBuildPath.begin(), ModuleBuildPath.end(), ModuleName); + if (Pos != ModuleBuildPath.end()) { + SmallString<256> CyclePath; + for (; Pos != ModuleBuildPath.end(); ++Pos) { + CyclePath += *Pos; + CyclePath += " -> "; + } + CyclePath += ModuleName; - getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build) - << ModuleName.getName(); - BuildingModule = true; - compileModule(*this, ModuleName.getName(), ModuleFileName, UmbrellaHeader); - ModuleFile = PP->getHeaderSearchInfo().lookupModule(ModuleName.getName()); - } + getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle) + << ModuleName << CyclePath; + return 0; + } - if (!ModuleFile) { - getDiagnostics().Report(ModuleNameLoc, - BuildingModule? diag::err_module_not_built - : diag::err_module_not_found) - << ModuleName.getName() - << SourceRange(ImportLoc, ModuleNameLoc); - return 0; - } + getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build) + << ModuleName; + BuildingModule = true; + compileModule(*this, Module, ModuleFileName); + ModuleFile = FileMgr->getFile(ModuleFileName); + } - // If we don't already have an ASTReader, create one now. - if (!ModuleManager) { - if (!hasASTContext()) - createASTContext(); - - std::string Sysroot = getHeaderSearchOpts().Sysroot; - const PreprocessorOptions &PPOpts = getPreprocessorOpts(); - ModuleManager = new ASTReader(getPreprocessor(), *Context, - Sysroot.empty() ? "" : Sysroot.c_str(), - PPOpts.DisablePCHValidation, - PPOpts.DisableStatCache); - if (hasASTConsumer()) { - ModuleManager->setDeserializationListener( - getASTConsumer().GetASTDeserializationListener()); - getASTContext().setASTMutationListener( - getASTConsumer().GetASTMutationListener()); + if (!ModuleFile) { + getDiagnostics().Report(ModuleNameLoc, + BuildingModule? diag::err_module_not_built + : diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + return 0; } - llvm::OwningPtr<ExternalASTSource> Source; - Source.reset(ModuleManager); - getASTContext().setExternalSource(Source); - if (hasSema()) - ModuleManager->InitializeSema(getSema()); - if (hasASTConsumer()) - ModuleManager->StartTranslationUnit(&getASTConsumer()); - } - // Try to load the module we found. - switch (ModuleManager->ReadAST(ModuleFile->getName(), - serialization::MK_Module)) { - case ASTReader::Success: - break; + // If we don't already have an ASTReader, create one now. + if (!ModuleManager) { + if (!hasASTContext()) + createASTContext(); + + std::string Sysroot = getHeaderSearchOpts().Sysroot; + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + ModuleManager = new ASTReader(getPreprocessor(), *Context, + Sysroot.empty() ? "" : Sysroot.c_str(), + PPOpts.DisablePCHValidation, + PPOpts.DisableStatCache); + if (hasASTConsumer()) { + ModuleManager->setDeserializationListener( + getASTConsumer().GetASTDeserializationListener()); + getASTContext().setASTMutationListener( + getASTConsumer().GetASTMutationListener()); + } + OwningPtr<ExternalASTSource> Source; + Source.reset(ModuleManager); + getASTContext().setExternalSource(Source); + if (hasSema()) + ModuleManager->InitializeSema(getSema()); + if (hasASTConsumer()) + ModuleManager->StartTranslationUnit(&getASTConsumer()); + } - case ASTReader::IgnorePCH: - // FIXME: The ASTReader will already have complained, but can we showhorn - // that diagnostic information into a more useful form? - return 0; + // Try to load the module we found. + switch (ModuleManager->ReadAST(ModuleFile->getName(), + serialization::MK_Module)) { + case ASTReader::Success: + break; - case ASTReader::Failure: - // Already complained. + case ASTReader::IgnorePCH: + // FIXME: The ASTReader will already have complained, but can we showhorn + // that diagnostic information into a more useful form? + KnownModules[Path[0].first] = 0; + return 0; + + case ASTReader::Failure: + // Already complained, but note now that we failed. + KnownModules[Path[0].first] = 0; + return 0; + } + + if (!Module) { + // If we loaded the module directly, without finding a module map first, + // we'll have loaded the module's information from the module itself. + Module = PP->getHeaderSearchInfo().getModuleMap() + .findModule((Path[0].first->getName())); + } + + // Cache the result of this top-level module lookup for later. + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } + + // If we never found the module, fail. + if (!Module) return 0; + + // Verify that the rest of the module path actually corresponds to + // a submodule. + if (Path.size() > 1) { + for (unsigned I = 1, N = Path.size(); I != N; ++I) { + StringRef Name = Path[I].first->getName(); + clang::Module *Sub = Module->findSubmodule(Name); + + if (!Sub) { + // Attempt to perform typo correction to find a module name that works. + llvm::SmallVector<StringRef, 2> Best; + unsigned BestEditDistance = (std::numeric_limits<unsigned>::max)(); + + for (clang::Module::submodule_iterator J = Module->submodule_begin(), + JEnd = Module->submodule_end(); + J != JEnd; ++J) { + unsigned ED = Name.edit_distance((*J)->Name, + /*AllowReplacements=*/true, + BestEditDistance); + if (ED <= BestEditDistance) { + if (ED < BestEditDistance) { + Best.clear(); + BestEditDistance = ED; + } + + Best.push_back((*J)->Name); + } + } + + // If there was a clear winner, user it. + if (Best.size() == 1) { + getDiagnostics().Report(Path[I].second, + diag::err_no_submodule_suggest) + << Path[I].first << Module->getFullModuleName() << Best[0] + << SourceRange(Path[0].second, Path[I-1].second) + << FixItHint::CreateReplacement(SourceRange(Path[I].second), + Best[0]); + + Sub = Module->findSubmodule(Best[0]); + } + } + + if (!Sub) { + // No submodule by this name. Complain, and don't look for further + // submodules. + getDiagnostics().Report(Path[I].second, diag::err_no_submodule) + << Path[I].first << Module->getFullModuleName() + << SourceRange(Path[0].second, Path[I-1].second); + break; + } + + Module = Sub; + } } + + // Make the named module visible, if it's not already part of the module + // we are parsing. + if (ModuleName != getLangOpts().CurrentModule) { + if (!Module->IsFromModuleFile) { + // We have an umbrella header or directory that doesn't actually include + // all of the headers within the directory it covers. Complain about + // this missing submodule and recover by forgetting that we ever saw + // this submodule. + // FIXME: Should we detect this at module load time? It seems fairly + // expensive (and rare). + getDiagnostics().Report(ImportLoc, diag::warn_missing_submodule) + << Module->getFullModuleName() + << SourceRange(Path.front().second, Path.back().second); + + return 0; + } - // FIXME: The module file's FileEntry makes a poor key indeed! - return (ModuleKey)ModuleFile; -} + // Check whether this module is available. + StringRef Feature; + if (!Module->isAvailable(getLangOpts(), getTarget(), Feature)) { + getDiagnostics().Report(ImportLoc, diag::err_module_unavailable) + << Module->getFullModuleName() + << Feature + << SourceRange(Path.front().second, Path.back().second); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = 0; + return 0; + } + ModuleManager->makeModuleVisible(Module, Visibility); + } + + // If this module import was due to an inclusion directive, create an + // implicit import declaration to capture it in the AST. + if (IsInclusionDirective && hasASTContext()) { + TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); + TU->addDecl(ImportDecl::CreateImplicit(getASTContext(), TU, + ImportLoc, Module, + Path.back().second)); + } + + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = Module; + return Module; +} |