When an import statement fails to find a module in the module cache,

but there is a corresponding umbrella header in a framework, build the
module on-the-fly so it can be immediately loaded at the import
statement. This is very much proof-of-concept code, with details to be
fleshed out over time.

llvm-svn: 139558
This commit is contained in:
Douglas Gregor 2011-09-12 23:31:24 +00:00
parent d4a2b37091
commit faeb1d4658
8 changed files with 146 additions and 6 deletions

View File

@ -60,6 +60,7 @@ def err_friends_define_only_namespace_scope : Error<
def err_deleted_non_function : Error<
"only functions can have deleted definitions">;
def err_module_not_found : Error<"module '%0' not found">, DefaultFatal;
def err_module_not_built : Error<"could not build module '%0'">, DefaultFatal;
// Sema && Lex
def ext_longlong : Extension<

View File

@ -187,6 +187,10 @@ public:
/// \param openFile if true and the file exists, it will be opened.
const FileEntry *getFile(StringRef Filename, bool openFile = false);
/// \brief Forget any information about the given file name, because (for
/// example) something within this process has changed the file in some way.
void forgetFile(StringRef Filename);
/// \brief Returns the current file system options
const FileSystemOptions &getFileSystemOptions() { return FileSystemOpts; }

View File

@ -319,9 +319,15 @@ public:
/// \brief Search in the module cache path for a module with the given
/// name.
///
/// \param UmbrellaHeader If non-NULL, and no module was found in the module
/// cache, this routine will search in the framework paths to determine
/// whether a module can be built from an umbrella header. If so, the pointee
/// will be set to the path of the umbrella header.
///
/// \returns A file describing the named module, if available, or NULL to
/// indicate that the module could not be found.
const FileEntry *lookupModule(StringRef ModuleName);
const FileEntry *lookupModule(StringRef ModuleName,
std::string *UmbrellaHeader = 0);
void IncrementFrameworkLookupCount() { ++NumFrameworkLookups; }

View File

@ -380,6 +380,10 @@ const FileEntry *FileManager::getFile(StringRef Filename, bool openFile) {
return &UFE;
}
void FileManager::forgetFile(StringRef Filename) {
SeenFileEntries.erase(Filename);
}
const FileEntry *
FileManager::getVirtualFile(StringRef Filename, off_t Size,
time_t ModificationTime) {

View File

@ -21,6 +21,7 @@
#include "clang/Lex/PTHManager.h"
#include "clang/Frontend/ChainedDiagnosticClient.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/LogDiagnosticPrinter.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
@ -624,6 +625,64 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
return !getDiagnostics().getClient()->getNumErrors();
}
/// \brief Determine the appropriate source input kind based on language
/// options.
static InputKind getSourceInputKindFromOptions(const LangOptions &LangOpts) {
if (LangOpts.OpenCL)
return IK_OpenCL;
if (LangOpts.CUDA)
return IK_CUDA;
if (LangOpts.ObjC1)
return LangOpts.CPlusPlus? IK_ObjCXX : IK_ObjC;
return LangOpts.CPlusPlus? IK_CXX : IK_C;
}
/// \brief Compile a module file for the given module name with the given
/// umbrella header, using the options provided by the importing compiler
/// instance.
static void compileModule(CompilerInstance &ImportingInstance,
StringRef ModuleName,
StringRef UmbrellaHeader) {
// Determine the file that we'll be writing to.
llvm::SmallString<128> ModuleFile;
ModuleFile +=
ImportingInstance.getInvocation().getHeaderSearchOpts().ModuleCachePath;
llvm::sys::path::append(ModuleFile, ModuleName + ".pcm");
// Construct a compiler invocation for creating this module.
llvm::IntrusiveRefCntPtr<CompilerInvocation> Invocation
(new CompilerInvocation(ImportingInstance.getInvocation()));
FrontendOptions &FrontendOpts = Invocation->getFrontendOpts();
FrontendOpts.OutputFile = ModuleFile.str();
FrontendOpts.DisableFree = false;
FrontendOpts.Inputs.clear();
FrontendOpts.Inputs.push_back(
std::make_pair(getSourceInputKindFromOptions(Invocation->getLangOpts()),
UmbrellaHeader));
// FIXME: Strip away all of the compilation options that won't be transferred
// down to the module. This presumably includes -D flags, optimization
// settings, etc.
// Construct a compiler instance that will be used to actually create the
// module.
CompilerInstance Instance;
Instance.setInvocation(&*Invocation);
// Instance.setDiagnostics(&ImportingInstance.getDiagnostics());
// FIXME: Need to route diagnostics over to the same diagnostic client!
Instance.createDiagnostics(0, 0, 0);
// Construct a module-generating action.
GeneratePCHAction CreateModuleAction(true);
// Execute the action to actually build the module in-place.
// FIXME: Need to synchronize when multiple processes do this.
Instance.ExecuteAction(CreateModuleAction);
// Tell the importing instance's file manager to forget about the module
// file, since we've just created it.
ImportingInstance.getFileManager().forgetFile(ModuleFile);
}
ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc,
IdentifierInfo &ModuleName,
SourceLocation ModuleNameLoc) {
@ -636,10 +695,25 @@ ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc,
CurFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID());
// Search for a module with the given name.
std::string UmbrellaHeader;
const FileEntry *ModuleFile
= PP->getHeaderSearchInfo().lookupModule(ModuleName.getName());
= PP->getHeaderSearchInfo().lookupModule(ModuleName.getName(),
&UmbrellaHeader);
bool BuildingModule = false;
if (!ModuleFile && !UmbrellaHeader.empty()) {
// We didn't find the module, but there is an umbrella header that
// can be used to create the module file. Create a separate compilation
// module to do so.
BuildingModule = true;
compileModule(*this, ModuleName.getName(), UmbrellaHeader);
ModuleFile = PP->getHeaderSearchInfo().lookupModule(ModuleName.getName());
}
if (!ModuleFile) {
getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found)
getDiagnostics().Report(ModuleNameLoc,
BuildingModule? diag::err_module_not_built
: diag::err_module_not_found)
<< ModuleName.getName()
<< SourceRange(ImportLoc, ModuleNameLoc);
return 0;

View File

@ -98,14 +98,48 @@ const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) {
return 0;
}
const FileEntry *HeaderSearch::lookupModule(StringRef ModuleName) {
const FileEntry *HeaderSearch::lookupModule(StringRef ModuleName,
std::string *UmbrellaHeader) {
// If we don't have a module cache path, we can't do anything.
if (ModuleCachePath.empty())
return 0;
// Try to find the module path.
llvm::SmallString<256> FileName(ModuleCachePath);
llvm::sys::path::append(FileName, ModuleName + ".pcm");
return getFileMgr().getFile(FileName);
if (const FileEntry *ModuleFile = getFileMgr().getFile(FileName))
return ModuleFile;
// We didn't find the module. If we're not supposed to look for an
// umbrella header, this is the end of the road.
if (!UmbrellaHeader)
return 0;
// Look in each of the framework directories for an umbrella header with
// the same name as the module.
// FIXME: We need a way for non-frameworks to provide umbrella headers.
llvm::SmallString<128> UmbrellaHeaderName;
UmbrellaHeaderName = ModuleName;
UmbrellaHeaderName += '/';
UmbrellaHeaderName += ModuleName;
UmbrellaHeaderName += ".h";
for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) {
// Skip non-framework include paths
if (!SearchDirs[Idx].isFramework())
continue;
// Look for the umbrella header in this directory.
if (const FileEntry *HeaderFile
= SearchDirs[Idx].LookupFile(UmbrellaHeaderName, *this, 0, 0)) {
*UmbrellaHeader = HeaderFile->getName();
return 0;
}
}
// We did not find an umbrella header. Clear out the UmbrellaHeader pointee
// so our caller knows that we failed.
UmbrellaHeader->clear();
return 0;
}
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,6 @@
const char *getModuleVersion(void);
@interface Module
+(const char *)version;
@end

View File

@ -0,0 +1,11 @@
// RUN: mkdir -p %t
// RUN: rm -f %t/Module.pcm
// RUN: %clang_cc1 -fmodule-cache-path %t -F %S/Inputs -verify %s
__import_module__ Module;
void test_getModuleVersion() {
int version = getModuleVersion(); // expected-warning{{incompatible pointer to integer conversion initializing 'int' with an expression of type 'const char *'}}
int version2 = [Module version]; // expected-warning{{incompatible pointer to integer conversion initializing 'int' with an expression of type 'const char *'}}
}