From: Douglas Gregor Date: Wed, 16 Nov 2011 00:09:06 +0000 (+0000) Subject: Add support for building a module from a module map to the -cc1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=db1cde7dc7bb3aaf48118bd9605192ab94a93635;p=clang Add support for building a module from a module map to the -cc1 interface. This is currently limited to modules with umbrella headers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144736 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 857f77e855..167d070f54 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -170,4 +170,12 @@ def warn_incompatible_analyzer_plugin_api : Warning< InGroup >; def note_incompatible_analyzer_plugin_api : Note< "current API version is '%0', but plugin was compiled with version '%1'">; + +def err_module_map_not_found : Error<"module map file '%0' not found">, + DefaultFatal; +def err_missing_module_name : Error< + "no module name provided; specify one with -fmodule-name=">, + DefaultFatal; +def err_missing_module : Error< + "no module named '%0' declared in module map file '%1'">, DefaultFatal; } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 2177aa5a72..ae97341872 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -377,6 +377,8 @@ def print_decl_contexts : Flag<"-print-decl-contexts">, HelpText<"Print DeclContexts and their Decls">; def emit_module : Flag<"-emit-module">, HelpText<"Generate pre-compiled module file">; +def emit_module_from_map : Flag<"-emit-module-from-map">, + HelpText<"Generate pre-compiled module file from module map">; def emit_pth : Flag<"-emit-pth">, HelpText<"Generate pre-tokenized header file">; def emit_pch : Flag<"-emit-pch">, diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index 72a3d90847..19d8c520c3 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -94,6 +94,31 @@ public: raw_ostream *&OS); }; +class GenerateModuleAction : public ASTFrontendAction { +protected: + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile); + + virtual TranslationUnitKind getTranslationUnitKind() { + return TU_Module; + } + + virtual bool hasASTFileSupport() const { return false; } + +public: + virtual bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename); + + /// \brief Compute the AST consumer arguments that will be used to + /// create the PCHGenerator instance returned by CreateASTConsumer. + /// + /// \returns true if an error occurred, false otherwise. + static bool ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS); +}; + class SyntaxOnlyAction : public ASTFrontendAction { protected: virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index fa6d044ce0..6e3d7a39c9 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -35,6 +35,7 @@ namespace frontend { EmitObj, ///< Emit a .o file. FixIt, ///< Parse and apply any fixits to the source. GenerateModule, ///< Generate pre-compiled module. + GenerateModuleFromMap, ///< Generate pre-compiled module from module map. GeneratePCH, ///< Generate pre-compiled header. GeneratePTH, ///< Generate pre-tokenized header. InitOnly, ///< Only execute frontend initialization. diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index 05e66b39c9..07cdc55180 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -214,6 +214,9 @@ public: this->BuildingModule = BuildingModule; } + /// \brief Retrieve the path to the module cache. + StringRef getModuleCachePath() const { return ModuleCachePath; } + /// ClearFileInfo - Forget everything we know about headers so far. void ClearFileInfo() { FileInfo.clear(); @@ -367,6 +370,26 @@ public: /// FIXME: This will need to be generalized for submodules. StringRef findModuleForHeader(const FileEntry *File); + + /// \brief Read the contents of the given module map file. + /// + /// \param File The module map file. + /// + /// \param OnlyModule If non-NULL, this will receive the + /// + /// \returns true if an error occurred, false otherwise. + bool loadModuleMapFile(const FileEntry *File); + + /// \brief Retrieve a module with the given name. + /// + /// \param Name The name of the module to retrieve. + /// + /// \param AllowSearch If true, we're allowed to look for module maps within + /// the header search path. Otherwise, the module must already be known. + /// + /// \returns The module, if found; otherwise, null. + ModuleMap::Module *getModule(StringRef Name, bool AllowSearch = true); + unsigned header_file_size() const { return FileInfo.size(); } // Used by ASTReader. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 015288c853..58ee2735b9 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -394,6 +394,7 @@ static const char *getActionName(frontend::ActionKind Kind) { case frontend::EmitObj: return "-emit-obj"; case frontend::FixIt: return "-fixit"; case frontend::GenerateModule: return "-emit-module"; + case frontend::GenerateModuleFromMap: return "-emit-module-from-map"; case frontend::GeneratePCH: return "-emit-pch"; case frontend::GeneratePTH: return "-emit-pth"; case frontend::InitOnly: return "-init-only"; @@ -1252,6 +1253,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::FixIt; break; case OPT_emit_module: Opts.ProgramAction = frontend::GenerateModule; break; + case OPT_emit_module_from_map: + Opts.ProgramAction = frontend::GenerateModuleFromMap; break; case OPT_emit_pch: Opts.ProgramAction = frontend::GeneratePCH; break; case OPT_emit_pth: diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 6f84da9630..aea233b313 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -9,6 +9,7 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/AST/ASTConsumer.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Pragma.h" #include "clang/Lex/Preprocessor.h" #include "clang/Parse/Parser.h" @@ -113,6 +114,98 @@ bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, return false; } +ASTConsumer *GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = 0; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS)) + return 0; + + return new PCHGenerator(CI.getPreprocessor(), OutputFile, /*MakeModule=*/true, + Sysroot, OS); +} + +bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + // Find the module map file. + const FileEntry *ModuleMap = CI.getFileManager().getFile(Filename); + if (!ModuleMap) { + CI.getDiagnostics().Report(diag::err_module_map_not_found) + << Filename; + return false; + } + + // Parse the module map file. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + if (HS.loadModuleMapFile(ModuleMap)) + return false; + + if (CI.getLangOpts().CurrentModule.empty()) { + CI.getDiagnostics().Report(diag::err_missing_module_name); + + // FIXME: Eventually, we could consider asking whether there was just + // a single module described in the module map, and use that as a + // default. Then it would be fairly trivial to just "compile" a module + // map with a single module (the common case). + return false; + } + + // Dig out the module definition. + ModuleMap::Module *Module = HS.getModule(CI.getLangOpts().CurrentModule, + /*AllowSearch=*/false); + if (!Module) { + CI.getDiagnostics().Report(diag::err_missing_module) + << CI.getLangOpts().CurrentModule << Filename; + + return false; + } + + // If there is an umbrella header, use it as our actual input file. + if (Module->UmbrellaHeader) { + // FIXME: Deal with explicit submodule headers, which won't be contained + // within the umbrella header. + fprintf(stderr, "note: using umbrella header \"%s\"\n", + Module->UmbrellaHeader->getName()); + setCurrentFile(Module->UmbrellaHeader->getName(), getCurrentFileKind()); + } else { + // FIXME: Deal with the non-umbrella case, where we have to synthesize + // a header to parse. + // FIXME: Diagnose, at least for now. + return false; + } + + return true; +} + +bool GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS) { + // If no output file was provided, figure out where this module would go + // in the module cache. + if (CI.getFrontendOpts().OutputFile.empty()) { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + llvm::SmallString<256> ModuleFileName(HS.getModuleCachePath()); + llvm::sys::path::append(ModuleFileName, + CI.getLangOpts().CurrentModule + ".pcm"); + CI.getFrontendOpts().OutputFile = ModuleFileName.str(); + } + + // We use createOutputFile here because this is exposed via libclang, and we + // must disable the RemoveFileOnSignal behavior. + // We use a temporary to avoid race conditions. + OS = CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true); + if (!OS) + return true; + + OutputFile = CI.getFrontendOpts().OutputFile; + return false; +} + ASTConsumer *SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return new ASTConsumer(); diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index c9af3cc3ff..db76886095 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -50,6 +50,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case EmitObj: return new EmitObjAction(); case FixIt: return new FixItAction(); case GenerateModule: return new GeneratePCHAction(true); + case GenerateModuleFromMap: return new GenerateModuleAction; case GeneratePCH: return new GeneratePCHAction(false); case GeneratePTH: return new GeneratePTHAction(); case InitOnly: return new InitOnlyAction(); diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 9d36f97335..161c4b6d14 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -800,6 +800,46 @@ StringRef HeaderSearch::findModuleForHeader(const FileEntry *File) { return StringRef(); } +bool HeaderSearch::loadModuleMapFile(const FileEntry *File) { + const DirectoryEntry *Dir = File->getDir(); + + llvm::DenseMap::iterator KnownDir + = DirectoryHasModuleMap.find(Dir); + if (KnownDir != DirectoryHasModuleMap.end()) + return !KnownDir->second; + + bool Result = ModMap.parseModuleMapFile(File); + DirectoryHasModuleMap[Dir] = !Result; + return Result; +} + +ModuleMap::Module *HeaderSearch::getModule(StringRef Name, bool AllowSearch) { + if (ModuleMap::Module *Module = ModMap.findModule(Name)) + return Module; + + if (!AllowSearch) + return 0; + + for (unsigned I = 0, N = SearchDirs.size(); I != N; ++I) { + if (!SearchDirs[I].isNormalDir()) + continue; + + switch (loadModuleMapFile(SearchDirs[I].getDir())) { + case LMM_AlreadyLoaded: + case LMM_InvalidModuleMap: + case LMM_NoDirectory: + break; + + case LMM_NewlyLoaded: + if (ModuleMap::Module *Module = ModMap.findModule(Name)) + return Module; + break; + } + } + + return 0; +} + HeaderSearch::LoadModuleMapResult HeaderSearch::loadModuleMapFile(StringRef DirName) { if (const DirectoryEntry *Dir = FileMgr.getDirectory(DirName)) diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map new file mode 100644 index 0000000000..6737ee31a7 --- /dev/null +++ b/test/Modules/Inputs/module.map @@ -0,0 +1,4 @@ +module diamond_top { umbrella "diamond_top.h" } +module diamond_left { umbrella "diamond_left.h" } +module diamond_right { umbrella "diamond_right.h" } +module diamond_bottom { umbrella "diamond_bottom.h" } diff --git a/test/Modules/diamond.c b/test/Modules/diamond.c index 482836c449..195e1da456 100644 --- a/test/Modules/diamond.c +++ b/test/Modules/diamond.c @@ -20,8 +20,9 @@ void test_diamond(int i, float f, double d, char c) { lr.left = 17; } -// RUN: %clang_cc1 -emit-module -o %T/diamond_top.pcm %S/Inputs/diamond_top.h -// RUN: %clang_cc1 -fmodule-cache-path %T -fdisable-module-hash -emit-module -o %T/diamond_left.pcm %S/Inputs/diamond_left.h -// RUN: %clang_cc1 -fmodule-cache-path %T -fdisable-module-hash -emit-module -o %T/diamond_right.pcm %S/Inputs/diamond_right.h -// RUN: %clang_cc1 -fmodule-cache-path %T -fdisable-module-hash -emit-module -o %T/diamond_bottom.pcm %S/Inputs/diamond_bottom.h -// RUN: %clang_cc1 -fmodule-cache-path %T -fdisable-module-hash %s -verify +// RUN: rm -rf %t +// RUN: %clang_cc1 -emit-module-from-map -fmodule-cache-path %t -fmodule-name=diamond_top %S/Inputs/module.map +// RUN: %clang_cc1 -emit-module-from-map -fmodule-cache-path %t -fmodule-name=diamond_left %S/Inputs/module.map +// RUN: %clang_cc1 -emit-module-from-map -fmodule-cache-path %t -fmodule-name=diamond_right %S/Inputs/module.map +// RUN: %clang_cc1 -emit-module-from-map -fmodule-cache-path %t -fmodule-name=diamond_bottom %S/Inputs/module.map +// RUN: %clang_cc1 -fmodule-cache-path %t %s -verify diff --git a/test/Modules/normal-module-map.cpp b/test/Modules/normal-module-map.cpp index 4dfa9c9a6c..73cfe8b675 100644 --- a/test/Modules/normal-module-map.cpp +++ b/test/Modules/normal-module-map.cpp @@ -1,8 +1,6 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -x objective-c -fmodule-cache-path %t -fauto-module-import -I %S/Inputs/normal-module-map %s -verify -// FIXME: The expected error here is temporary, since we don't yet have the -// logic to build a module from a module map. #include "Umbrella/Umbrella.h" int getUmbrella() { @@ -11,6 +9,8 @@ int getUmbrella() { __import_module__ Umbrella2; +// FIXME: The expected error here is temporary, since we don't yet have the +// logic to build a module from a module map. #include "a1.h" // expected-error{{module 'libA' not found}} #include "b1.h" #include "nested/nested2.h"