Skip to content

Commit

Permalink
Module: add -fprebuilt-module-path to support loading prebuilt modules.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
manman-ren committed Aug 18, 2016
1 parent c948d18 commit 11f2a47
Show file tree
Hide file tree
Showing 19 changed files with 155 additions and 35 deletions.
3 changes: 3 additions & 0 deletions clang/docs/Modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
================

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Lex/HeaderSearch.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,12 @@ class HeaderSearch {
/// \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.
///
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Lex/HeaderSearchOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ class HeaderSearchOptions : public RefCountedBase<HeaderSearchOptions> {
/// \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;

Expand Down Expand Up @@ -201,6 +204,10 @@ class HeaderSearchOptions : public RefCountedBase<HeaderSearchOptions> {
void AddVFSOverlayFile(StringRef Name) {
VFSOverlayFiles.push_back(Name);
}

void AddPrebuiltModulePath(StringRef Name) {
PrebuiltModulePaths.push_back(Name);
}
};

} // end namespace clang
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Serialization/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -447,7 +448,8 @@ class ModuleFile {

/// \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.
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/Tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Frontend/ASTUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
69 changes: 60 additions & 9 deletions clang/lib/Frontend/CompilerInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1436,18 +1436,34 @@ 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);
ModuleBuildFailed = true;
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);
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 20 additions & 2 deletions clang/lib/Lex/HeaderSearch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
34 changes: 18 additions & 16 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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);
}
}
Expand Down
Loading

0 comments on commit 11f2a47

Please sign in to comment.