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:
Frederich Munch 2016-08-16 05:46:10 -04:00 committed by sftnight
parent 998ca7af13
commit 2142853de6
11 changed files with 185 additions and 91 deletions

View File

@ -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.
///

View File

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

View File

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

View File

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

View File

@ -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) {

View File

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

View File

@ -0,0 +1 @@
const char* TestA = "TestA";

View File

@ -0,0 +1 @@
const char* TestB = "TestB";

View File

@ -0,0 +1 @@
const char* TestC = "TestC";

View File

@ -0,0 +1 @@
const char* TestD = "TestD";

32
test/Interfaces/paths.C Normal file
View 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