Module: add -fprebuilt-module-path to support loading prebuilt modules.

In this mode, there is no need to load any module map and the programmer can
simply use "@import" syntax to load the module directly from a prebuilt
module path. When loading from prebuilt module path, we don't support
rebuilding of the module files and we ignore compatible configuration
mismatches.

rdar://27290316
Differential Revision: http://reviews.llvm.org/D23125

llvm-svn: 279096
This commit is contained in:
Manman Ren 2016-08-18 17:42:15 +00:00
parent c948d182e1
commit 11f2a47772
19 changed files with 155 additions and 35 deletions

View File

@ -213,6 +213,9 @@ Command-line parameters
``-fmodule-file=<file>``
Load the given precompiled module file.
``-fprebuilt-module-path=<directory>``
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
================

View File

@ -94,6 +94,8 @@ def err_module_lock_timeout : Error<
"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">;

View File

@ -840,6 +840,9 @@ def fmodules_cache_path : Joined<["-"], "fmodules-cache-path=">, Group<i_Group>,
def fmodules_user_build_path : Separate<["-"], "fmodules-user-build-path">, Group<i_Group>,
Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">,
HelpText<"Specify the module user build path">;
def fprebuilt_module_path : Joined<["-"], "fprebuilt-module-path=">, Group<i_Group>,
Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">,
HelpText<"Specify the prebuilt module path">;
def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group<i_Group>,
Flags<[CC1Option]>, MetaVarName<"<seconds>">,
HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">;

View File

@ -481,9 +481,12 @@ public:
/// \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.
///

View File

@ -93,6 +93,9 @@ public:
/// \brief The directory used for a user build.
std::string ModuleUserBuildPath;
/// \brief The directories used to load prebuilt module files.
std::vector<std::string> PrebuiltModulePaths;
/// The module/pch container format.
std::string ModuleFormat;
@ -201,6 +204,10 @@ public:
void AddVFSOverlayFile(StringRef Name) {
VFSOverlayFiles.push_back(Name);
}
void AddPrebuiltModulePath(StringRef Name) {
PrebuiltModulePaths.push_back(Name);
}
};
} // end namespace clang

View File

@ -48,7 +48,8 @@ enum ModuleKind {
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 @@ public:
/// \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.

View File

@ -5405,6 +5405,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
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);

View File

@ -2805,6 +2805,7 @@ const FileEntry *ASTUnit::getPCHFile() {
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;

View File

@ -1436,7 +1436,25 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
} 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 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
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 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
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 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
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

View File

@ -1368,6 +1368,8 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) {
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);

View File

@ -391,7 +391,8 @@ GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI,
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

View File

@ -121,11 +121,29 @@ const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) {
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())

View File

@ -1312,8 +1312,7 @@ bool ASTReader::ReadSLocEntry(int ID) {
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 @@ std::pair<SourceLocation, StringRef> ASTReader::getModuleImportLoc(int ID) {
// 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 @@ void ASTReader::resolvePendingMacro(IdentifierInfo *II,
// 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 @@ ASTReader::ReadControlBlock(ModuleFile &F,
// 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 @@ ASTReader::ReadControlBlock(ModuleFile &F,
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 @@ ASTReader::ReadControlBlock(ModuleFile &F,
//
// 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 @@ ASTReader::ReadControlBlock(ModuleFile &F,
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 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
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 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F,
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 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName,
}
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 @@ static unsigned moduleKindForDiagnostic(ModuleKind Kind) {
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 @@ ASTReader::ReadASTCore(StringRef FileName,
//
// 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 @@ void ASTReader::finishPendingActions() {
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);
}
}

View File

@ -1398,7 +1398,7 @@ void ASTDeclReader::VisitNamespaceDecl(NamespaceDecl *D) {
// any other module's anonymous namespaces, so don't attach the anonymous
// namespace at all.
NamespaceDecl *Anon = cast<NamespaceDecl>(Reader.GetDecl(AnonNamespace));
if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule)
if (!F.isModule())
D->setAnonymousNamespace(Anon);
}
}
@ -3767,8 +3767,7 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
// 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<TranslationUnitDecl>(D))
TU->setAnonymousNamespace(Anon);
else

View File

@ -66,7 +66,7 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type,
// 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

View File

@ -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"

View File

@ -0,0 +1 @@
const int a = 1;

View File

@ -0,0 +1 @@
module prebuilt { header "a.h" }

View File

@ -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;
}