Skip to content

Commit 892f9e6

Browse files
authored
[interop] Ship resource-dir Clang headers for self-contained install (compiler-research#33)
This is the second part of the patch that ships the required runtime clang headers. With this change, a build/install of cppjit is self-contained and with the LLVM that is statically linked into libClangCppInterOp.so, we drop the LLVM dependency on a target machine. The wheels build PR will exercise this on clean images without LLVM. The bundle is produced by the CMake install rules from the LLVM used at build time, so it stays identical to the headers libClangCppInterOp was compiled against under every install channel (pip, conda recipes, distro tooling, bare cmake --install) and for development builds against any provided LLVM_DIR. Only include/ ships, the JIT reads nothing else from a resource directory. This adds ~0.8 MB to the wheel (the headers compress well) and 8 MB to the installed package on-disk. Setups without the bundled headers such as raw build trees, or distributions that strip vendored copies, fall back to a single Cpp::DetectResourceDir("clang-<major>") probe and bare clang detection at CppInterOp's interpreter creation time.
1 parent c8165cb commit 892f9e6

2 files changed

Lines changed: 33 additions & 8 deletions

File tree

‎CMakeLists.txt‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ target_compile_definitions(cppjit PRIVATE
125125
CPPINTEROP_LIBRARY="cppjit_backend/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
126126
CPPINTEROP_INCLUDE_DIR="cppjit_backend/include"
127127
CPPJIT_CLANG_MAJOR="${LLVM_VERSION_MAJOR}"
128+
CPPJIT_CLANG_INCLUDE_DIR="cppjit_backend/lib/clang/${LLVM_VERSION_MAJOR}"
128129
)
129130

130131
target_include_directories(cppjit PRIVATE
@@ -175,6 +176,18 @@ install(CODE "
175176
file(INSTALL \"${CPPINTEROP_INSTALL_DIR}/include/\" DESTINATION \${CMAKE_INSTALL_PREFIX}/cppjit_backend/include)
176177
")
177178

179+
# ship the builtin headers of the build clang, laid out as a headers-only
180+
# resource dir: only include/ ships
181+
set(_clang_resource_dir "${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}")
182+
if(NOT EXISTS "${_clang_resource_dir}/include")
183+
message(FATAL_ERROR
184+
"No builtin headers at ${_clang_resource_dir}/include; the LLVM at "
185+
"${LLVM_DIR} carries no clang resource directory")
186+
endif()
187+
install(DIRECTORY "${_clang_resource_dir}/include/"
188+
DESTINATION "cppjit_backend/lib/clang/${LLVM_VERSION_MAJOR}/include"
189+
)
190+
178191
# the public cpyrt API headers keep their installed cpyrt/ prefix
179192
install(FILES
180193
src/cpyrt/API.h

‎src/interop/interop_wrapper.cxx‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ static inline bool is_integral(std::string& s) {
7878
struct InterOpPaths {
7979
std::string Library;
8080
std::string IncludeDir;
81+
std::string ClangIncludeDir; // empty when the bundled headers are absent
8182
};
8283

8384
// One relative layout, two anchors: prefer CppInterOp next to our own load
@@ -94,8 +95,17 @@ static InterOpPaths cppinterop_paths() {
9495
anchor = here;
9596
}
9697
#endif
97-
return {(anchor / CPPINTEROP_LIBRARY).string(),
98-
(anchor / CPPINTEROP_INCLUDE_DIR).string()};
98+
InterOpPaths Paths{(anchor / CPPINTEROP_LIBRARY).string(),
99+
(anchor / CPPINTEROP_INCLUDE_DIR).string(),
100+
{}};
101+
// The builtin headers of the build clang ship with every installed
102+
// package (see the CMake install rule); a raw build tree has none and
103+
// falls back to resource-dir detection.
104+
const std::filesystem::path bundled = anchor / CPPJIT_CLANG_INCLUDE_DIR;
105+
std::error_code ec;
106+
if (std::filesystem::exists(bundled / "include", ec))
107+
Paths.ClangIncludeDir = bundled.string();
108+
return Paths;
99109
}
100110

101111
// The one place libclangCppInterOp is dlopen'd.
@@ -109,7 +119,8 @@ static bool loadDispatchAPI(const InterOpPaths& Paths) {
109119

110120
// CppInterOp itself appends CPPINTEROP_EXTRA_INTERPRETER_ARGS inside
111121
// CreateInterpreter, so nothing needs to be forwarded from here.
112-
static interop::TInterp_t acquireOrCreateInterpreter() {
122+
static interop::TInterp_t
123+
acquireOrCreateInterpreter(const InterOpPaths& Paths) {
113124
if (auto existingInterp = Cpp::GetInterpreter())
114125
return existingInterp;
115126

@@ -119,10 +130,11 @@ static interop::TInterp_t acquireOrCreateInterpreter() {
119130
args.push_back("-march=native");
120131
#endif
121132
// Without clang's builtin headers the interpreter fails at its first
122-
// #include. CppInterOp probes only bare `clang`; when just
123-
// clang-<major> is installed, resolve and pass it explicitly.
124-
std::string resourceDir;
125-
if (Cpp::DetectResourceDir("clang").empty())
133+
// #include. Prefer the bundled copy: it matches the build clang and
134+
// needs no LLVM on the host. DetectResourceDir refuses version
135+
// mismatches, and CppInterOp itself probes only bare `clang`.
136+
std::string resourceDir = Paths.ClangIncludeDir;
137+
if (resourceDir.empty())
126138
resourceDir = Cpp::DetectResourceDir("clang-" CPPJIT_CLANG_MAJOR);
127139
if (!resourceDir.empty()) {
128140
args.push_back("-resource-dir");
@@ -219,7 +231,7 @@ extern "C" int LoadCppInterOp() {
219231
if (!loadDispatchAPI(Paths))
220232
return;
221233

222-
acquireOrCreateInterpreter();
234+
acquireOrCreateInterpreter(Paths);
223235
configureInterpreter(Paths);
224236
preloadHeaders();
225237
defineRuntimeHelpers();

0 commit comments

Comments
 (0)