cpp-interop: init at 1.9.0

This commit is contained in:
thomasjm
2026-06-30 14:23:37 -07:00
parent a85fe67ace
commit eb8494ce2e
+192
View File
@@ -0,0 +1,192 @@
{
lib,
fetchFromGitHub,
cmake,
ninja,
python3,
llvmPackages_21,
cling,
gcc-unwrapped,
libffi,
libxml2,
ncurses,
zlib,
zstd,
# tests
gtest,
# Which interpreter backend to build against. CppInterOp can use either
# clang-repl (from upstream LLVM/Clang) or Cling. They are mutually exclusive.
backend ? "clang-repl", # "clang-repl" | "cling"
}:
let
llvmPackages = llvmPackages_21;
inherit (llvmPackages) stdenv;
useCling = backend == "cling";
llvm = llvmPackages.llvm;
clang = llvmPackages.clang-unwrapped;
# For the cling backend we build against the LLVM/Clang/Cling that ship inside
# `cling` itself (its LLVM 20 fork), so the ABI matches libcling. The CMake
# config packages (LLVM, Clang, Cling) all live under cling.unwrapped.
clingRoot = cling.unwrapped;
# The Clang resource dir and standard-library include flags the JIT interpreter
# needs, since there is no system compiler to probe in the Nix sandbox. Both
# this package's own tests and xeus-cpp (via passthru) feed these to CppInterOp
# through CPPINTEROP_EXTRA_INTERPRETER_ARGS. The resource dir must match the
# Clang that CppInterOp was built against: the cling fork for the cling backend,
# upstream LLVM otherwise. -nostdinc(++) makes the search hermetic: only the
# -isystem paths below are used, never any stray host include dirs.
resourceDir =
if useCling then
"${clingRoot}/lib/clang/20"
else
"${lib.getLib clang}/lib/clang/${lib.versions.major llvm.version}";
interpreterArgs = [
"-nostdinc"
"-nostdinc++"
"-resource-dir"
resourceDir
"-isystem"
"${resourceDir}/include"
"-isystem"
"${gcc-unwrapped}/include/c++/${gcc-unwrapped.version}"
"-isystem"
"${gcc-unwrapped}/include/c++/${gcc-unwrapped.version}/${stdenv.hostPlatform.config}"
"-isystem"
"${lib.getDev stdenv.cc.libc}/include"
];
in
assert lib.assertOneOf "backend" backend [
"clang-repl"
"cling"
];
stdenv.mkDerivation (finalAttrs: {
pname = "cpp-interop-${backend}";
version = "1.9.0";
src = fetchFromGitHub {
owner = "compiler-research";
repo = "CppInterOp";
tag = "v${finalAttrs.version}";
hash = "sha256-am2WObER9dlNQU/VMTY2ScMe/w8c4N8m/DVyNwHiBnw=";
};
strictDeps = true;
__structuredAttrs = true;
nativeBuildInputs = [
cmake
ninja
python3
];
buildInputs = [
libffi
libxml2
ncurses
zlib
zstd
]
++ (
if useCling then
[ clingRoot ]
else
[
llvm
clang
]
);
# Upstream's unittests/CMakeLists.txt only fetches GoogleTest over the network
# (forbidden in the sandbox) when no gtest target exists; point it at the
# nixpkgs gtest instead so the tests can build offline.
postPatch = ''
substituteInPlace unittests/CMakeLists.txt \
--replace-fail "include(GoogleTest)" "find_package(GTest REQUIRED)"
'';
cmakeFlags = [
(lib.cmakeBool "BUILD_SHARED_LIBS" true)
(lib.cmakeBool "CPPINTEROP_USE_CLING" useCling)
(lib.cmakeBool "CPPINTEROP_USE_REPL" (!useCling))
(lib.cmakeBool "CPPINTEROP_ENABLE_TESTING" finalAttrs.finalPackage.doCheck)
]
++ (
if useCling then
[
"-DCling_DIR=${clingRoot}/lib/cmake/cling"
"-DLLVM_DIR=${clingRoot}/lib/cmake/llvm"
"-DClang_DIR=${clingRoot}/lib/cmake/clang"
]
else
[
"-DLLVM_DIR=${llvm.dev}/lib/cmake/llvm"
"-DClang_DIR=${clang.dev}/lib/cmake/clang"
]
);
# Run the upstream GoogleTest suite. Only the clang-repl backend is exercised;
# the Cling backend skips many of these tests upstream.
doCheck = !useCling;
checkInputs = [ gtest ];
checkPhase = ''
runHook preCheck
export CPPINTEROP_EXTRA_INTERPRETER_ARGS="${lib.concatStringsSep " " interpreterArgs}"
# Upstream registers the tests only in the unittests subdir; its
# check-cppinterop target builds them and runs ctest from the right place.
ninja check-cppinterop
runHook postCheck
'';
# Smoke test: drive the backend to JIT-compile and run a function, proving
# the installed library, headers and runtime linking all work together.
doInstallCheck = !useCling;
installCheckPhase = ''
runHook preInstallCheck
cat > smoke.cpp <<'EOF'
#include "CppInterOp/CppInterOp.h"
#include <cstdio>
int main() {
Cpp::CreateInterpreter();
if (Cpp::Declare("int square(int x) { return x * x; }") != 0) return 1;
bool hadError = false;
intptr_t result = Cpp::Evaluate("square(7)", &hadError);
if (hadError) return 2;
if (result != 49) { std::printf("expected 49, got %ld\n", (long)result); return 3; }
if (Cpp::GetNamed("square") == nullptr) return 4;
return 0;
}
EOF
$CXX -std=c++17 smoke.cpp -I$out/include -L$out/lib -lclangCppInterOp -o smoke
LD_LIBRARY_PATH=$out/lib ./smoke
runHook postInstallCheck
'';
passthru = {
inherit backend resourceDir interpreterArgs;
};
meta = {
description = "Clang-based C++ interoperability library (${backend} backend)";
homepage = "https://github.com/compiler-research/CppInterOp";
changelog = "https://github.com/compiler-research/CppInterOp/releases/tag/v${finalAttrs.version}";
license = with lib.licenses; [
asl20
llvm-exception
];
maintainers = with lib.maintainers; [ thomasjm ];
platforms = lib.platforms.unix;
};
})