forked from OSchip/llvm-project
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:
parent
d4a2b37091
commit
faeb1d4658
|
@ -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<
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
const char *getModuleVersion(void);
|
||||
|
||||
@interface Module
|
||||
+(const char *)version;
|
||||
@end
|
||||
|
|
@ -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 *'}}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue