From: Douglas Gregor Date: Fri, 11 Nov 2011 19:10:28 +0000 (+0000) Subject: Introduce basic support for parsing module map files. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a30cfe5026b12c28b7b575b48176e0a3543ce939;p=clang Introduce basic support for parsing module map files. Module map files provide a way to map between headers and modules, so that we can layer a module system on top of existing headers without changing those headers at all. This commit introduces the module map file parser and the module map that it generates, and wires up the module map file parser so that we'll automatically find module map files as part of header search. Note that we don't yet use the information stored in the module map. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144402 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index f1e8c75d6b..97095e792c 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -374,4 +374,19 @@ def err_pp_include_in_arc_cf_code_audited : Error< def err_pp_eof_in_arc_cf_code_audited : Error< "'#pragma clang arc_cf_code_audited' was not ended within this file">; +// Module map parsing +def err_mmap_unknown_token : Error<"skipping stray token">; +def err_mmap_expected_module : Error<"expected module declaration">; +def err_mmap_expected_module_after_explicit : Error< + "expected 'module' keyword after 'explicit'">; +def err_mmap_expected_module_name : Error<"expected module name">; +def err_mmap_expected_lbrace : Error<"expected '{' to start module '%0'">; +def err_mmap_expected_rbrace : Error<"expected '}'">; +def note_mmap_lbrace_match : Note<"to match this '{'">; +def err_mmap_expected_member : Error< + "expected umbrella header, header, or submodule">; +def err_mmap_expected_header : Error<"expected a header name after '%0'">; +def err_mmap_module_redefinition : Error< + "redefinition of module '%0'">; +def note_mmap_prev_definition : Note<"previously defined here">; } diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index 0b21373858..995deab9dc 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_LEX_HEADERSEARCH_H #include "clang/Lex/DirectoryLookup.h" +#include "clang/Lex/ModuleMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Allocator.h" @@ -151,7 +152,6 @@ class HeaderSearch { llvm::StringMap, llvm::BumpPtrAllocator> LookupFileCache; - /// FrameworkMap - This is a collection mapping a framework or subframework /// name like "Carbon" to the Carbon.framework directory. llvm::StringMap @@ -161,6 +161,12 @@ class HeaderSearch { /// headermaps. This vector owns the headermap. std::vector > HeaderMaps; + /// \brief The mapping between modules and headers. + ModuleMap ModMap; + + /// \brief Describes whether a given directory has a module map in it. + llvm::DenseMap DirectoryHasModuleMap; + /// \brief Uniqued set of framework names, which is used to track which /// headers were included as framework headers. llvm::StringSet FrameworkNames; @@ -347,6 +353,20 @@ public: void IncrementFrameworkLookupCount() { ++NumFrameworkLookups; } + /// \brief Determine whether there is a module map that may map the header + /// with the given file name to a (sub)module. + /// + /// \param Filename The name of the file. + /// + /// \param Root The "root" directory, at which we should stop looking for + /// module maps. + bool hasModuleMap(StringRef Filename, const DirectoryEntry *Root); + + /// \brief Retrieve the module that corresponds to the given file, if any. + /// + /// FIXME: This will need to be generalized for submodules. + StringRef getModuleForHeader(const FileEntry *File); + typedef std::vector::const_iterator header_file_iterator; header_file_iterator header_file_begin() const { return FileInfo.begin(); } header_file_iterator header_file_end() const { return FileInfo.end(); } diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h new file mode 100644 index 0000000000..2834f6a11b --- /dev/null +++ b/include/clang/Lex/ModuleMap.h @@ -0,0 +1,124 @@ +//===--- ModuleMap.h - Describe the layout of modules -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ModuleMap interface, which describes the layout of a +// module as it relates to headers. +// +//===----------------------------------------------------------------------===// + + +#ifndef LLVM_CLANG_LEX_MODULEMAP_H +#define LLVM_CLANG_LEX_MODULEMAP_H + +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringMap.h" +#include + +namespace clang { + +class FileEntry; +class FileManager; +class DiagnosticConsumer; +class DiagnosticsEngine; +class ModuleMapParser; + +class ModuleMap { +public: + /// \brief Describes a module or submodule. + struct Module { + /// \brief The name of this module. + std::string Name; + + /// \brief The location of the module definition. + SourceLocation DefinitionLoc; + + /// \brief The parent of this module. This will be NULL for the top-level + /// module. + Module *Parent; + + /// \brief The umbrella header, if any. + /// + /// Only the top-level module can have an umbrella header. + const FileEntry *UmbrellaHeader; + + /// \brief The submodules of this module, indexed by name. + llvm::StringMap SubModules; + + /// \brief The headers that are part of this module. + llvm::SmallVector Headers; + + /// \brief Whether this is an explicit submodule. + bool IsExplicit; + + /// \brief Construct a top-level module. + explicit Module(StringRef Name, SourceLocation DefinitionLoc) + : Name(Name), DefinitionLoc(DefinitionLoc), Parent(0), UmbrellaHeader(0), + IsExplicit(false) { } + + /// \brief Construct a new module or submodule. + Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, + bool IsExplicit) + : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent), + UmbrellaHeader(0), IsExplicit(IsExplicit) { + } + + /// \brief Determine whether this module is a submodule. + bool isSubModule() const { return Parent != 0; } + + /// \brief Retrieve the full name of this module, including the path from + /// its top-level module. + std::string getFullModuleName() const; + }; + +private: + SourceManager *SourceMgr; + llvm::IntrusiveRefCntPtr Diags; + LangOptions LangOpts; + + /// \brief The top-level modules that are known + llvm::StringMap Modules; + + /// \brief Mapping from each header to the module that owns the contents of the + /// that header. + llvm::DenseMap Headers; + + friend class ModuleMapParser; + +public: + /// \brief Construct a new module map. + /// + /// \param FileMgr The file manager used to find module files and headers. + /// This file manager should be shared with the header-search mechanism, since + /// they will refer to the same headers. + /// + /// \param DC A diagnostic consumer that will be cloned for use in generating + /// diagnostics. + ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC); + + ~ModuleMap(); + + /// \brief Parse the given module map file, and record any modules we + /// encounter. + /// + /// \param File The file to be parsed. + /// + /// \returns true if an error occurred, false otherwise. + bool parseModuleMapFile(const FileEntry *File); + + /// \brief Dump the contents of the module map, for debugging purposes. + void dump(); +}; + +} +#endif diff --git a/lib/Lex/CMakeLists.txt b/lib/Lex/CMakeLists.txt index 80e2820101..39c4fd590d 100644 --- a/lib/Lex/CMakeLists.txt +++ b/lib/Lex/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(clangLex LiteralSupport.cpp MacroArgs.cpp MacroInfo.cpp + ModuleMap.cpp PPCaching.cpp PPDirectives.cpp PPExpressions.cpp diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 837b913d47..6403cfb868 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -13,6 +13,7 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/HeaderMap.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/Support/FileSystem.h" @@ -37,7 +38,8 @@ HeaderFileInfo::getControllingMacro(ExternalIdentifierLookup *External) { ExternalHeaderFileInfoSource::~ExternalHeaderFileInfoSource() {} HeaderSearch::HeaderSearch(FileManager &FM, DiagnosticsEngine &Diags) - : FileMgr(FM), Diags(Diags), FrameworkMap(64) + : FileMgr(FM), Diags(Diags), FrameworkMap(64), + ModMap(FileMgr, *Diags.getClient()) { AngledDirIdx = 0; SystemDirIdx = 0; @@ -192,6 +194,24 @@ const FileEntry *DirectoryLookup::LookupFile( RelativePath->clear(); RelativePath->append(Filename.begin(), Filename.end()); } + + // If we have a module map that might map this header, load it and + // check whether we'll have a suggestion for a module. + if (SuggestedModule && HS.hasModuleMap(TmpDir, getDir())) { + const FileEntry *File = HS.getFileMgr().getFile(TmpDir.str(), + /*openFile=*/false); + if (!File) + return File; + + // If there is a module that corresponds to this header, + // suggest it. + StringRef Module = HS.getModuleForHeader(File); + if (!Module.empty() && Module != BuildingModule) + *SuggestedModule = Module; + + return File; + } + return HS.getFileMgr().getFile(TmpDir.str(), /*openFile=*/true); } @@ -688,3 +708,73 @@ size_t HeaderSearch::getTotalMemory() const { StringRef HeaderSearch::getUniqueFrameworkName(StringRef Framework) { return FrameworkNames.GetOrCreateValue(Framework).getKey(); } + +bool HeaderSearch::hasModuleMap(StringRef FileName, + const DirectoryEntry *Root) { + llvm::SmallVector FixUpDirectories; + + StringRef DirName = FileName; + do { + // Get the parent directory name. + DirName = llvm::sys::path::parent_path(DirName); + if (DirName.empty()) + return false; + + // Determine whether this directory exists. + const DirectoryEntry *Dir = FileMgr.getDirectory(DirName); + if (!Dir) + return false; + + llvm::DenseMap::iterator + KnownDir = DirectoryHasModuleMap.find(Dir); + if (KnownDir != DirectoryHasModuleMap.end()) { + // We have seen this directory before. If it has no module map file, + // we're done. + if (!KnownDir->second) + return false; + + // All of the directories we stepped through inherit this module map + // file. + for (unsigned I = 0, N = FixUpDirectories.size(); I != N; ++I) + DirectoryHasModuleMap[FixUpDirectories[I]] = true; + + return true; + } + + // We have not checked for a module map file in this directory yet; + // do so now. + llvm::SmallString<128> ModuleMapFileName; + ModuleMapFileName += Dir->getName(); + llvm::sys::path::append(ModuleMapFileName, "module.map"); + if (const FileEntry *ModuleMapFile = FileMgr.getFile(ModuleMapFileName)) { + // We have found a module map file. Try to parse it. + if (!ModMap.parseModuleMapFile(ModuleMapFile)) { + // This directory has a module map. + DirectoryHasModuleMap[Dir] = true; + + // All of the directories we stepped through inherit this module map + // file. + for (unsigned I = 0, N = FixUpDirectories.size(); I != N; ++I) + DirectoryHasModuleMap[FixUpDirectories[I]] = true; + + return true; + } + } + + // This directory did not have a module map file. + DirectoryHasModuleMap[Dir] = false; + + // Keep track of all of the directories we checked, so we can mark them as + // having module maps if we eventually do find a module map. + FixUpDirectories.push_back(Dir); + } while (true); + + return false; +} + +StringRef HeaderSearch::getModuleForHeader(const FileEntry *File) { + // FIXME: Actually look for the corresponding module for this header. + return StringRef(); +} + + diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp new file mode 100644 index 0000000000..69bc52636c --- /dev/null +++ b/lib/Lex/ModuleMap.cpp @@ -0,0 +1,506 @@ +//===--- ModuleMap.cpp - Describe the layout of modules ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ModuleMap implementation, which describes the layout +// of a module as it relates to headers. +// +//===----------------------------------------------------------------------===// +#include "clang/Lex/ModuleMap.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/LiteralSupport.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +using namespace clang; + +//----------------------------------------------------------------------------// +// Module +//----------------------------------------------------------------------------// + +std::string ModuleMap::Module::getFullModuleName() const { + llvm::SmallVector Names; + + // Build up the set of module names (from innermost to outermost). + for (const Module *M = this; M; M = M->Parent) + Names.push_back(M->Name); + + std::string Result; + for (llvm::SmallVector::reverse_iterator I = Names.rbegin(), + IEnd = Names.rend(); + I != IEnd; ++I) { + if (!Result.empty()) + Result += '.'; + + Result += *I; + } + + return Result; +} + +//----------------------------------------------------------------------------// +// Module map +//----------------------------------------------------------------------------// + +ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC) { + llvm::IntrusiveRefCntPtr DiagIDs(new DiagnosticIDs); + Diags = llvm::IntrusiveRefCntPtr( + new DiagnosticsEngine(DiagIDs)); + Diags->setClient(DC.clone(*Diags), /*ShouldOwnClient=*/true); + SourceMgr = new SourceManager(*Diags, FileMgr); +} + +ModuleMap::~ModuleMap() { + delete SourceMgr; +} + +static void indent(llvm::raw_ostream &OS, unsigned Spaces) { + OS << std::string(' ', Spaces); +} + +static void dumpModule(llvm::raw_ostream &OS, ModuleMap::Module *M, + unsigned Indent) { + indent(OS, Indent); + if (M->IsExplicit) + OS << "explicit "; + OS << M->Name << " {\n"; + + if (M->UmbrellaHeader) { + indent(OS, Indent + 2); + OS << "umbrella \"" << M->UmbrellaHeader->getName() << "\"\n"; + } + + for (unsigned I = 0, N = M->Headers.size(); I != N; ++I) { + indent(OS, Indent + 2); + OS << "header \"" << M->Headers[I]->getName() << "\"\n"; + } + + for (llvm::StringMap::iterator + MI = M->SubModules.begin(), + MIEnd = M->SubModules.end(); + MI != MIEnd; ++MI) + dumpModule(llvm::errs(), MI->getValue(), Indent + 2); + + indent(OS, Indent); + OS << "}\n"; +} + +void ModuleMap::dump() { + llvm::errs() << "Modules:"; + for (llvm::StringMap::iterator M = Modules.begin(), + MEnd = Modules.end(); + M != MEnd; ++M) + dumpModule(llvm::errs(), M->getValue(), 2); + + llvm::errs() << "Headers:"; + for (llvm::DenseMap::iterator + H = Headers.begin(), + HEnd = Headers.end(); + H != HEnd; ++H) { + llvm::errs() << " \"" << H->first->getName() << "\" -> " + << H->second->getFullModuleName() << "\n"; + } +} + +//----------------------------------------------------------------------------// +// Module map file parser +//----------------------------------------------------------------------------// + +namespace clang { + /// \brief A token in a module map file. + struct MMToken { + enum TokenKind { + EndOfFile, + HeaderKeyword, + Identifier, + ExplicitKeyword, + ModuleKeyword, + UmbrellaKeyword, + StringLiteral, + LBrace, + RBrace + } Kind; + + unsigned Location; + unsigned StringLength; + const char *StringData; + + void clear() { + Kind = EndOfFile; + Location = 0; + StringLength = 0; + StringData = 0; + } + + bool is(TokenKind K) const { return Kind == K; } + + SourceLocation getLocation() const { + return SourceLocation::getFromRawEncoding(Location); + } + + StringRef getString() const { + return StringRef(StringData, StringLength); + } + }; + + class ModuleMapParser { + Lexer &L; + SourceManager &SourceMgr; + DiagnosticsEngine &Diags; + ModuleMap ⤅ + + /// \brief Whether an error occurred. + bool HadError; + + /// \brief Default target information, used only for string literal + /// parsing. + TargetInfo *Target; + + /// \brief Stores string data for the various string literals referenced + /// during parsing. + llvm::BumpPtrAllocator StringData; + + /// \brief The current token. + MMToken Tok; + + /// \brief The active module. + ModuleMap::Module *ActiveModule; + + /// \brief Consume the current token and return its location. + SourceLocation consumeToken(); + + /// \brief Skip tokens until we reach the a token with the given kind + /// (or the end of the file). + void skipUntil(MMToken::TokenKind K); + + void parseModuleDecl(); + void parseUmbrellaDecl(); + void parseHeaderDecl(); + + public: + typedef ModuleMap::Module Module; + + explicit ModuleMapParser(Lexer &L, SourceManager &SourceMgr, + DiagnosticsEngine &Diags, + ModuleMap &Map) + : L(L), SourceMgr(SourceMgr), Diags(Diags), Map(Map), HadError(false), + ActiveModule(0) + { + TargetOptions TargetOpts; + TargetOpts.Triple = llvm::sys::getDefaultTargetTriple(); + Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); + + Tok.clear(); + consumeToken(); + } + + bool parseModuleMapFile(); + }; +} + +SourceLocation ModuleMapParser::consumeToken() { +retry: + SourceLocation Result = Tok.getLocation(); + Tok.clear(); + + Token LToken; + L.LexFromRawLexer(LToken); + Tok.Location = LToken.getLocation().getRawEncoding(); + switch (LToken.getKind()) { + case tok::raw_identifier: + Tok.StringData = LToken.getRawIdentifierData(); + Tok.StringLength = LToken.getLength(); + Tok.Kind = llvm::StringSwitch(Tok.getString()) + .Case("header", MMToken::HeaderKeyword) + .Case("explicit", MMToken::ExplicitKeyword) + .Case("module", MMToken::ModuleKeyword) + .Case("umbrella", MMToken::UmbrellaKeyword) + .Default(MMToken::Identifier); + break; + + case tok::eof: + Tok.Kind = MMToken::EndOfFile; + break; + + case tok::l_brace: + Tok.Kind = MMToken::LBrace; + break; + + case tok::r_brace: + Tok.Kind = MMToken::RBrace; + break; + + case tok::string_literal: { + // Parse the string literal. + LangOptions LangOpts; + StringLiteralParser StringLiteral(<oken, 1, SourceMgr, LangOpts, *Target); + if (StringLiteral.hadError) + goto retry; + + // Copy the string literal into our string data allocator. + unsigned Length = StringLiteral.GetStringLength(); + char *Saved = StringData.Allocate(Length + 1); + memcpy(Saved, StringLiteral.GetString().data(), Length); + Saved[Length] = 0; + + // Form the token. + Tok.Kind = MMToken::StringLiteral; + Tok.StringData = Saved; + Tok.StringLength = Length; + break; + } + + case tok::comment: + goto retry; + + default: + Diags.Report(LToken.getLocation(), diag::err_mmap_unknown_token); + HadError = true; + goto retry; + } + + return Result; +} + +void ModuleMapParser::skipUntil(MMToken::TokenKind K) { + unsigned braceDepth = 0; + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + return; + + case MMToken::LBrace: + if (Tok.is(K) && braceDepth == 0) + return; + + ++braceDepth; + break; + + case MMToken::RBrace: + if (braceDepth > 0) + --braceDepth; + else if (Tok.is(K)) + return; + break; + + default: + if (braceDepth == 0 && Tok.is(K)) + return; + break; + } + + consumeToken(); + } while (true); +} + +/// \brief Parse a module declaration. +/// +/// module-declaration: +/// 'module' identifier { module-member* } +/// +/// module-member: +/// umbrella-declaration +/// header-declaration +/// 'explicit'[opt] module-declaration +void ModuleMapParser::parseModuleDecl() { + assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword)); + + // Parse 'explicit' keyword, if present. + bool Explicit = false; + if (Tok.is(MMToken::ExplicitKeyword)) { + consumeToken(); + Explicit = true; + } + + // Parse 'module' keyword. + if (!Tok.is(MMToken::ModuleKeyword)) { + Diags.Report(Tok.getLocation(), + diag::err_mmap_expected_module_after_explicit); + consumeToken(); + HadError = true; + return; + } + consumeToken(); // 'module' keyword + + // Parse the module name. + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name); + HadError = true; + return; + } + StringRef ModuleName = Tok.getString(); + SourceLocation ModuleNameLoc = consumeToken(); + + // Parse the opening brace. + if (!Tok.is(MMToken::LBrace)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace) + << ModuleName; + HadError = true; + return; + } + SourceLocation LBraceLoc = consumeToken(); + + // Determine whether this (sub)module has already been defined. + llvm::StringMap &ModuleSpace + = ActiveModule? ActiveModule->SubModules : Map.Modules; + llvm::StringMap::iterator ExistingModule + = ModuleSpace.find(ModuleName); + if (ExistingModule != ModuleSpace.end()) { + Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) + << ModuleName; + Diags.Report(ExistingModule->getValue()->DefinitionLoc, + diag::note_mmap_prev_definition); + + // Skip the module definition. + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + + HadError = true; + return; + } + + // Start defining this module. + ActiveModule = new Module(ModuleName, ModuleNameLoc, ActiveModule, Explicit); + ModuleSpace[ModuleName] = ActiveModule; + + bool Done = false; + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + case MMToken::RBrace: + Done = true; + break; + + case MMToken::ExplicitKeyword: + case MMToken::ModuleKeyword: + parseModuleDecl(); + break; + + case MMToken::HeaderKeyword: + parseHeaderDecl(); + break; + + case MMToken::UmbrellaKeyword: + parseUmbrellaDecl(); + break; + + default: + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member); + consumeToken(); + break; + } + } while (!Done); + + if (Tok.is(MMToken::RBrace)) + consumeToken(); + else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); + Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); + HadError = true; + } + + // We're done parsing this module. Pop back to our parent scope. + ActiveModule = ActiveModule->Parent; +} + +/// \brief Parse an umbrella header declaration. +/// +/// umbrella-declaration: +/// 'umbrella' string-literal +void ModuleMapParser::parseUmbrellaDecl() { + assert(Tok.is(MMToken::UmbrellaKeyword)); + SourceLocation UmbrellaLoc = consumeToken(); + + // Parse the header name. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << "umbrella"; + HadError = true; + return; + } + StringRef FileName = Tok.getString(); + SourceLocation FileNameLoc = consumeToken(); + + // FIXME: Record the umbrella header. +} + +/// \brief Parse a header declaration. +/// +/// header-declaration: +/// 'header' string-literal +void ModuleMapParser::parseHeaderDecl() { + assert(Tok.is(MMToken::HeaderKeyword)); + SourceLocation HeaderLoc = consumeToken(); + + // Parse the header name. + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) + << "header"; + HadError = true; + return; + } + StringRef FileName = Tok.getString(); + SourceLocation FileNameLoc = consumeToken(); + + // FIXME: Record the header. +} + +/// \brief Parse a module map file. +/// +/// module-map-file: +/// module-declaration* +bool ModuleMapParser::parseModuleMapFile() { + do { + switch (Tok.Kind) { + case MMToken::EndOfFile: + return HadError; + + case MMToken::ModuleKeyword: + parseModuleDecl(); + break; + + case MMToken::ExplicitKeyword: + case MMToken::HeaderKeyword: + case MMToken::Identifier: + case MMToken::LBrace: + case MMToken::RBrace: + case MMToken::StringLiteral: + case MMToken::UmbrellaKeyword: + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); + HadError = true; + consumeToken(); + break; + } + } while (true); + + return HadError; +} + +bool ModuleMap::parseModuleMapFile(const FileEntry *File) { + FileID ID = SourceMgr->createFileID(File, SourceLocation(), SrcMgr::C_User); + const llvm::MemoryBuffer *Buffer = SourceMgr->getBuffer(ID); + if (!Buffer) + return true; + + // Parse this module map file. + Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, LangOpts); + Diags->getClient()->BeginSourceFile(LangOpts); + ModuleMapParser Parser(L, *SourceMgr, *Diags, *this); + bool Result = Parser.parseModuleMapFile(); + Diags->getClient()->EndSourceFile(); + + return Result; +} diff --git a/test/Modules/Inputs/normal-module-map/a1.h b/test/Modules/Inputs/normal-module-map/a1.h new file mode 100644 index 0000000000..f2d5a49f37 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/a1.h @@ -0,0 +1 @@ +int a1; diff --git a/test/Modules/Inputs/normal-module-map/a2.h b/test/Modules/Inputs/normal-module-map/a2.h new file mode 100644 index 0000000000..5c4e7ff476 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/a2.h @@ -0,0 +1 @@ +int a2; diff --git a/test/Modules/Inputs/normal-module-map/b1.h b/test/Modules/Inputs/normal-module-map/b1.h new file mode 100644 index 0000000000..2ed111281e --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/b1.h @@ -0,0 +1,2 @@ +int b1; + diff --git a/test/Modules/Inputs/normal-module-map/module.map b/test/Modules/Inputs/normal-module-map/module.map new file mode 100644 index 0000000000..c372662c25 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/module.map @@ -0,0 +1,8 @@ +module libA { + header "a1.h" + header "a2.h" +} + +module libB { + header "b1.h" +} diff --git a/test/Modules/Inputs/normal-module-map/nested/module.map b/test/Modules/Inputs/normal-module-map/nested/module.map new file mode 100644 index 0000000000..fd463c2f08 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/nested/module.map @@ -0,0 +1,4 @@ +module libNested { + header "nested1.h" + header "nested2.h" +} \ No newline at end of file diff --git a/test/Modules/Inputs/normal-module-map/nested/nested1.h b/test/Modules/Inputs/normal-module-map/nested/nested1.h new file mode 100644 index 0000000000..3790d1a196 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/nested/nested1.h @@ -0,0 +1 @@ +int nested1; diff --git a/test/Modules/Inputs/normal-module-map/nested/nested2.h b/test/Modules/Inputs/normal-module-map/nested/nested2.h new file mode 100644 index 0000000000..d56d601e20 --- /dev/null +++ b/test/Modules/Inputs/normal-module-map/nested/nested2.h @@ -0,0 +1 @@ +int nested2; diff --git a/test/Modules/normal-module-map.cpp b/test/Modules/normal-module-map.cpp new file mode 100644 index 0000000000..2935c8baa9 --- /dev/null +++ b/test/Modules/normal-module-map.cpp @@ -0,0 +1,10 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -x objective-c -fmodule-cache-path %t -fauto-module-import -I %S/Inputs/normal-module-map -verify %s + +#include "a1.h" +#include "b1.h" +#include "nested/nested2.h" + +int test() { + return a1 + b1 + nested2; +}