Skip to content

Commit a6f001d

Browse files
committed
Implement detection of resource and include dirs.
This is useful when deployed CppInterOp needs to create an interpreter. Often the interpreter itself cannot use the clang toolchain logic to discover both the resource-dir and the include paths. This is because CppInterOp is a library and usually put in the `lib` folder whereas the toolchain logic expects things to be relative to the `bin` folder. In such setups, we can ask CppInterOp to detect the resource directory by asking clang about it. In the same vain, of finding C and C++ headers we can ask the system compiler about the include paths.
1 parent 687ae18 commit a6f001d

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

include/clang/Interpreter/CppInterOp.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,21 @@ namespace Cpp {
444444
/// Returns the resource-dir path (for headers).
445445
const char* GetResourceDir();
446446

447+
/// Uses the underlying clang compiler to detect the resource directory.
448+
/// In essence calling clang -print-resource-dir and checks if it ends with
449+
/// a compatible to CppInterOp version.
450+
///\param[in] ClangBinaryName - the name (or the full path) of the compiler
451+
/// to ask.
452+
std::string DetectResourceDir(const char* ClangBinaryName = "clang");
453+
454+
/// Asks the system compiler for its default include paths.
455+
///\param[out] Paths - the list of include paths returned by eg.
456+
/// `c++ -xc++ -E -v /dev/null 2>&1`
457+
///\param[in] CompilerName - the name (or the full path) of the compiler
458+
/// binary file.
459+
void DetectSystemCompilerIncludePaths(std::vector<std::string>& Paths,
460+
const char* CompilerName = "c++");
461+
447462
/// Secondary search path for headers, if not found using the
448463
/// GetResourceDir() function.
449464
void AddIncludePath(const char *dir);

lib/Interpreter/CppInterOp.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
#include <io.h>
3939
#ifndef STDOUT_FILENO
4040
#define STDOUT_FILENO 1
41+
// For exec().
42+
#include <stdio.h>
43+
#define popen(x, y) (_popen(x, y))
44+
#define pclose (_pclose)
4145
#endif
4246
#else
4347
#include <dlfcn.h>
@@ -2534,6 +2538,64 @@ namespace Cpp {
25342538
return getInterp().getCI()->getHeaderSearchOpts().ResourceDir.c_str();
25352539
}
25362540

2541+
///\returns 0 on success.
2542+
static bool exec(const char* cmd, std::vector<std::string>& outputs) {
2543+
#define DEBUG_TYPE "exec"
2544+
2545+
std::array<char, 256> buffer;
2546+
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
2547+
LLVM_DEBUG(dbgs() << "Executing command '" << cmd << "'\n");
2548+
2549+
if (!pipe) {
2550+
LLVM_DEBUG(dbgs() << "Execute failed!\n");
2551+
perror("exec: ");
2552+
return false;
2553+
}
2554+
2555+
LLVM_DEBUG(dbgs() << "Execute returned:\n");
2556+
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get())) {
2557+
LLVM_DEBUG(dbgs() << buffer.data());
2558+
llvm::StringRef trimmed = buffer.data();
2559+
outputs.push_back(trimmed.trim().str());
2560+
}
2561+
2562+
#undef DEBUG_TYPE
2563+
2564+
return true;
2565+
}
2566+
2567+
std::string DetectResourceDir(const char* ClangBinaryName /* = clang */) {
2568+
std::string cmd = std::string(ClangBinaryName) + " -print-resource-dir";
2569+
std::vector<std::string> outs;
2570+
exec(cmd.c_str(), outs);
2571+
if (outs.empty() || outs.size() > 1)
2572+
return "";
2573+
2574+
std::string detected_resource_dir = outs.back();
2575+
2576+
std::string version =
2577+
#if CLANG_VERSION_MAJOR < 16
2578+
CLANG_VERSION_STRING;
2579+
#else
2580+
CLANG_VERSION_MAJOR_STRING;
2581+
#endif
2582+
// We need to check if the detected resource directory is compatible.
2583+
if (llvm::sys::path::filename(detected_resource_dir) != version)
2584+
return "";
2585+
2586+
return detected_resource_dir;
2587+
}
2588+
2589+
void DetectSystemCompilerIncludePaths(std::vector<std::string>& Paths,
2590+
const char* CompilerName /*= "c++"*/) {
2591+
std::string cmd = "LC_ALL=C ";
2592+
cmd += CompilerName;
2593+
cmd += " -xc++ -E -v /dev/null 2>&1 | sed -n -e '/^.include/,${' -e '/^ "
2594+
"\\/.*/p' -e '}'";
2595+
std::vector<std::string> outs;
2596+
exec(cmd.c_str(), Paths);
2597+
}
2598+
25372599
void AddIncludePath(const char *dir) {
25382600
getInterp().AddIncludePath(dir);
25392601
}

unittests/CppInterOp/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ target_link_libraries(CppInterOpTests
1818
clangCppInterOp
1919
)
2020

21+
set_source_files_properties(InterpreterTest.cpp PROPERTIES COMPILE_DEFINITIONS
22+
"LLVM_BINARY_DIR=\"${LLVM_BINARY_DIR}\""
23+
)
2124
export_executable_symbols(CppInterOpTests)
2225

2326
unset(LLVM_LINK_COMPONENTS)

unittests/CppInterOp/InterpreterTest.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "clang/Interpreter/CppInterOp.h"
22

3+
#include "llvm/ADT/SmallString.h"
4+
#include "llvm/Support/Path.h"
5+
36
#include <gmock/gmock.h>
47
#include "gtest/gtest.h"
58

@@ -14,6 +17,7 @@ TEST(InterpreterTest, DISABLED_DebugFlag) {
1417
#else
1518
TEST(InterpreterTest, DebugFlag) {
1619
#endif // NDEBUG
20+
Cpp::CreateInterpreter();
1721
EXPECT_FALSE(Cpp::IsDebugOutputEnabled());
1822
std::string cerrs;
1923
testing::internal::CaptureStderr();
@@ -49,7 +53,7 @@ TEST(InterpreterTest, Evaluate) {
4953
EXPECT_FALSE(HadError) ;
5054
}
5155

52-
TEST(InterpreterTest, Process) {
56+
TEST(InterpreterTest, Process) {
5357
Cpp::CreateInterpreter();
5458
EXPECT_TRUE(Cpp::Process("") == 0);
5559
EXPECT_TRUE(Cpp::Process("int a = 12;") == 0);
@@ -80,3 +84,22 @@ TEST(InterpreterTest, CreateInterpreter) {
8084
EXPECT_TRUE(Cpp::GetNamed("cpp17"));
8185
EXPECT_FALSE(Cpp::GetNamed("cppUnknown"));
8286
}
87+
88+
#ifdef LLVM_BINARY_DIR
89+
TEST(InterpreterTest, DetectResourceDir) {
90+
#else
91+
TEST(InterpreterTest, DISABLED_DetectResourceDir) {
92+
#endif // LLVM_BINARY_DIR
93+
Cpp::CreateInterpreter();
94+
EXPECT_STRNE(Cpp::DetectResourceDir().c_str(), Cpp::GetResourceDir());
95+
llvm::SmallString<256> Clang(LLVM_BINARY_DIR);
96+
llvm::sys::path::append(Clang, "bin", "clang");
97+
std::string DetectedPath = Cpp::DetectResourceDir(Clang.str().str().c_str());
98+
EXPECT_STREQ(DetectedPath.c_str(), Cpp::GetResourceDir());
99+
}
100+
101+
TEST(InterpreterTest, DetectSystemCompilerIncludePaths) {
102+
std::vector<std::string> includes;
103+
Cpp::DetectSystemCompilerIncludePaths(includes);
104+
EXPECT_FALSE(includes.empty());
105+
}

0 commit comments

Comments
 (0)