]> granicus.if.org Git - clang/commitdiff
Add support for building a module from a module map to the -cc1
authorDouglas Gregor <dgregor@apple.com>
Wed, 16 Nov 2011 00:09:06 +0000 (00:09 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 16 Nov 2011 00:09:06 +0000 (00:09 +0000)
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

12 files changed:
include/clang/Basic/DiagnosticFrontendKinds.td
include/clang/Driver/CC1Options.td
include/clang/Frontend/FrontendActions.h
include/clang/Frontend/FrontendOptions.h
include/clang/Lex/HeaderSearch.h
lib/Frontend/CompilerInvocation.cpp
lib/Frontend/FrontendActions.cpp
lib/FrontendTool/ExecuteCompilerInvocation.cpp
lib/Lex/HeaderSearch.cpp
test/Modules/Inputs/module.map [new file with mode: 0644]
test/Modules/diamond.c
test/Modules/normal-module-map.cpp

index 857f77e85506044ec7e9c7968bf19531cf4a28f2..167d070f54f436df83db683113119c5e5d340f17 100644 (file)
@@ -170,4 +170,12 @@ def warn_incompatible_analyzer_plugin_api : Warning<
     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;
 }
index 2177aa5a72e04e5d4d88dd5dd7496c80efc827e4..ae97341872e26dea9c9aeec5d07619b55b0d779c 100644 (file)
@@ -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">,
index 72a3d908476a581874ff1f895bfd3788a42bca6c..19d8c520c36eb93aa3fe727b4a269fcbab421610 100644 (file)
@@ -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,
index fa6d044ce04b65d32f857dc14006ed423d18d977..6e3d7a39c916f3f7fcb96ee8dd57c87ad991c848 100644 (file)
@@ -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.
index 05e66b39c932b8ac9635e31295f99ba04308e18e..07cdc5518081a81dcf0c440a5813b5efcd01d333 100644 (file)
@@ -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.
index 015288c85315bab05f37e71fd24e8e294e4b7494..58ee2735b95b9051b57b2d74f3247ccea88200f4 100644 (file)
@@ -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:
index 6f84da96300c7422ed4ed7d54791fc809d381afb..aea233b313335fbe1cf67d1a419e60a1d221bfda 100644 (file)
@@ -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();
index c9af3cc3ff1e04634d436cda402a771988f06928..db76886095b250be978d7c1b36cf1d84692bb657 100644 (file)
@@ -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();
index 9d36f97335304caa006adfd18072e8d0586d6442..161c4b6d14be90445e5663b362712ae268de9b3b 100644 (file)
@@ -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<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))
diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map
new file mode 100644 (file)
index 0000000..6737ee3
--- /dev/null
@@ -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" }
index 482836c44978a165792e1e901ecd578669fd425f..195e1da4563a6480b27fa0dc26cdde36591e87e5 100644 (file)
@@ -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
index 4dfa9c9a6c46976301284df2f8abac8a330833c3..73cfe8b675b655a48ea1cd762276e4880ee8fd54 100644 (file)
@@ -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"