Index: cfe/trunk/docs/Modules.rst =================================================================== --- cfe/trunk/docs/Modules.rst +++ cfe/trunk/docs/Modules.rst @@ -213,6 +213,9 @@ ``-fmodule-file=`` Load the given precompiled module file. +``-fprebuilt-module-path=`` + Specify the path to the prebuilt modules. If specified, we will look for modules in this directory for a given top-level module name. We don't need a module map for loading prebuilt modules in this directory and the compiler will not try to rebuild these modules. This can be specified multiple times. + Module Semantics ================ Index: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td @@ -94,6 +94,8 @@ "timed out waiting to acquire lock file for module '%0'">, DefaultFatal; def err_module_cycle : Error<"cyclic dependency in module '%0': %1">, DefaultFatal; +def err_module_prebuilt : Error< + "error in loading module '%0' from prebuilt module path">, DefaultFatal; def note_pragma_entered_here : Note<"#pragma entered here">; def note_decl_hiding_tag_type : Note< "%1 %0 is hidden by a non-type declaration of %0 here">; Index: cfe/trunk/include/clang/Driver/Options.td =================================================================== --- cfe/trunk/include/clang/Driver/Options.td +++ cfe/trunk/include/clang/Driver/Options.td @@ -840,6 +840,9 @@ def fmodules_user_build_path : Separate<["-"], "fmodules-user-build-path">, Group, Flags<[DriverOption, CC1Option]>, MetaVarName<"">, HelpText<"Specify the module user build path">; +def fprebuilt_module_path : Joined<["-"], "fprebuilt-module-path=">, Group, + Flags<[DriverOption, CC1Option]>, MetaVarName<"">, + HelpText<"Specify the prebuilt module path">; def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group, Flags<[CC1Option]>, MetaVarName<"">, HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">; Index: cfe/trunk/include/clang/Lex/HeaderSearch.h =================================================================== --- cfe/trunk/include/clang/Lex/HeaderSearch.h +++ cfe/trunk/include/clang/Lex/HeaderSearch.h @@ -481,9 +481,12 @@ /// \param ModuleMapPath A path that when combined with \c ModuleName /// uniquely identifies this module. See Module::ModuleMap. /// + /// \param UsePrebuiltPath Whether we should use the prebuilt module path. + /// /// \returns The name of the module file that corresponds to this module, /// or an empty string if this module does not correspond to any module file. - std::string getModuleFileName(StringRef ModuleName, StringRef ModuleMapPath); + std::string getModuleFileName(StringRef ModuleName, StringRef ModuleMapPath, + bool UsePrebuiltPath); /// \brief Lookup a module Search for a module with the given name. /// Index: cfe/trunk/include/clang/Lex/HeaderSearchOptions.h =================================================================== --- cfe/trunk/include/clang/Lex/HeaderSearchOptions.h +++ cfe/trunk/include/clang/Lex/HeaderSearchOptions.h @@ -93,6 +93,9 @@ /// \brief The directory used for a user build. std::string ModuleUserBuildPath; + /// \brief The directories used to load prebuilt module files. + std::vector PrebuiltModulePaths; + /// The module/pch container format. std::string ModuleFormat; @@ -201,6 +204,10 @@ void AddVFSOverlayFile(StringRef Name) { VFSOverlayFiles.push_back(Name); } + + void AddPrebuiltModulePath(StringRef Name) { + PrebuiltModulePaths.push_back(Name); + } }; } // end namespace clang Index: cfe/trunk/include/clang/Serialization/Module.h =================================================================== --- cfe/trunk/include/clang/Serialization/Module.h +++ cfe/trunk/include/clang/Serialization/Module.h @@ -48,7 +48,8 @@ MK_ExplicitModule, ///< File is an explicitly-loaded module. MK_PCH, ///< File is a PCH file treated as such. MK_Preamble, ///< File is a PCH file treated as the preamble. - MK_MainFile ///< File is a PCH file treated as the actual main file. + MK_MainFile, ///< File is a PCH file treated as the actual main file. + MK_PrebuiltModule ///< File is from a prebuilt module path. }; /// \brief The input file that has been loaded from this AST file, along with @@ -447,7 +448,8 @@ /// \brief Is this a module file for a module (rather than a PCH or similar). bool isModule() const { - return Kind == MK_ImplicitModule || Kind == MK_ExplicitModule; + return Kind == MK_ImplicitModule || Kind == MK_ExplicitModule || + Kind == MK_PrebuiltModule; } /// \brief Dump debugging output for this module. Index: cfe/trunk/lib/Driver/Tools.cpp =================================================================== --- cfe/trunk/lib/Driver/Tools.cpp +++ cfe/trunk/lib/Driver/Tools.cpp @@ -5405,6 +5405,13 @@ CmdArgs.push_back(Args.MakeArgString(Path)); } + if (HaveModules) { + // -fprebuilt-module-path specifies where to load the prebuilt module files. + for (const Arg *A : Args.filtered(options::OPT_fprebuilt_module_path)) + CmdArgs.push_back(Args.MakeArgString( + std::string("-fprebuilt-module-path=") + A->getValue())); + } + // -fmodule-name specifies the module that is currently being built (or // used for header checking by -fmodule-maps). Args.AddLastArg(CmdArgs, options::OPT_fmodule_name_EQ); Index: cfe/trunk/lib/Frontend/ASTUnit.cpp =================================================================== --- cfe/trunk/lib/Frontend/ASTUnit.cpp +++ cfe/trunk/lib/Frontend/ASTUnit.cpp @@ -2805,6 +2805,7 @@ switch (M.Kind) { case serialization::MK_ImplicitModule: case serialization::MK_ExplicitModule: + case serialization::MK_PrebuiltModule: return true; // skip dependencies. case serialization::MK_PCH: Mod = &M; Index: cfe/trunk/lib/Frontend/CompilerInstance.cpp =================================================================== --- cfe/trunk/lib/Frontend/CompilerInstance.cpp +++ cfe/trunk/lib/Frontend/CompilerInstance.cpp @@ -1436,7 +1436,25 @@ } else { // Search for a module with the given name. Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); - if (!Module) { + HeaderSearchOptions &HSOpts = + PP->getHeaderSearchInfo().getHeaderSearchOpts(); + + std::string ModuleFileName; + bool LoadFromPrebuiltModulePath = false; + // We try to load the module from the prebuilt module paths. If not + // successful, we then try to find it in the module cache. + if (!HSOpts.PrebuiltModulePaths.empty()) { + // Load the module from the prebuilt module path. + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName( + ModuleName, "", /*UsePrebuiltPath*/ true); + if (!ModuleFileName.empty()) + LoadFromPrebuiltModulePath = true; + } + if (!LoadFromPrebuiltModulePath && Module) { + // Load the module from the module cache. + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(Module); + } else if (!LoadFromPrebuiltModulePath) { + // We can't find a module, error out here. getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) << ModuleName << SourceRange(ImportLoc, ModuleNameLoc); @@ -1444,10 +1462,8 @@ return ModuleLoadResult(); } - std::string ModuleFileName = - PP->getHeaderSearchInfo().getModuleFileName(Module); if (ModuleFileName.empty()) { - if (Module->HasIncompatibleModuleFile) { + if (Module && Module->HasIncompatibleModuleFile) { // We tried and failed to load a module file for this module. Fall // back to textual inclusion for its headers. return ModuleLoadResult(nullptr, /*missingExpected*/true); @@ -1468,16 +1484,46 @@ Timer.init("Loading " + ModuleFileName, *FrontendTimerGroup); llvm::TimeRegion TimeLoading(FrontendTimerGroup ? &Timer : nullptr); - // Try to load the module file. - unsigned ARRFlags = ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing; + // Try to load the module file. If we are trying to load from the prebuilt + // module path, we don't have the module map files and don't know how to + // rebuild modules. + unsigned ARRFlags = LoadFromPrebuiltModulePath ? + ASTReader::ARR_ConfigurationMismatch : + ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing; switch (ModuleManager->ReadAST(ModuleFileName, + LoadFromPrebuiltModulePath ? + serialization::MK_PrebuiltModule : serialization::MK_ImplicitModule, - ImportLoc, ARRFlags)) { - case ASTReader::Success: + ImportLoc, + ARRFlags)) { + case ASTReader::Success: { + if (LoadFromPrebuiltModulePath && !Module) { + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + if (!Module || !Module->getASTFile() || + FileMgr->getFile(ModuleFileName) != Module->getASTFile()) { + // Error out if Module does not refer to the file in the prebuilt + // module path. + getDiagnostics().Report(ModuleNameLoc, diag::err_module_prebuilt) + << ModuleName; + ModuleBuildFailed = true; + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + } + } break; + } case ASTReader::OutOfDate: case ASTReader::Missing: { + if (LoadFromPrebuiltModulePath) { + // We can't rebuild the module without a module map. Since ReadAST + // already produces diagnostics for these two cases, we simply + // error out here. + ModuleBuildFailed = true; + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + } + // The module file is missing or out-of-date. Build it. assert(Module && "missing module file"); // Check whether there is a cycle in the module graph. @@ -1528,8 +1574,13 @@ break; } - case ASTReader::VersionMismatch: case ASTReader::ConfigurationMismatch: + if (LoadFromPrebuiltModulePath) + getDiagnostics().Report(SourceLocation(), + diag::warn_module_config_mismatch) + << ModuleFileName; + // Fall through to error out. + case ASTReader::VersionMismatch: case ASTReader::HadErrors: ModuleLoader::HadFatalFailure = true; // FIXME: The ASTReader will already have complained, but can we shoehorn Index: cfe/trunk/lib/Frontend/CompilerInvocation.cpp =================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp @@ -1368,6 +1368,8 @@ Opts.ResourceDir = Args.getLastArgValue(OPT_resource_dir); Opts.ModuleCachePath = Args.getLastArgValue(OPT_fmodules_cache_path); Opts.ModuleUserBuildPath = Args.getLastArgValue(OPT_fmodules_user_build_path); + for (const Arg *A : Args.filtered(OPT_fprebuilt_module_path)) + Opts.AddPrebuiltModulePath(A->getValue()); Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); Opts.ModulesValidateDiagnosticOptions = !Args.hasArg(OPT_fmodules_disable_diagnostic_validation); Index: cfe/trunk/lib/Frontend/FrontendActions.cpp =================================================================== --- cfe/trunk/lib/Frontend/FrontendActions.cpp +++ cfe/trunk/lib/Frontend/FrontendActions.cpp @@ -391,7 +391,8 @@ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); CI.getFrontendOpts().OutputFile = HS.getModuleFileName(CI.getLangOpts().CurrentModule, - ModuleMapForUniquing->getName()); + ModuleMapForUniquing->getName(), + /*UsePrebuiltPath=*/false); } // We use createOutputFile here because this is exposed via libclang, and we Index: cfe/trunk/lib/Lex/HeaderSearch.cpp =================================================================== --- cfe/trunk/lib/Lex/HeaderSearch.cpp +++ cfe/trunk/lib/Lex/HeaderSearch.cpp @@ -121,11 +121,29 @@ std::string HeaderSearch::getModuleFileName(Module *Module) { const FileEntry *ModuleMap = getModuleMap().getModuleMapFileForUniquing(Module); - return getModuleFileName(Module->Name, ModuleMap->getName()); + return getModuleFileName(Module->Name, ModuleMap->getName(), + /*UsePrebuiltPath*/false); } std::string HeaderSearch::getModuleFileName(StringRef ModuleName, - StringRef ModuleMapPath) { + StringRef ModuleMapPath, + bool UsePrebuiltPath) { + if (UsePrebuiltPath) { + if (HSOpts->PrebuiltModulePaths.empty()) + return std::string(); + + // Go though each prebuilt module path and try to find the pcm file. + for (const std::string &Dir : HSOpts->PrebuiltModulePaths) { + SmallString<256> Result(Dir); + llvm::sys::fs::make_absolute(Result); + + llvm::sys::path::append(Result, ModuleName + ".pcm"); + if (getFileMgr().getFile(Result.str())) + return Result.str().str(); + } + return std::string(); + } + // If we don't have a module cache path or aren't supposed to use one, we // can't do anything. if (getModuleCachePath().empty()) Index: cfe/trunk/lib/Serialization/ASTReader.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTReader.cpp +++ cfe/trunk/lib/Serialization/ASTReader.cpp @@ -1312,8 +1312,7 @@ SrcMgr::CharacteristicKind FileCharacter = (SrcMgr::CharacteristicKind)Record[2]; SourceLocation IncludeLoc = ReadSourceLocation(*F, Record[1]); - if (IncludeLoc.isInvalid() && - (F->Kind == MK_ImplicitModule || F->Kind == MK_ExplicitModule)) { + if (IncludeLoc.isInvalid() && F->isModule()) { IncludeLoc = getImportLocation(F); } @@ -1351,7 +1350,7 @@ // Find which module file this entry lands in. ModuleFile *M = GlobalSLocEntryMap.find(-ID)->second; - if (M->Kind != MK_ImplicitModule && M->Kind != MK_ExplicitModule) + if (!M->isModule()) return std::make_pair(SourceLocation(), ""); // FIXME: Can we map this down to a particular submodule? That would be @@ -1861,7 +1860,7 @@ // Don't read the directive history for a module; we don't have anywhere // to put it. - if (M.Kind == MK_ImplicitModule || M.Kind == MK_ExplicitModule) + if (M.isModule()) return; // Deserialize the macro directives history in reverse source-order. @@ -2194,7 +2193,8 @@ // All user input files reside at the index range [0, NumUserInputs), and // system input files reside at [NumUserInputs, NumInputs). For explicitly // loaded module files, ignore missing inputs. - if (!DisableValidation && F.Kind != MK_ExplicitModule) { + if (!DisableValidation && F.Kind != MK_ExplicitModule && + F.Kind != MK_PrebuiltModule) { bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0; // If we are reading a module, we will create a verification timestamp, @@ -2225,7 +2225,8 @@ bool IsSystem = I >= NumUserInputs; InputFileInfo FI = readInputFileInfo(F, I+1); Listener->visitInputFile(FI.Filename, IsSystem, FI.Overridden, - F.Kind == MK_ExplicitModule); + F.Kind == MK_ExplicitModule || + F.Kind == MK_PrebuiltModule); } } @@ -2255,7 +2256,7 @@ // // FIXME: Allow this for files explicitly specified with -include-pch. bool AllowCompatibleConfigurationMismatch = - F.Kind == MK_ExplicitModule; + F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule; const HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts(); @@ -2417,7 +2418,7 @@ if (M && M->Directory) { // If we're implicitly loading a module, the base directory can't // change between the build and use. - if (F.Kind != MK_ExplicitModule) { + if (F.Kind != MK_ExplicitModule && F.Kind != MK_PrebuiltModule) { const DirectoryEntry *BuildDir = PP.getFileManager().getDirectory(Blob); if (!BuildDir || BuildDir != M->Directory) { @@ -3141,7 +3142,7 @@ break; case IMPORTED_MODULES: { - if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) { + if (!F.isModule()) { // If we aren't loading a module (which has its own exports), make // all of the imported modules visible. // FIXME: Deal with macros-only imports. @@ -3225,7 +3226,7 @@ unsigned Idx = 0; F.ModuleMapPath = ReadPath(F, Record, Idx); - if (F.Kind == MK_ExplicitModule) { + if (F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule) { // For an explicitly-loaded module, we don't care whether the original // module map file exists or matches. return Success; @@ -3596,7 +3597,8 @@ } if (!Context.getLangOpts().CPlusPlus || - (Type != MK_ImplicitModule && Type != MK_ExplicitModule)) { + (Type != MK_ImplicitModule && Type != MK_ExplicitModule && + Type != MK_PrebuiltModule)) { // Mark all of the identifiers in the identifier table as being out of date, // so that various accessors know to check the loaded modules when the // identifier is used. @@ -3713,6 +3715,7 @@ return 0; // PCH case MK_ImplicitModule: case MK_ExplicitModule: + case MK_PrebuiltModule: return 1; // module case MK_MainFile: case MK_Preamble: @@ -3818,7 +3821,8 @@ // // FIXME: Should we also perform the converse check? Loading a module as // a PCH file sort of works, but it's a bit wonky. - if ((Type == MK_ImplicitModule || Type == MK_ExplicitModule) && + if ((Type == MK_ImplicitModule || Type == MK_ExplicitModule || + Type == MK_PrebuiltModule) && F.ModuleName.empty()) { auto Result = (Type == MK_ImplicitModule) ? OutOfDate : Failure; if (Result != OutOfDate || @@ -8364,16 +8368,14 @@ for (unsigned IDIdx = 0, NumIDs = GlobalIDs.size(); IDIdx != NumIDs; ++IDIdx) { const PendingMacroInfo &Info = GlobalIDs[IDIdx]; - if (Info.M->Kind != MK_ImplicitModule && - Info.M->Kind != MK_ExplicitModule) + if (!Info.M->isModule()) resolvePendingMacro(II, Info); } // Handle module imports. for (unsigned IDIdx = 0, NumIDs = GlobalIDs.size(); IDIdx != NumIDs; ++IDIdx) { const PendingMacroInfo &Info = GlobalIDs[IDIdx]; - if (Info.M->Kind == MK_ImplicitModule || - Info.M->Kind == MK_ExplicitModule) + if (Info.M->isModule()) resolvePendingMacro(II, Info); } } Index: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp @@ -1398,7 +1398,7 @@ // any other module's anonymous namespaces, so don't attach the anonymous // namespace at all. NamespaceDecl *Anon = cast(Reader.GetDecl(AnonNamespace)); - if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) + if (!F.isModule()) D->setAnonymousNamespace(Anon); } } @@ -3767,8 +3767,7 @@ // Each module has its own anonymous namespace, which is disjoint from // any other module's anonymous namespaces, so don't attach the anonymous // namespace at all. - if (ModuleFile.Kind != MK_ImplicitModule && - ModuleFile.Kind != MK_ExplicitModule) { + if (!ModuleFile.isModule()) { if (TranslationUnitDecl *TU = dyn_cast(D)) TU->setAnonymousNamespace(Anon); else Index: cfe/trunk/lib/Serialization/ModuleManager.cpp =================================================================== --- cfe/trunk/lib/Serialization/ModuleManager.cpp +++ cfe/trunk/lib/Serialization/ModuleManager.cpp @@ -66,7 +66,7 @@ // Look for the file entry. This only fails if the expected size or // modification time differ. const FileEntry *Entry; - if (Type == MK_ExplicitModule) { + if (Type == MK_ExplicitModule || Type == MK_PrebuiltModule) { // If we're not expecting to pull this file out of the module cache, it // might have a different mtime due to being moved across filesystems in // a distributed build. The size must still match, though. (As must the Index: cfe/trunk/test/Driver/modules.m =================================================================== --- cfe/trunk/test/Driver/modules.m +++ cfe/trunk/test/Driver/modules.m @@ -39,6 +39,13 @@ // RUN: %clang -fmodules-disable-diagnostic-validation -### %s 2>&1 | FileCheck -check-prefix=MODULES_DISABLE_DIAGNOSTIC_VALIDATION %s // MODULES_DISABLE_DIAGNOSTIC_VALIDATION: -fmodules-disable-diagnostic-validation +// RUN: %clang -fmodules -### %s 2>&1 | FileCheck -check-prefix=MODULES_PREBUILT_PATH_DEFAULT %s +// MODULES_PREBUILT_PATH_DEFAULT-NOT: -fprebuilt-module-path + +// RUN: %clang -fmodules -fprebuilt-module-path=foo -fprebuilt-module-path=bar -### %s 2>&1 | FileCheck -check-prefix=MODULES_PREBUILT_PATH %s +// MODULES_PREBUILT_PATH: "-fprebuilt-module-path=foo" +// MODULES_PREBUILT_PATH: "-fprebuilt-module-path=bar" + // RUN: %clang -fmodules -fmodule-map-file=foo.map -fmodule-map-file=bar.map -### %s 2>&1 | FileCheck -check-prefix=CHECK-MODULE-MAP-FILES %s // CHECK-MODULE-MAP-FILES: "-fmodules" // CHECK-MODULE-MAP-FILES: "-fmodule-map-file=foo.map" Index: cfe/trunk/test/Modules/Inputs/prebuilt-module/a.h =================================================================== --- cfe/trunk/test/Modules/Inputs/prebuilt-module/a.h +++ cfe/trunk/test/Modules/Inputs/prebuilt-module/a.h @@ -0,0 +1 @@ +const int a = 1; Index: cfe/trunk/test/Modules/Inputs/prebuilt-module/module.modulemap =================================================================== --- cfe/trunk/test/Modules/Inputs/prebuilt-module/module.modulemap +++ cfe/trunk/test/Modules/Inputs/prebuilt-module/module.modulemap @@ -0,0 +1 @@ +module prebuilt { header "a.h" } Index: cfe/trunk/test/Modules/prebuilt-module.m =================================================================== --- cfe/trunk/test/Modules/prebuilt-module.m +++ cfe/trunk/test/Modules/prebuilt-module.m @@ -0,0 +1,10 @@ +// RUN: rm -rf %t +// +// RUN: %clang_cc1 -fmodules -x objective-c -I %S/Inputs/prebuilt-module -triple %itanium_abi_triple -emit-module %S/Inputs/prebuilt-module/module.modulemap -fmodule-name=prebuilt -o %t/prebuilt.pcm +// RUN: %clang_cc1 -fmodules -fprebuilt-module-path=%t/ -fdisable-module-hash %s -verify + +// expected-no-diagnostics +@import prebuilt; +int test() { + return a; +}