InGroup<DiagGroup<"analyzer-incompatible-plugin"> >;
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;
}
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">,
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,
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.
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();
/// 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.
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";
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:
#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"
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();
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();
return StringRef();
}
+bool HeaderSearch::loadModuleMapFile(const FileEntry *File) {
+ const DirectoryEntry *Dir = File->getDir();
+
+ llvm::DenseMap<const DirectoryEntry *, bool>::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))
--- /dev/null
+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" }
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
// 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() {
__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"