Don't call clang::ApplyHeaderSearchOptions from Interpreter::AddIncludePaths.
Instead call it at the end of cling::createCI (that should probably change too). clang::ApplyHeaderSearchOptions is a very heavy-weight function that does a lot of work to determine and add system headers. More importantly it can also wind up invalidating clangs internal cache making inclusion of files later impossible. By using the lighter HeaderSearchOptions::AddSearchPath we not only avoid redoing a lot of work that has been done, but can adjust the method in clang to avoid cache invalidation so that calling Interpreter::AddIncludePath will actually make the files in that path accessible to clang. Signed-off-by: Vassil Vassilev <vvasilev@cern.ch>
This commit is contained in:
parent
998ca7af13
commit
2142853de6
@ -268,10 +268,6 @@ namespace cling {
|
||||
llvm::StringRef code,
|
||||
bool withAccessControl);
|
||||
|
||||
///\brief Set up include paths for runtime headers.
|
||||
///
|
||||
void AddRuntimeIncludePaths(const char* argv0);
|
||||
|
||||
///\brief Include C++ runtime headers and definitions.
|
||||
///
|
||||
void IncludeCXXRuntime();
|
||||
@ -370,9 +366,18 @@ namespace cling {
|
||||
///
|
||||
bool isUniqueWrapper(llvm::StringRef name);
|
||||
|
||||
///\brief Adds an include path (-I).
|
||||
///\brief Adds multiple include paths separated by a delimter.
|
||||
///
|
||||
void AddIncludePath(llvm::StringRef incpath);
|
||||
///\param[in] PathsStr - Path(s)
|
||||
///\param[in] Delim - Delimiter to separate paths or NULL if a single path
|
||||
///
|
||||
void AddIncludePaths(llvm::StringRef PathsStr, const char* Delim = ":");
|
||||
|
||||
///\brief Adds a single include path (-I).
|
||||
///
|
||||
void AddIncludePath(llvm::StringRef PathsStr) {
|
||||
return AddIncludePaths(PathsStr, nullptr);
|
||||
}
|
||||
|
||||
///\brief Prints the current include paths that are used.
|
||||
///
|
||||
|
@ -25,22 +25,41 @@ namespace clang {
|
||||
namespace cling {
|
||||
namespace utils {
|
||||
|
||||
enum SplitMode {
|
||||
kPruneNonExistant, ///< Don't add non-existant paths into output
|
||||
kFailNonExistant, ///< Fail on any non-existant paths
|
||||
kAllowNonExistant ///< Add all paths whether they exist or not
|
||||
};
|
||||
|
||||
///\brief Collect the constituant paths from a PATH string.
|
||||
/// /bin:/usr/bin:/usr/local/bin -> {/bin, /usr/bin, /usr/local/bin}
|
||||
///
|
||||
/// All paths returned existed at the time of the call
|
||||
/// \param [in] PathStr - The PATH string to be split
|
||||
/// \param [out] Paths - All the paths in the string that exist
|
||||
/// \param [in] EarlyOut - If any path doesn't exist stop and return false
|
||||
/// \param [in] Mode - If any path doesn't exist stop and return false
|
||||
/// \param [in] Delim - The delimeter to use
|
||||
///
|
||||
/// \return true if all paths existed, otherwise false
|
||||
///
|
||||
bool SplitPaths(llvm::StringRef PathStr,
|
||||
llvm::SmallVectorImpl<llvm::StringRef>& Paths,
|
||||
bool EarlyOut = false,
|
||||
SplitMode Mode = kPruneNonExistant,
|
||||
llvm::StringRef Delim = llvm::StringRef(":"));
|
||||
|
||||
///\brief Adds multiple include paths separated by a delimter into the
|
||||
/// given HeaderSearchOptions. This adds the paths but does no further
|
||||
/// processing. See Interpreter::AddIncludePaths or CIFactory::createCI
|
||||
/// for examples of what needs to be done once the paths have been added.
|
||||
///
|
||||
///\param[in] PathsStr - Path(s)
|
||||
///\param[in] Opts - HeaderSearchOptions to add paths into
|
||||
///\param[in] Delim - Delimiter to separate paths or NULL if a single path
|
||||
///
|
||||
void AddIncludePaths(llvm::StringRef PathStr,
|
||||
clang::HeaderSearchOptions& Opts,
|
||||
const char* Delim = ":");
|
||||
|
||||
///\brief Copies the current include paths into the HeaderSearchOptions.
|
||||
///
|
||||
///\param[in] Opts - HeaderSearchOptions to read from
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "ClingUtils.h"
|
||||
|
||||
#include "DeclCollector.h"
|
||||
#include "cling-compiledata.h"
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
@ -43,7 +44,6 @@
|
||||
#include <memory>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include "cling-compiledata.h"
|
||||
#include <unistd.h>
|
||||
#define getcwd_func getcwd
|
||||
#endif
|
||||
@ -463,13 +463,19 @@ static bool getISysRoot(std::string& sysRoot) {
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
//
|
||||
// Dummy function so we can use dladdr to find the executable path.
|
||||
//
|
||||
void locate_cling_executable()
|
||||
{
|
||||
// This function isn't referenced outside its translation unit, but it
|
||||
// can't use the "static" keyword because its address is used for
|
||||
// GetMainExecutable (since some platforms don't support taking the
|
||||
// address of main, and some platforms can't implement GetMainExecutable
|
||||
// without being given the address of a function in the main executable).
|
||||
std::string GetExecutablePath(const char *Argv0) {
|
||||
// This just needs to be some symbol in the binary; C++ doesn't
|
||||
// allow taking the address of ::main however.
|
||||
void *MainAddr = (void*) intptr_t(GetExecutablePath);
|
||||
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
|
||||
}
|
||||
|
||||
|
||||
struct CompilerOpts {
|
||||
enum {
|
||||
kHasMinusX = 1,
|
||||
@ -589,8 +595,9 @@ namespace {
|
||||
}
|
||||
|
||||
static bool AddCxxPaths(llvm::StringRef PathStr, AdditionalArgList& Args) {
|
||||
using namespace cling::utils;
|
||||
llvm::SmallVector<llvm::StringRef, 6> Paths;
|
||||
if (!cling::utils::SplitPaths(PathStr, Paths, true))
|
||||
if (!SplitPaths(PathStr, Paths, kFailNonExistant))
|
||||
return false;
|
||||
|
||||
for (llvm::StringRef Path : Paths)
|
||||
@ -601,7 +608,8 @@ namespace {
|
||||
#endif
|
||||
|
||||
///\brief Adds standard library -I used by whatever compiler is found in PATH.
|
||||
static void AddHostArguments(std::vector<const char*>& args,
|
||||
static void AddHostArguments(llvm::StringRef clingBin,
|
||||
std::vector<const char*>& args,
|
||||
const char* llvmdir, const CompilerOpts& opts) {
|
||||
static AdditionalArgList sArguments;
|
||||
if (sArguments.empty()) {
|
||||
@ -655,10 +663,7 @@ namespace {
|
||||
#ifdef _LIBCPP_VERSION
|
||||
// Try to use a version of clang that is located next to cling
|
||||
// in case cling was built with a new/custom libc++
|
||||
std::string clang = llvm::sys::fs::getMainExecutable(
|
||||
"cling", (void*)(uintptr_t(locate_cling_executable)));
|
||||
|
||||
clang = llvm::sys::path::parent_path(clang);
|
||||
std::string clang = llvm::sys::path::parent_path(clingBin);
|
||||
buffer.assign(clang);
|
||||
llvm::sys::path::append(buffer, "clang");
|
||||
clang.assign(&buffer[0], buffer.size());
|
||||
@ -737,7 +742,7 @@ namespace {
|
||||
//
|
||||
resourcePath
|
||||
= CompilerInvocation::GetResourcesPath("cling",
|
||||
(void*)(intptr_t) locate_cling_executable);
|
||||
(void*)intptr_t(GetExecutablePath));
|
||||
} else {
|
||||
llvm::SmallString<512> tmp(llvmdir);
|
||||
llvm::sys::path::append(tmp, "lib", "clang", CLANG_VERSION_STRING);
|
||||
@ -938,6 +943,27 @@ namespace {
|
||||
To.insert(To.end(), From.begin(), From.end());
|
||||
}
|
||||
|
||||
|
||||
static void AddRuntimeIncludePaths(llvm::StringRef ClingBin,
|
||||
clang::HeaderSearchOptions& HOpts) {
|
||||
// Add configuration paths to interpreter's include files.
|
||||
#ifdef CLING_INCLUDE_PATHS
|
||||
cling::utils::AddIncludePaths(CLING_INCLUDE_PATHS, HOpts);
|
||||
#endif
|
||||
llvm::SmallString<512> P(ClingBin);
|
||||
if (!P.empty()) {
|
||||
// Remove /cling from foo/bin/clang
|
||||
llvm::StringRef ExeIncl = llvm::sys::path::parent_path(P);
|
||||
// Remove /bin from foo/bin
|
||||
ExeIncl = llvm::sys::path::parent_path(ExeIncl);
|
||||
P.resize(ExeIncl.size());
|
||||
// Get foo/include
|
||||
llvm::sys::path::append(P, "include");
|
||||
if (llvm::sys::fs::is_directory(P.str()))
|
||||
cling::utils::AddIncludePaths(P.str(), HOpts, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static CompilerInstance* createCIImpl(
|
||||
std::unique_ptr<llvm::MemoryBuffer> buffer,
|
||||
int argc,
|
||||
@ -954,6 +980,7 @@ namespace {
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
|
||||
std::string clingBin = GetExecutablePath(argv[0]);
|
||||
CompilerOpts copts(argv, argv + argc);
|
||||
|
||||
std::vector<const char*> argvCompile(argv, argv+1);
|
||||
@ -967,7 +994,7 @@ namespace {
|
||||
argvCompile.insert(argvCompile.end(), argv+1, argv + argc);
|
||||
|
||||
// Add host specific includes, -resource-dir if necessary, and -isysroot
|
||||
AddHostArguments(argvCompile, llvmdir, copts);
|
||||
AddHostArguments(clingBin, argvCompile, llvmdir, copts);
|
||||
|
||||
// Be explicit about the stdlib on OS X
|
||||
// Would be nice on Linux but will warn 'argument unused during compilation'
|
||||
@ -1178,6 +1205,24 @@ namespace {
|
||||
// the JIT to crash
|
||||
CI->getCodeGenOpts().VerifyModule = 0; // takes too long
|
||||
|
||||
if (!OnlyLex) {
|
||||
// -nobuiltininc
|
||||
clang::HeaderSearchOptions& HOpts = CI->getHeaderSearchOpts();
|
||||
if (CI->getHeaderSearchOpts().UseBuiltinIncludes)
|
||||
AddRuntimeIncludePaths(clingBin, HOpts);
|
||||
|
||||
// ### FIXME:
|
||||
// Want to update LLVM to 3.9 realease and better testing first, but
|
||||
// ApplyHeaderSearchOptions shouldn't even be called here:
|
||||
// 1. It's already been called via CI->createPreprocessor(TU_Complete)
|
||||
// 2. It could corrupt clang's directory cache
|
||||
// HeaderSearchOptions.::AddSearchPath is a better alternative
|
||||
|
||||
clang::ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), HOpts,
|
||||
PP.getLangOpts(),
|
||||
PP.getTargetInfo().getTriple());
|
||||
}
|
||||
|
||||
return CI.release(); // Passes over the ownership to the caller.
|
||||
}
|
||||
|
||||
|
@ -292,5 +292,3 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cling-compiledata.h
|
||||
|
||||
add_file_dependencies(${CMAKE_CURRENT_SOURCE_DIR}/CIFactory.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cling-compiledata.h)
|
||||
add_file_dependencies(${CMAKE_CURRENT_SOURCE_DIR}/Interpreter.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cling-compiledata.h)
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "cling/Utils/Paths.h"
|
||||
#include "ClingUtils.h"
|
||||
|
||||
#include "cling-compiledata.h"
|
||||
#include "DynamicLookup.h"
|
||||
#include "ExternalInterpreterSource.h"
|
||||
#include "ForwardDeclPrinter.h"
|
||||
@ -40,6 +39,7 @@
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Lex/HeaderSearch.h"
|
||||
#include "clang/Parse/Parser.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/SemaDiagnostic.h"
|
||||
@ -137,18 +137,6 @@ namespace cling {
|
||||
m_State->compare("aName");
|
||||
}
|
||||
|
||||
// This function isn't referenced outside its translation unit, but it
|
||||
// can't use the "static" keyword because its address is used for
|
||||
// GetMainExecutable (since some platforms don't support taking the
|
||||
// address of main, and some platforms can't implement GetMainExecutable
|
||||
// without being given the address of a function in the main executable).
|
||||
std::string GetExecutablePath(const char *Argv0) {
|
||||
// This just needs to be some symbol in the binary; C++ doesn't
|
||||
// allow taking the address of ::main however.
|
||||
void *MainAddr = (void*) (intptr_t) GetExecutablePath;
|
||||
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
|
||||
}
|
||||
|
||||
const Parser& Interpreter::getParser() const {
|
||||
return *m_IncrParser->getParser();
|
||||
}
|
||||
@ -212,10 +200,6 @@ namespace cling {
|
||||
|
||||
handleFrontendOptions();
|
||||
|
||||
// -nobuiltininc
|
||||
if (getCI()->getHeaderSearchOpts().UseBuiltinIncludes)
|
||||
AddRuntimeIncludePaths(argv[0]);
|
||||
|
||||
if (!noRuntime) {
|
||||
if (getCI()->getLangOpts().CPlusPlus)
|
||||
IncludeCXXRuntime();
|
||||
@ -297,30 +281,6 @@ namespace cling {
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::AddRuntimeIncludePaths(const char* argv0) {
|
||||
// Add configuration paths to interpreter's include files.
|
||||
#ifdef CLING_INCLUDE_PATHS
|
||||
llvm::SmallVector<llvm::StringRef, 6> Paths;
|
||||
utils::SplitPaths(CLING_INCLUDE_PATHS, Paths);
|
||||
for (llvm::StringRef Path : Paths)
|
||||
AddIncludePath(Path);
|
||||
#endif
|
||||
llvm::SmallString<512> P(GetExecutablePath(argv0));
|
||||
if (!P.empty()
|
||||
&& llvm::StringRef(argv0).endswith("cling")
|
||||
&& llvm::sys::path::filename(P) == "cling") {
|
||||
// Remove /cling from foo/bin/clang
|
||||
llvm::StringRef ExeIncl = llvm::sys::path::parent_path(P);
|
||||
// Remove /bin from foo/bin
|
||||
ExeIncl = llvm::sys::path::parent_path(ExeIncl);
|
||||
P.resize(ExeIncl.size());
|
||||
// Get foo/include
|
||||
llvm::sys::path::append(P, "include");
|
||||
if (llvm::sys::fs::is_directory(P.str()))
|
||||
AddIncludePath(P.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::IncludeCXXRuntime() {
|
||||
// Set up common declarations which are going to be available
|
||||
// only at runtime
|
||||
@ -361,31 +321,28 @@ namespace cling {
|
||||
declare("#include \"cling/Interpreter/CValuePrinter.h\"");
|
||||
}
|
||||
|
||||
void Interpreter::AddIncludePath(llvm::StringRef incpath)
|
||||
{
|
||||
// Add the given path to the list of directories in which the interpreter
|
||||
// looks for include files. Only one path item can be specified at a
|
||||
// time, i.e. "path1:path2" is not supported.
|
||||
|
||||
void Interpreter::AddIncludePaths(llvm::StringRef PathStr, const char* Delm) {
|
||||
CompilerInstance* CI = getCI();
|
||||
HeaderSearchOptions& headerOpts = CI->getHeaderSearchOpts();
|
||||
const bool IsFramework = false;
|
||||
const bool IsSysRootRelative = true;
|
||||
HeaderSearchOptions& HOpts = CI->getHeaderSearchOpts();
|
||||
|
||||
// Avoid duplicates; just return early if incpath is already in UserEntries.
|
||||
for (std::vector<HeaderSearchOptions::Entry>::const_iterator
|
||||
I = headerOpts.UserEntries.begin(),
|
||||
E = headerOpts.UserEntries.end(); I != E; ++I)
|
||||
if (I->Path == incpath)
|
||||
return;
|
||||
|
||||
headerOpts.AddPath(incpath, frontend::Angled, IsFramework,
|
||||
IsSysRootRelative);
|
||||
// Save the current number of entries
|
||||
size_t Idx = HOpts.UserEntries.size();
|
||||
utils::AddIncludePaths(PathStr, HOpts, Delm);
|
||||
|
||||
Preprocessor& PP = CI->getPreprocessor();
|
||||
clang::ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), headerOpts,
|
||||
PP.getLangOpts(),
|
||||
PP.getTargetInfo().getTriple());
|
||||
SourceManager& SM = PP.getSourceManager();
|
||||
FileManager& FM = SM.getFileManager();
|
||||
HeaderSearch& HSearch = PP.getHeaderSearchInfo();
|
||||
const bool isFramework = false;
|
||||
|
||||
// Add all the new entries into Preprocessor
|
||||
for (const size_t N = HOpts.UserEntries.size(); Idx < N; ++Idx) {
|
||||
const HeaderSearchOptions::Entry& E = HOpts.UserEntries[Idx];
|
||||
if (const clang::DirectoryEntry *DE = FM.getDirectory(E.Path)) {
|
||||
HSearch.AddSearchPath(DirectoryLookup(DE, SrcMgr::C_User, isFramework),
|
||||
E.Group == frontend::Angled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::DumpIncludePath(llvm::raw_ostream* S) {
|
||||
|
@ -115,28 +115,62 @@ void DumpIncludePaths(const clang::HeaderSearchOptions& Opts,
|
||||
}
|
||||
|
||||
bool SplitPaths(llvm::StringRef PathStr,
|
||||
llvm::SmallVectorImpl<llvm::StringRef>& Paths, bool EarlyOut,
|
||||
llvm::SmallVectorImpl<llvm::StringRef>& Paths,
|
||||
SplitMode Mode,
|
||||
llvm::StringRef Delim) {
|
||||
bool AllExisted = true;
|
||||
for (std::pair<llvm::StringRef, llvm::StringRef> Split = PathStr.split(Delim);
|
||||
!Split.second.empty(); Split = PathStr.split(Delim)) {
|
||||
if (!llvm::sys::fs::is_directory(Split.first)) {
|
||||
if (EarlyOut)
|
||||
if (Mode == kFailNonExistant)
|
||||
return false;
|
||||
AllExisted = false;
|
||||
if (Mode == kAllowNonExistant)
|
||||
Paths.push_back(Split.first);
|
||||
} else
|
||||
Paths.push_back(Split.first);
|
||||
|
||||
PathStr = Split.second;
|
||||
}
|
||||
|
||||
// Add remaining part
|
||||
if (llvm::sys::fs::is_directory(PathStr))
|
||||
Paths.push_back(PathStr);
|
||||
else
|
||||
if (!llvm::sys::fs::is_directory(PathStr)) {
|
||||
AllExisted = false;
|
||||
if (Mode == kAllowNonExistant)
|
||||
Paths.push_back(PathStr);
|
||||
} else
|
||||
Paths.push_back(PathStr);
|
||||
|
||||
return AllExisted;
|
||||
}
|
||||
|
||||
void AddIncludePaths(llvm::StringRef PathStr, clang::HeaderSearchOptions& HOpts,
|
||||
const char* Delim) {
|
||||
|
||||
llvm::SmallVector<llvm::StringRef, 10> Paths;
|
||||
if (Delim && *Delim)
|
||||
SplitPaths(PathStr, Paths, kAllowNonExistant, Delim);
|
||||
else
|
||||
Paths.push_back(PathStr);
|
||||
|
||||
// Avoid duplicates
|
||||
llvm::SmallVector<llvm::StringRef, 10> PathsChecked;
|
||||
for (llvm::StringRef Path : Paths) {
|
||||
bool Exists = false;
|
||||
for (const clang::HeaderSearchOptions::Entry& E : HOpts.UserEntries) {
|
||||
if ((Exists = E.Path == Path))
|
||||
break;
|
||||
}
|
||||
if (!Exists)
|
||||
PathsChecked.push_back(Path);
|
||||
}
|
||||
|
||||
const bool IsFramework = false;
|
||||
const bool IsSysRootRelative = true;
|
||||
for (llvm::StringRef Path : PathsChecked)
|
||||
HOpts.AddPath(Path, clang::frontend::Angled,
|
||||
IsFramework, IsSysRootRelative);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace cling
|
||||
|
1
test/Interfaces/Paths/A/A.h
Normal file
1
test/Interfaces/Paths/A/A.h
Normal file
@ -0,0 +1 @@
|
||||
const char* TestA = "TestA";
|
1
test/Interfaces/Paths/B/B.h
Normal file
1
test/Interfaces/Paths/B/B.h
Normal file
@ -0,0 +1 @@
|
||||
const char* TestB = "TestB";
|
1
test/Interfaces/Paths/C/C.h
Normal file
1
test/Interfaces/Paths/C/C.h
Normal file
@ -0,0 +1 @@
|
||||
const char* TestC = "TestC";
|
1
test/Interfaces/Paths/D/D.h
Normal file
1
test/Interfaces/Paths/D/D.h
Normal file
@ -0,0 +1 @@
|
||||
const char* TestD = "TestD";
|
32
test/Interfaces/paths.C
Normal file
32
test/Interfaces/paths.C
Normal file
@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// CLING - the C++ LLVM-based InterpreterG :)
|
||||
//
|
||||
// This file is dual-licensed: you can choose to license it under the University
|
||||
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
||||
// LICENSE.TXT for details.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// RUN: cat %s | %cling -DTEST_PATH=\"%p/\" -Xclang -verify 2>&1 | FileCheck %s
|
||||
|
||||
#include "cling/Interpreter/Interpreter.h"
|
||||
|
||||
gCling->AddIncludePaths(TEST_PATH "Paths/A:" TEST_PATH "Paths/B:"
|
||||
TEST_PATH "Paths/C");
|
||||
#include "A.h"
|
||||
#include "B.h"
|
||||
#include "C.h"
|
||||
|
||||
gCling->AddIncludePath(TEST_PATH "Paths/D");
|
||||
#include "D.h"
|
||||
|
||||
TestA
|
||||
// CHECK: (const char *) "TestA"
|
||||
TestB
|
||||
// CHECK: (const char *) "TestB"
|
||||
TestC
|
||||
// CHECK: (const char *) "TestC"
|
||||
TestD
|
||||
// CHECK: (const char *) "TestD"
|
||||
|
||||
// expected-no-diagnostics
|
||||
.q
|
Loading…
x
Reference in New Issue
Block a user