diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp | 674 |
1 files changed, 578 insertions, 96 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp index c58e3af..5526487 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -19,12 +19,13 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PTHManager.h" -#include "clang/Frontend/ChainedDiagnosticClient.h" +#include "clang/Frontend/ChainedDiagnosticConsumer.h" #include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/LogDiagnosticPrinter.h" #include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Frontend/VerifyDiagnosticsClient.h" +#include "clang/Frontend/VerifyDiagnosticConsumer.h" #include "clang/Frontend/Utils.h" #include "clang/Serialization/ASTReader.h" #include "clang/Sema/CodeCompleteConsumer.h" @@ -38,11 +39,25 @@ #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/system_error.h" +#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() - : Invocation(new CompilerInvocation()) { + : Invocation(new CompilerInvocation()), ModuleManager(0) { } CompilerInstance::~CompilerInstance() { @@ -52,7 +67,7 @@ void CompilerInstance::setInvocation(CompilerInvocation *Value) { Invocation = Value; } -void CompilerInstance::setDiagnostics(Diagnostic *Value) { +void CompilerInstance::setDiagnostics(DiagnosticsEngine *Value) { Diagnostics = Value; } @@ -64,7 +79,7 @@ void CompilerInstance::setFileManager(FileManager *Value) { FileMgr = Value; } -void CompilerInstance::setSourceManager(SourceManager *Value) { +void CompilerInstance::setSourceManager(SourceManager *Value) { SourceMgr = Value; } @@ -87,9 +102,9 @@ void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { // Diagnostics static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts, unsigned argc, const char* const *argv, - Diagnostic &Diags) { + DiagnosticsEngine &Diags) { std::string ErrorInfo; - llvm::OwningPtr<llvm::raw_ostream> OS( + llvm::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) @@ -103,17 +118,17 @@ static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts, (*OS) << '\n'; // Chain in a diagnostic client which will log the diagnostics. - DiagnosticClient *Logger = + DiagnosticConsumer *Logger = new TextDiagnosticPrinter(*OS.take(), DiagOpts, /*OwnsOutputStream=*/true); - Diags.setClient(new ChainedDiagnosticClient(Diags.takeClient(), Logger)); + Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), Logger)); } static void SetUpDiagnosticLog(const DiagnosticOptions &DiagOpts, const CodeGenOptions *CodeGenOpts, - Diagnostic &Diags) { + DiagnosticsEngine &Diags) { std::string ErrorInfo; bool OwnsStream = false; - llvm::raw_ostream *OS = &llvm::errs(); + raw_ostream *OS = &llvm::errs(); if (DiagOpts.DiagnosticLogFile != "-") { // Create the output stream. llvm::raw_fd_ostream *FileOS( @@ -135,38 +150,47 @@ static void SetUpDiagnosticLog(const DiagnosticOptions &DiagOpts, OwnsStream); if (CodeGenOpts) Logger->setDwarfDebugFlags(CodeGenOpts->DwarfDebugFlags); - Diags.setClient(new ChainedDiagnosticClient(Diags.takeClient(), Logger)); + Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), Logger)); } void CompilerInstance::createDiagnostics(int Argc, const char* const *Argv, - DiagnosticClient *Client) { + DiagnosticConsumer *Client, + bool ShouldOwnClient, + bool ShouldCloneClient) { Diagnostics = createDiagnostics(getDiagnosticOpts(), Argc, Argv, Client, + ShouldOwnClient, ShouldCloneClient, &getCodeGenOpts()); } -llvm::IntrusiveRefCntPtr<Diagnostic> +llvm::IntrusiveRefCntPtr<DiagnosticsEngine> CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, int Argc, const char* const *Argv, - DiagnosticClient *Client, + DiagnosticConsumer *Client, + bool ShouldOwnClient, + bool ShouldCloneClient, const CodeGenOptions *CodeGenOpts) { llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<Diagnostic> Diags(new Diagnostic(DiagID)); + llvm::IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(new DiagnosticsEngine(DiagID)); // Create the diagnostic client for reporting errors or for // implementing -verify. - if (Client) - Diags->setClient(Client); - else + if (Client) { + if (ShouldCloneClient) + Diags->setClient(Client->clone(*Diags), ShouldOwnClient); + else + Diags->setClient(Client, ShouldOwnClient); + } else Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); // Chain in -verify checker, if requested. if (Opts.VerifyDiagnostics) - Diags->setClient(new VerifyDiagnosticsClient(*Diags, Diags->takeClient())); + Diags->setClient(new VerifyDiagnosticConsumer(*Diags)); // Chain in -diagnostic-log-file dumper, if requested. if (!Opts.DiagnosticLogFile.empty()) SetUpDiagnosticLog(Opts, CodeGenOpts, *Diags); - + if (!Opts.DumpBuildInformation.empty()) SetUpBuildDumpLog(Opts, Argc, Argv, *Diags); @@ -191,49 +215,47 @@ void CompilerInstance::createSourceManager(FileManager &FileMgr) { // Preprocessor void CompilerInstance::createPreprocessor() { - PP = createPreprocessor(getDiagnostics(), getLangOpts(), - getPreprocessorOpts(), getHeaderSearchOpts(), - getDependencyOutputOpts(), getTarget(), - getFrontendOpts(), getSourceManager(), - getFileManager()); -} - -Preprocessor * -CompilerInstance::createPreprocessor(Diagnostic &Diags, - const LangOptions &LangInfo, - const PreprocessorOptions &PPOpts, - const HeaderSearchOptions &HSOpts, - const DependencyOutputOptions &DepOpts, - const TargetInfo &Target, - const FrontendOptions &FEOpts, - SourceManager &SourceMgr, - FileManager &FileMgr) { + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + // Create a PTH manager if we are using some form of a token cache. PTHManager *PTHMgr = 0; if (!PPOpts.TokenCache.empty()) - PTHMgr = PTHManager::Create(PPOpts.TokenCache, Diags); + PTHMgr = PTHManager::Create(PPOpts.TokenCache, getDiagnostics()); // Create the Preprocessor. - HeaderSearch *HeaderInfo = new HeaderSearch(FileMgr); - Preprocessor *PP = new Preprocessor(Diags, LangInfo, Target, - SourceMgr, *HeaderInfo, PTHMgr, - /*OwnsHeaderSearch=*/true); + HeaderSearch *HeaderInfo = new HeaderSearch(getFileManager()); + PP = new Preprocessor(getDiagnostics(), getLangOpts(), &getTarget(), + getSourceManager(), *HeaderInfo, *this, PTHMgr, + /*OwnsHeaderSearch=*/true); // Note that this is different then passing PTHMgr to Preprocessor's ctor. // That argument is used as the IdentifierInfoLookup argument to // IdentifierTable's ctor. if (PTHMgr) { - PTHMgr->setPreprocessor(PP); + PTHMgr->setPreprocessor(&*PP); PP->setPTHManager(PTHMgr); } if (PPOpts.DetailedRecord) PP->createPreprocessingRecord( - PPOpts.DetailedRecordIncludesNestedMacroExpansions); - - InitializePreprocessor(*PP, PPOpts, HSOpts, FEOpts); + PPOpts.DetailedRecordIncludesNestedMacroExpansions); + + InitializePreprocessor(*PP, PPOpts, getHeaderSearchOpts(), getFrontendOpts()); + + // Set up the module path, including the hash for the + // module-creation options. + llvm::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()); // Handle generating dependencies, if requested. + const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); if (!DepOpts.OutputFile.empty()) AttachDependencyFileGen(*PP, DepOpts); @@ -241,14 +263,12 @@ CompilerInstance::createPreprocessor(Diagnostic &Diags, if (DepOpts.ShowHeaderIncludes) AttachHeaderIncludeGen(*PP); if (!DepOpts.HeaderIncludeOutputFile.empty()) { - llvm::StringRef OutputPath = DepOpts.HeaderIncludeOutputFile; + StringRef OutputPath = DepOpts.HeaderIncludeOutputFile; if (OutputPath == "-") OutputPath = ""; AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/true, OutputPath, /*ShowDepth=*/false); } - - return PP; } // ASTContext @@ -256,30 +276,31 @@ CompilerInstance::createPreprocessor(Diagnostic &Diags, void CompilerInstance::createASTContext() { Preprocessor &PP = getPreprocessor(); Context = new ASTContext(getLangOpts(), PP.getSourceManager(), - getTarget(), PP.getIdentifierTable(), + &getTarget(), PP.getIdentifierTable(), PP.getSelectorTable(), PP.getBuiltinInfo(), /*size_reserve=*/ 0); } // ExternalASTSource -void CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path, +void CompilerInstance::createPCHExternalASTSource(StringRef Path, bool DisablePCHValidation, bool DisableStatCache, void *DeserializationListener){ llvm::OwningPtr<ExternalASTSource> Source; bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot, - DisablePCHValidation, + DisablePCHValidation, DisableStatCache, getPreprocessor(), getASTContext(), DeserializationListener, Preamble)); + ModuleManager = static_cast<ASTReader*>(Source.get()); getASTContext().setExternalSource(Source); } ExternalASTSource * -CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path, +CompilerInstance::createPCHExternalASTSource(StringRef Path, const std::string &Sysroot, bool DisablePCHValidation, bool DisableStatCache, @@ -288,14 +309,15 @@ CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path, void *DeserializationListener, bool Preamble) { llvm::OwningPtr<ASTReader> Reader; - Reader.reset(new ASTReader(PP, &Context, - Sysroot.empty() ? 0 : Sysroot.c_str(), + Reader.reset(new ASTReader(PP, Context, + Sysroot.empty() ? "" : Sysroot.c_str(), DisablePCHValidation, DisableStatCache)); Reader->setDeserializationListener( static_cast<ASTDeserializationListener *>(DeserializationListener)); switch (Reader->ReadAST(Path, - Preamble ? ASTReader::Preamble : ASTReader::PCH)) { + Preamble ? serialization::MK_Preamble + : serialization::MK_PCH)) { case ASTReader::Success: // Set the predefines buffer as suggested by the PCH reader. Typically, the // predefines buffer will be empty. @@ -316,7 +338,7 @@ CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path, // Code Completion -static bool EnableCodeCompletion(Preprocessor &PP, +static bool EnableCodeCompletion(Preprocessor &PP, const std::string &Filename, unsigned Line, unsigned Column) { @@ -371,19 +393,19 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP, bool ShowMacros, bool ShowCodePatterns, bool ShowGlobals, - llvm::raw_ostream &OS) { + raw_ostream &OS) { if (EnableCodeCompletion(PP, Filename, Line, Column)) return 0; // Set up the creation routine for code-completion. - return new PrintingCodeCompleteConsumer(ShowMacros, ShowCodePatterns, + return new PrintingCodeCompleteConsumer(ShowMacros, ShowCodePatterns, ShowGlobals, OS); } -void CompilerInstance::createSema(bool CompleteTranslationUnit, +void CompilerInstance::createSema(TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) { TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), - CompleteTranslationUnit, CompletionConsumer)); + TUKind, CompletionConsumer)); } // Output Files @@ -418,28 +440,30 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) { } } else if (!it->Filename.empty() && EraseFiles) llvm::sys::Path(it->Filename).eraseFromDisk(); - + } OutputFiles.clear(); } llvm::raw_fd_ostream * CompilerInstance::createDefaultOutputFile(bool Binary, - llvm::StringRef InFile, - llvm::StringRef Extension) { + StringRef InFile, + StringRef Extension) { return createOutputFile(getFrontendOpts().OutputFile, Binary, /*RemoveFileOnSignal=*/true, InFile, Extension); } llvm::raw_fd_ostream * -CompilerInstance::createOutputFile(llvm::StringRef OutputPath, +CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, - llvm::StringRef InFile, - llvm::StringRef Extension) { + StringRef InFile, + StringRef Extension, + bool UseTemporary) { std::string Error, OutputPathName, TempPathName; llvm::raw_fd_ostream *OS = createOutputFile(OutputPath, Error, Binary, RemoveFileOnSignal, InFile, Extension, + UseTemporary, &OutputPathName, &TempPathName); if (!OS) { @@ -457,12 +481,13 @@ CompilerInstance::createOutputFile(llvm::StringRef OutputPath, } llvm::raw_fd_ostream * -CompilerInstance::createOutputFile(llvm::StringRef OutputPath, +CompilerInstance::createOutputFile(StringRef OutputPath, std::string &Error, bool Binary, bool RemoveFileOnSignal, - llvm::StringRef InFile, - llvm::StringRef Extension, + StringRef InFile, + StringRef Extension, + bool UseTemporary, std::string *ResultPathName, std::string *TempPathName) { std::string OutFile, TempFile; @@ -478,8 +503,11 @@ CompilerInstance::createOutputFile(llvm::StringRef OutputPath, } else { OutFile = "-"; } - - if (OutFile != "-") { + + llvm::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. @@ -487,21 +515,26 @@ CompilerInstance::createOutputFile(llvm::StringRef OutputPath, if ((llvm::sys::fs::exists(OutPath.str(), Exists) || !Exists) || (OutPath.isRegularFile() && OutPath.canWrite())) { // Create a temporary file. - llvm::sys::Path TempPath(OutFile); - if (!TempPath.createTemporaryFileOnDisk()) - TempFile = TempPath.str(); + llvm::SmallString<128> TempPath; + TempPath = OutFile; + TempPath += "-%%%%%%%%"; + int fd; + if (llvm::sys::fs::unique_file(TempPath.str(), fd, TempPath, + /*makeAbsolute=*/false) == llvm::errc::success) { + OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); + OSFile = TempFile = TempPath.str(); + } } } - std::string OSFile = OutFile; - if (!TempFile.empty()) - OSFile = TempFile; - - llvm::OwningPtr<llvm::raw_fd_ostream> OS( - new llvm::raw_fd_ostream(OSFile.c_str(), Error, - (Binary ? llvm::raw_fd_ostream::F_Binary : 0))); - if (!Error.empty()) - return 0; + if (!OS) { + OSFile = OutFile; + OS.reset( + new llvm::raw_fd_ostream(OSFile.c_str(), Error, + (Binary ? llvm::raw_fd_ostream::F_Binary : 0))); + if (!Error.empty()) + return 0; + } // Make sure the out stream file gets removed if we crash. if (RemoveFileOnSignal) @@ -517,21 +550,18 @@ CompilerInstance::createOutputFile(llvm::StringRef OutputPath, // Initialization Utilities -bool CompilerInstance::InitializeSourceManager(llvm::StringRef InputFile) { +bool CompilerInstance::InitializeSourceManager(StringRef InputFile) { return InitializeSourceManager(InputFile, getDiagnostics(), getFileManager(), getSourceManager(), getFrontendOpts()); } -bool CompilerInstance::InitializeSourceManager(llvm::StringRef InputFile, - Diagnostic &Diags, +bool CompilerInstance::InitializeSourceManager(StringRef InputFile, + DiagnosticsEngine &Diags, FileManager &FileMgr, SourceManager &SourceMgr, const FrontendOptions &Opts) { - // Figure out where to get and map in the main file, unless it's already - // been created (e.g., by a precompiled preamble). - if (!SourceMgr.getMainFileID().isInvalid()) { - // Do nothing: the main file has already been set. - } else if (InputFile != "-") { + // Figure out where to get and map in the main file. + if (InputFile != "-") { const FileEntry *File = FileMgr.getFile(InputFile); if (!File) { Diags.Report(diag::err_fe_error_reading) << InputFile; @@ -565,7 +595,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { // FIXME: Take this as an argument, once all the APIs we used have moved to // taking it as an input instead of hard-coding llvm::errs. - llvm::raw_ostream &OS = llvm::errs(); + raw_ostream &OS = llvm::errs(); // Create the target instance. setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), getTargetOpts())); @@ -589,7 +619,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { if (getFrontendOpts().ShowStats) llvm::EnableStatistics(); - + for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) { const std::string &InFile = getFrontendOpts().Inputs[i].second; @@ -608,7 +638,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { // Get the total number of warnings/errors from the client. unsigned NumWarnings = getDiagnostics().getClient()->getNumWarnings(); unsigned NumErrors = getDiagnostics().getClient()->getNumErrors(); - + if (NumWarnings) OS << NumWarnings << " warning" << (NumWarnings == 1 ? "" : "s"); if (NumWarnings && NumErrors) @@ -627,4 +657,456 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { return !getDiagnostics().getClient()->getNumErrors(); } +/// \brief Determine the appropriate source input kind based on language +/// options. +static InputKind getSourceInputKindFromOptions(const LangOptions &LangOpts) { + if (LangOpts.OpenCL) + return IK_OpenCL; + if (LangOpts.CUDA) + return IK_CUDA; + if (LangOpts.ObjC1) + return LangOpts.CPlusPlus? IK_ObjCXX : IK_ObjC; + return LangOpts.CPlusPlus? IK_CXX : IK_C; +} + +namespace { + struct CompileModuleData { + CompilerInstance &Instance; + GeneratePCHAction &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); + 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. +static void compileModule(CompilerInstance &ImportingInstance, + StringRef ModuleName, + StringRef ModuleFileName, + StringRef UmbrellaHeader) { + LockFileManager Locked(ModuleFileName); + switch (Locked) { + case LockFileManager::LFS_Error: + return; + + case LockFileManager::LFS_Owned: + // We're responsible for building the module ourselves. Do so below. + break; + + case LockFileManager::LFS_Shared: + // Someone else is responsible for building the module. Wait for them to + // finish. + Locked.waitForUnlock(); + break; + } + + // Construct a compiler invocation for creating this module. + llvm::IntrusiveRefCntPtr<CompilerInvocation> Invocation + (new CompilerInvocation(ImportingInstance.getInvocation())); + + // 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(); + + // 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); + + // 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)); + + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; + + + assert(ImportingInstance.getInvocation().getModuleHash() == + Invocation->getModuleHash() && "Module hash mismatch!"); + + // Construct a compiler instance that will be used to actually create the + // module. + CompilerInstance Instance; + Instance.setInvocation(&*Invocation); + Instance.createDiagnostics(/*argc=*/0, /*argv=*/0, + &ImportingInstance.getDiagnosticClient(), + /*ShouldOwnClient=*/true, + /*ShouldCloneClient=*/true); + + // Construct a module-generating action. + GeneratePCHAction CreateModuleAction(true); + + // 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); +} + +ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc, + IdentifierInfo &ModuleName, + SourceLocation ModuleNameLoc) { + // Determine what file we're searching from. + SourceManager &SourceMgr = getSourceManager(); + SourceLocation ExpandedImportLoc = SourceMgr.getExpansionLoc(ImportLoc); + const FileEntry *CurFile + = SourceMgr.getFileEntryForID(SourceMgr.getFileID(ExpandedImportLoc)); + 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; + return 0; + } + + getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build) + << ModuleName.getName(); + BuildingModule = true; + compileModule(*this, ModuleName.getName(), ModuleFileName, UmbrellaHeader); + ModuleFile = PP->getHeaderSearchInfo().lookupModule(ModuleName.getName()); + } + + if (!ModuleFile) { + getDiagnostics().Report(ModuleNameLoc, + BuildingModule? diag::err_module_not_built + : diag::err_module_not_found) + << ModuleName.getName() + << SourceRange(ImportLoc, ModuleNameLoc); + return 0; + } + + // 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()); + } + 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; + + case ASTReader::IgnorePCH: + // FIXME: The ASTReader will already have complained, but can we showhorn + // that diagnostic information into a more useful form? + return 0; + + case ASTReader::Failure: + // Already complained. + return 0; + } + + // FIXME: The module file's FileEntry makes a poor key indeed! + return (ModuleKey)ModuleFile; +} |