Skip to content

Commit

Permalink
Implement detection of resource and include dirs.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vgvassilev committed Mar 16, 2024
1 parent 687ae18 commit f4fa34c
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 1 deletion.
15 changes: 15 additions & 0 deletions include/clang/Interpreter/CppInterOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,21 @@ namespace Cpp {
/// Returns the resource-dir path (for headers).
const char* GetResourceDir();

/// Uses the underlying clang compiler to detect the resource directory.
/// In essence calling clang -print-resource-dir and checks if it ends with
/// a compatible to CppInterOp version.
///\param[in] ClangBinaryName - the name (or the full path) of the compiler
/// to ask.
std::string DetectResourceDir(const char* ClangBinaryName = "clang");

/// Asks the system compiler for its default include paths.
///\param[out] Paths - the list of include paths returned by eg.
/// `c++ -xc++ -E -v /dev/null 2>&1`
///\param[in] CompilerName - the name (or the full path) of the compiler
/// binary file.
void DetectSystemCompilerIncludePaths(std::vector<std::string>& Paths,
const char* CompilerName = "c++");

/// Secondary search path for headers, if not found using the
/// GetResourceDir() function.
void AddIncludePath(const char *dir);
Expand Down
62 changes: 62 additions & 0 deletions lib/Interpreter/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
#include <io.h>
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
// For exec().
#include <stdio.h>
#define popen(x, y) (_popen(x, y))
#define pclose (_pclose)
#endif
#else
#include <dlfcn.h>
Expand Down Expand Up @@ -2534,6 +2538,64 @@ namespace Cpp {
return getInterp().getCI()->getHeaderSearchOpts().ResourceDir.c_str();
}

///\returns 0 on success.
static bool exec(const char* cmd, std::vector<std::string>& outputs) {
#define DEBUG_TYPE "exec"

std::array<char, 256> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
LLVM_DEBUG(dbgs() << "Executing command '" << cmd << "'\n");

if (!pipe) {
LLVM_DEBUG(dbgs() << "Execute failed!\n");
perror("exec: ");
return false;

Check warning on line 2552 in lib/Interpreter/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Interpreter/CppInterOp.cpp#L2550-L2552

Added lines #L2550 - L2552 were not covered by tests
}

LLVM_DEBUG(dbgs() << "Execute returned:\n");
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get())) {
LLVM_DEBUG(dbgs() << buffer.data());
llvm::StringRef trimmed = buffer.data();
outputs.push_back(trimmed.trim().str());
}

#undef DEBUG_TYPE

return true;
}

std::string DetectResourceDir(const char* ClangBinaryName /* = clang */) {
std::string cmd = std::string(ClangBinaryName) + " -print-resource-dir";
std::vector<std::string> outs;
exec(cmd.c_str(), outs);
if (outs.empty() || outs.size() > 1)
return "";

Check warning on line 2572 in lib/Interpreter/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Interpreter/CppInterOp.cpp#L2572

Added line #L2572 was not covered by tests

std::string detected_resource_dir = outs.back();

std::string version =
#if CLANG_VERSION_MAJOR < 16
CLANG_VERSION_STRING;
#else
CLANG_VERSION_MAJOR_STRING;
#endif
// We need to check if the detected resource directory is compatible.
if (llvm::sys::path::filename(detected_resource_dir) != version)
return "";

return detected_resource_dir;
}

void DetectSystemCompilerIncludePaths(std::vector<std::string>& Paths,
const char* CompilerName /*= "c++"*/) {
std::string cmd = "LC_ALL=C ";
cmd += CompilerName;
cmd += " -xc++ -E -v /dev/null 2>&1 | sed -n -e '/^.include/,${' -e '/^ "
"\\/.*/p' -e '}'";
std::vector<std::string> outs;
exec(cmd.c_str(), Paths);
}

void AddIncludePath(const char *dir) {
getInterp().AddIncludePath(dir);
}
Expand Down
3 changes: 3 additions & 0 deletions unittests/CppInterOp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ target_link_libraries(CppInterOpTests
clangCppInterOp
)

set_source_files_properties(InterpreterTest.cpp PROPERTIES COMPILE_DEFINITIONS
"LLVM_BINARY_DIR=\"${LLVM_BINARY_DIR}\""
)
export_executable_symbols(CppInterOpTests)

unset(LLVM_LINK_COMPONENTS)
Expand Down
25 changes: 24 additions & 1 deletion unittests/CppInterOp/InterpreterTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "clang/Interpreter/CppInterOp.h"

#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Path.h"

#include <gmock/gmock.h>
#include "gtest/gtest.h"

Expand All @@ -14,6 +17,7 @@ TEST(InterpreterTest, DISABLED_DebugFlag) {
#else
TEST(InterpreterTest, DebugFlag) {
#endif // NDEBUG
Cpp::CreateInterpreter();
EXPECT_FALSE(Cpp::IsDebugOutputEnabled());
std::string cerrs;
testing::internal::CaptureStderr();
Expand Down Expand Up @@ -49,7 +53,7 @@ TEST(InterpreterTest, Evaluate) {
EXPECT_FALSE(HadError) ;
}

TEST(InterpreterTest, Process) {
TEST(InterpreterTest, Process) {
Cpp::CreateInterpreter();
EXPECT_TRUE(Cpp::Process("") == 0);
EXPECT_TRUE(Cpp::Process("int a = 12;") == 0);
Expand Down Expand Up @@ -80,3 +84,22 @@ TEST(InterpreterTest, CreateInterpreter) {
EXPECT_TRUE(Cpp::GetNamed("cpp17"));
EXPECT_FALSE(Cpp::GetNamed("cppUnknown"));
}

#ifdef LLVM_BINARY_DIR
TEST(InterpreterTest, DetectResourceDir) {
#else
TEST(InterpreterTest, DISABLED_DetectResourceDir) {
#endif // LLVM_BINARY_DIR
Cpp::CreateInterpreter();
EXPECT_STRNE(Cpp::DetectResourceDir().c_str(), Cpp::GetResourceDir());
llvm::SmallString<256> Clang(LLVM_BINARY_DIR);
llvm::sys::path::append(Clang, "bin", "clang");
std::string DetectedPath = Cpp::DetectResourceDir(Clang.str().str().c_str());
EXPECT_STREQ(DetectedPath.c_str(), Cpp::GetResourceDir());
}

TEST(InterpreterTest, DetectSystemCompilerIncludePaths) {
std::vector<std::string> includes;
Cpp::DetectSystemCompilerIncludePaths(includes);
EXPECT_FALSE(includes.empty());
}

0 comments on commit f4fa34c

Please sign in to comment.