diff --git a/CMakeLists.txt b/CMakeLists.txt index 5003b02d..f5ac5e59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,10 @@ set(CMAKE_CXX_EXTENSIONS NO) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +include(GNUInstallDirs) + set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") set(XEUS_CPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -33,20 +37,6 @@ set(${PROJECT_NAME}_VERSION ${XEUS_CPP_VERSION_MAJOR}.${XEUS_CPP_VERSION_MINOR}.${XEUS_CPP_VERSION_PATCH}) message(STATUS "Building xeus-cpp v${${PROJECT_NAME}_VERSION}") -# Configuration -# ============= - -include(GNUInstallDirs) - -if (NOT DEFINED XEUS_CPP_KERNELSPEC_PATH) - set(XEUS_CPP_KERNELSPEC_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/") -endif () - -configure_file ( - "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xcpp/kernel.json.in" - "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xcpp/kernel.json" -) - # Build options # ============= @@ -121,13 +111,29 @@ if(EMSCRIPTEN) set(EMSCRIPTEN_FEATURES "${EMSCRIPTEN_FEATURES} -s \"EXTRA_EXPORTED_RUNTIME_METHODS=[ENV']\"") endif() -find_package(Clang REQUIRED) -include(AddLLVM) -include(HandleLLVMOptions) -add_definitions(${LLVM_DEFINITIONS}) +find_package(CppInterOp REQUIRED) +if(CppInterOp_FOUND) + message(STATUS "Found CppInterOp: config=${CPPINTEROP_CONFIG} dir=${CPPINTEROP_DIR} (found version=${CPPINTEROP_VERSION} compatible with Clang ${CPPINTEROP_LLVM_VERSION_MAJOR}.x)") +endif() + find_package(argparse REQUIRED) find_package(pugixml REQUIRED) +# Configuration +# ============= + +if (NOT DEFINED XEUS_CPP_KERNELSPEC_PATH) + set(XEUS_CPP_KERNELSPEC_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/") +endif () + +set(XEUS_CPP_PATH "$ENV{PATH}") +set(XEUS_CPP_LD_LIBRARY_PATH "$ENV{LD_LIBRARY_PATH}") +set(XEUS_CPP_RESOURCE_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/clang/${CppInterOp_CLANG_VERSION}) +configure_file ( + "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xcpp/kernel.json.in" + "${CMAKE_CURRENT_BINARY_DIR}/share/jupyter/kernels/xcpp/kernel.json" +) + # Source files # ============ @@ -174,7 +180,8 @@ include(CheckCXXCompilerFlag) string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib; ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib;${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") +list(REMOVE_DUPLICATES CMAKE_INSTALL_RPATH) macro(xeus_cpp_set_common_options target_name) if (MSVC) @@ -228,7 +235,6 @@ macro(xeus_cpp_set_kernel_options target_name) find_package(Threads) target_link_libraries(${target_name} PRIVATE ${CMAKE_THREAD_LIBS_INIT}) endif() - endmacro() # Common macro for shared and static library @@ -264,7 +270,8 @@ macro(xeus_cpp_create_target target_name linkage output_name) set(XEUS_CPP_XEUS_TARGET xeus-static) endif () - target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangInterpreter pugixml argparse::argparse xtl) + target_link_libraries(${target_name} PUBLIC ${XEUS_CPP_XEUS_TARGET} clangCppInterOp pugixml argparse::argparse xtl) + if (WIN32 OR CYGWIN) # elseif (APPLE) @@ -274,7 +281,7 @@ macro(xeus_cpp_create_target target_name linkage output_name) find_package(Threads) # TODO: add Threads as a dependence of xeus-static? target_link_libraries(${target_name} PRIVATE ${CMAKE_THREAD_LIBS_INIT}) endif() - + endmacro() # xeus-cpp-headers @@ -371,7 +378,7 @@ if(XEUS_CPP_BUILD_EXECUTABLE OR EMSCRIPTEN) set(XJUPYTER_DATA_DIR "share/jupyter" CACHE STRING "Jupyter data directory") # Install xcpp Jupyter kernelspec - set(KERNELSPEC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels) + set(KERNELSPEC_DIR ${CMAKE_CURRENT_BINARY_DIR}/share/jupyter/kernels) install(DIRECTORY ${KERNELSPEC_DIR} DESTINATION ${XJUPYTER_DATA_DIR} PATTERN "*.in" EXCLUDE) diff --git a/README.md b/README.md index 226a89d8..66a13d61 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ source activate "xeus-cpp" ``` We will now install the dependencies needed to compile xeux-cpp from source within this environment by executing the following ```bash -mamba install notebook cmake cxx-compiler xeus-zmq nlohmann_json=3.11.2 cppzmq xtl jupyterlab clangdev=16 cpp-argparse pugixml doctest -c conda-forge +mamba install notebook cmake cxx-compiler xeus-zmq nlohmann_json=3.11.2 cppzmq xtl jupyterlab CppInterOp cpp-argparse<3.1 pugixml doctest -c conda-forge ``` Now you can compile the kernel from the source by executing (replace `$CONDA_PREFIX` with a custom installation prefix if need be) ```bash @@ -65,13 +65,13 @@ http://xeus-cpp.readthedocs.io - [clang](https://github.com/llvm/llvm-project/) - [argparse](https://github.com/p-ranav/argparse) -| `xeus-cpp` | `xeus-zmq` | `xtl` | `clang` | `pugixml` | `cppzmq` | `cpp-argparse`| `nlohmann_json` | `dirent` (windows only) | -|------------|-----------------|-----------------|-----------|-----------|----------|---------------|-----------------|-------------------------| -| main | >=1.0.0,<2.0.0 | >=0.7.7,<0.8.0 | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | -| 0.3.0 | >=1.0.0,<2.0.0 | >=0.7.7,<0.8.0 | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | -| 0.2.0 | >=1.0.0,<2.0.0 | >=0.7.7,<0.8.0 | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | -| 0.1.0 | >=1.0.0,<2.0.0 | >=0.7.0,<0.8.0 | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | -| 0.0.1 | >=1.0.0,<2.0.0 | >=0.7.0,<0.8.0 | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | +| `xeus-cpp` | `xeus-zmq` | `xtl` | `CppInterOp` | `clang` | `pugixml` | `cppzmq` | `cpp-argparse`| `nlohmann_json` | `dirent` (windows only) | +|------------|-----------------|-----------------|--------------|-----------|-----------|----------|---------------|-----------------|-------------------------| +| main | >=1.0.0,<2.0.0 | >=0.7.7,<0.8.0 | >=1.2.0 | | ~1.8.1 | ~4.3.0 | <3.1 | >=3.11.2,<4.0 | >=2.3.2,<3 | +| 0.3.0 | >=1.0.0,<2.0.0 | >=0.7.7,<0.8.0 | | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | +| 0.2.0 | >=1.0.0,<2.0.0 | >=0.7.7,<0.8.0 | | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | +| 0.1.0 | >=1.0.0,<2.0.0 | >=0.7.0,<0.8.0 | | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | +| 0.0.1 | >=1.0.0,<2.0.0 | >=0.7.0,<0.8.0 | | >=16,<17 | ~1.8.1 | ~4.3.0 | ~2.9 | >=3.6.1,<4.0 | >=2.3.2,<3 | ## Contributing diff --git a/environment-dev.yml b/environment-dev.yml index e0acd574..34ce4434 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -11,9 +11,9 @@ dependencies: - nlohmann_json=3.11.2 - cppzmq - xtl - - clangdev >=16,<17 + - CppInterOp - pugixml - - cpp-argparse + - cpp-argparse <3.1 - zlib # Test dependencies - pytest diff --git a/environment-wasm-host.yml b/environment-wasm-host.yml index 4bb96d48..a9354956 100644 --- a/environment-wasm-host.yml +++ b/environment-wasm-host.yml @@ -4,9 +4,9 @@ channels: - https://repo.mamba.pm/conda-forge dependencies: - nlohmann_json - - xeus-lite + - xeus-lite <2.0 - xeus >=3.0.5,<4.0 - xtl >=0.7,<0.8 - - llvm =16.0.6 + - CppInterOp - cpp-argparse - pugixml diff --git a/include/xcpp/xdisplay.hpp b/include/xcpp/xdisplay.hpp index 1cb1d443..05acdccf 100644 --- a/include/xcpp/xdisplay.hpp +++ b/include/xcpp/xdisplay.hpp @@ -14,6 +14,8 @@ #include "xcpp/xmime.hpp" +#include "xeus/xinterpreter.hpp" + namespace nl = nlohmann; namespace xcpp diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index bec47613..a56abe79 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -16,7 +16,7 @@ #include #include -#include +#include "clang/Interpreter/CppInterOp.h" // from CppInterOp package #include @@ -76,10 +76,6 @@ namespace xcpp void init_preamble(); void init_magic(); - std::string get_stdopt(int argc, const char* const* argv); - - std::unique_ptr m_interpreter; - std::string m_version; xmagics_manager xmagics; diff --git a/share/jupyter/kernels/xcpp/kernel.json.in b/share/jupyter/kernels/xcpp/kernel.json.in index f053f27c..4fb8d36a 100644 --- a/share/jupyter/kernels/xcpp/kernel.json.in +++ b/share/jupyter/kernels/xcpp/kernel.json.in @@ -1,9 +1,16 @@ { "display_name": "cpp 14 (xcpp)", + "env": { + "PATH":"@XEUS_CPP_PATH@", + "LD_LIBRARY_PATH":"@XEUS_CPP_LD_LIBRARY_PATH@" + }, "argv": [ "@XEUS_CPP_KERNELSPEC_PATH@xcpp", "-f", - "{connection_file}" + "{connection_file}", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-I", "@CMAKE_INSTALL_PREFIX@/include", + "-std=c++14"@XEUS_CPP_OMP@ ], "language": "cpp", "metadata": {"debugger": false diff --git a/src/xinspect.hpp b/src/xinspect.hpp index 3b3fab91..17c0cdcc 100644 --- a/src/xinspect.hpp +++ b/src/xinspect.hpp @@ -13,7 +13,6 @@ #include #include - #include #include @@ -24,6 +23,8 @@ #include "xdemangle.hpp" #include "xparser.hpp" +#include "clang/Interpreter/CppInterOp.h" + namespace xcpp { struct node_predicate @@ -79,22 +80,23 @@ namespace xcpp } }; - std::string find_type(const std::string& expression, clang::Interpreter& interpreter) - { - auto PTU = interpreter.Parse(expression + ";"); - if (llvm::Error Err = PTU.takeError()) { - llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); - return ""; - } + std::string find_type_slow(const std::string& expression) { + static unsigned long long var_count = 0; + + if (auto *type = Cpp::GetType(expression)) + return Cpp::GetQualifiedName(type); - clang::Decl *D = *PTU->TUPart->decls_begin(); - if (!llvm::isa(D)) - return ""; + // Here we might need to deal with integral types such as 3.14. - clang::Expr *E = llvm::cast(llvm::cast(D)->getStmt()); + std::string id = "__Xeus_GetType_" + std::to_string(var_count++); + std::string using_clause = "using " + id + " = __typeof__(" + expression + ");\n"; - clang::QualType QT = E->getType(); - return QT.getAsString(); + if (!Cpp::Declare(using_clause.c_str(), /*silent=*/false)) { + Cpp::TCppScope_t lookup = Cpp::GetNamed(id, nullptr); + Cpp::TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup); + return Cpp::GetQualifiedCompleteName(Cpp::GetCanonicalType(lookup_ty)); + } + return ""; } static nl::json read_tagconfs(const char* path) @@ -111,17 +113,16 @@ namespace xcpp return result; } - std::pair is_inspect_request(const std::string code, std::regex re) + std::pair is_inspect_request(const std::string& code, + const std::regex& re) { std::smatch inspect; - if (std::regex_search(code, inspect, re)){ + if (std::regex_search(code, inspect, re)) return std::make_pair(true, inspect); - } return std::make_pair(false, inspect); - } - void inspect(const std::string& code, nl::json& kernel_res, clang::Interpreter& interpreter) + void inspect(const std::string& code, nl::json& kernel_res) { std::string tagconf_dir = XCPP_TAGCONFS_DIR; std::string tagfiles_dir = XCPP_TAGFILES_DIR; @@ -144,9 +145,9 @@ namespace xcpp // Method or variable of class found (xxxx.yyyy) if (std::regex_search(to_inspect, method, std::regex(R"((.*)\.(\w*)$)"))) { - std::string typename_ = find_type(method[1], interpreter); + std::string type_name = find_type_slow(method[1]); - if (!typename_.empty()) + if (!type_name.empty()) { for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it) { @@ -154,8 +155,8 @@ namespace xcpp tagfile = it->at("tagfile"); std::string filename = tagfiles_dir + "/" + tagfile; pugi::xml_document doc; - doc.load_file(filename.c_str()); - class_member_predicate predicate{typename_, "function", method[2]}; + pugi::xml_parse_result result = doc.load_file(filename.c_str()); + class_member_predicate predicate{type_name, "function", method[2]}; auto node = doc.find_node(predicate); if (!node.empty()) { @@ -178,8 +179,8 @@ namespace xcpp } else { - std::string typename_ = find_type(to_inspect, interpreter); - find_string = (typename_.empty()) ? to_inspect : typename_; + std::string type_name = find_type_slow(to_inspect); + find_string = (type_name.empty()) ? to_inspect : type_name; } for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it) @@ -188,7 +189,7 @@ namespace xcpp tagfile = it->at("tagfile"); std::string filename = tagfiles_dir + "/" + tagfile; pugi::xml_document doc; - doc.load_file(filename.c_str()); + pugi::xml_parse_result result = doc.load_file(filename.c_str()); for (auto c : check) { node_predicate predicate{c, find_string}; @@ -266,8 +267,7 @@ namespace xcpp using xpreamble::pattern; const std::string spattern = R"(^\?)"; - xintrospection(clang::Interpreter& p) - : m_interpreter{p} + xintrospection() { pattern = spattern; } @@ -277,17 +277,15 @@ namespace xcpp std::regex re(spattern + R"((.*))"); std::smatch to_inspect; std::regex_search(code, to_inspect, re); - inspect(to_inspect[1], kernel_res, m_interpreter); + inspect(to_inspect[1], kernel_res); } virtual xpreamble* clone() const override { return new xintrospection(*this); } - - private: - - clang::Interpreter& m_interpreter; }; + } -#endif + +#endif // XEUS_CPP_INSPECT_HPP diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 09fb27ce..b25606e5 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -7,241 +7,52 @@ * The full license is in the file LICENSE, distributed with this software. * ************************************************************************************/ -#include "xeus-cpp/xinterpreter.hpp" - -#include "xinput.hpp" -#include "xinspect.hpp" -// #include "xmagics/executable.hpp" -// #include "xmagics/execution.hpp" -#include "xmagics/os.hpp" -#include "xparser.hpp" -#include "xsystem.hpp" - -#include - -#include "xeus-cpp/xbuffer.hpp" -#include "xeus-cpp/xeus_cpp_config.hpp" - -#include "xeus-cpp/xmagics.hpp" - -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -//#include - - - #include #include // required before including llvm/ExecutionEngine/Orc/LLJIT.h because missing llvm/Object/SymbolicFile.h #include #include #include -#include #include #include #include -std::string DiagnosticOutput; -llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput); -auto DiagPrinter = std::make_unique(DiagnosticsOS, new clang::DiagnosticOptions()); - -///\returns true on error. -static bool -process_code(clang::Interpreter& Interp, const std::string& code, llvm::raw_string_ostream& error_stream) -{ - - if (code.substr(0, 1) == "?") - { - error_stream << " "; - return true; - } - else { - auto PTU = Interp.Parse(code); - if (!PTU) - { - auto Err = PTU.takeError(); - error_stream << DiagnosticsOS.str(); - // avoid printing the "Parsing failed error" - // llvm::logAllUnhandledErrors(std::move(Err), error_stream, "error: "); - return true; - } - if (PTU->TheModule) - { - llvm::Error ex = Interp.Execute(*PTU); - error_stream << DiagnosticsOS.str(); - if (code.substr(0, 3) == "int") - { - for (clang::Decl* D : PTU->TUPart->decls()) - { - if (clang::VarDecl* VD = llvm::dyn_cast(D)) - { - auto Name = VD->getNameAsString(); - auto Addr = Interp.getSymbolAddress(clang::GlobalDecl(VD)); - if (!Addr) - { - llvm::logAllUnhandledErrors(Addr.takeError(), error_stream, "error: "); - return true; - } - } - } - } - else if (code.substr(0, 16) == "std::vector") - { - for (clang::Decl* D : PTU->TUPart->decls()) - { - if (clang::VarDecl* VD = llvm::dyn_cast(D)) - { - auto Name = VD->getNameAsString(); - auto Addr = Interp.getSymbolAddress(clang::GlobalDecl(VD)); - if (!Addr) - { - llvm::logAllUnhandledErrors(Addr.takeError(), error_stream, "error: "); - return true; - } - } - } - } - - llvm::logAllUnhandledErrors(std::move(ex), error_stream, "error: "); - return false; - } - } - return false; -} - -using Args = std::vector; - -static std::unique_ptr -create_interpreter(const Args& ExtraArgs = {}, clang::DiagnosticConsumer* Client = nullptr) -{ - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - Args ClangArgs = {"-Xclang", "-emit-llvm-only", "-Xclang", "-diagnostic-log-file", "-Xclang", "-", "-xc++"}; - ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); - auto CI = cantFail(clang::IncrementalCompilerBuilder::create(ClangArgs)); - if (Client) - { - CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); - } - return cantFail(clang::Interpreter::create(std::move(CI))); -} - -static void -inject_symbol(llvm::StringRef LinkerMangledName, llvm::JITTargetAddress KnownAddr, clang::Interpreter& Interp) -{ - using namespace llvm; - using namespace llvm::orc; - - auto Symbol = Interp.getSymbolAddress(LinkerMangledName); //, /*IncludeFromHost=*/true); - - if (Error Err = Symbol.takeError()) - { - logAllUnhandledErrors(std::move(Err), errs(), "[IncrementalJIT] define() failed1: "); - return; - } - - // Nothing to define, we are redefining the same function. FIXME: Diagnose. - if (*Symbol && (JITTargetAddress) *Symbol == KnownAddr) - { - return; - } - - // Let's inject it - bool Inserted; - SymbolMap::iterator It; - static llvm::orc::SymbolMap m_InjectedSymbols; - - llvm::orc::LLJIT* Jit = const_cast(Interp.getExecutionEngine()); - JITDylib& DyLib = Jit->getMainJITDylib(); - - std::tie(It, Inserted) = m_InjectedSymbols.try_emplace( - Jit->getExecutionSession().intern(LinkerMangledName), - JITEvaluatedSymbol(KnownAddr, JITSymbolFlags::Exported) - ); - assert(Inserted && "Why wasn't this found in the initial Jit lookup?"); - - // We want to replace a symbol with a custom provided one. - if (Symbol && KnownAddr) - { - // The symbol be in the DyLib or in-process. - if (auto Err = DyLib.remove({It->first})) - { - logAllUnhandledErrors(std::move(Err), errs(), "[IncrementalJIT] define() failed2: "); - return; - } - } - - if (Error Err = DyLib.define(absoluteSymbols({*It}))) - { - logAllUnhandledErrors(std::move(Err), errs(), "[IncrementalJIT] define() failed3: "); - } -} - -namespace utils -{ - void AddIncludePath(llvm::StringRef Path, clang::HeaderSearchOptions& HOpts) - { - bool Exists = false; - for (const clang::HeaderSearchOptions::Entry& E : HOpts.UserEntries) - { - if ((Exists = E.Path == Path)) - { - break; - } - } - if (Exists) - { - return; - } +#include - HOpts.AddPath(Path, clang::frontend::Angled, false /* IsFramework */, true /* IsSysRootRelative */); +#include - if (HOpts.Verbose) - { - // std::clog << "Added include paths " << Path << std::endl; - } - } -} +#include "xeus-cpp/xbuffer.hpp" +#include "xeus-cpp/xeus_cpp_config.hpp" -void AddIncludePath(clang::Interpreter& Interp, llvm::StringRef Path) -{ - clang::CompilerInstance* CI = const_cast(Interp.getCompilerInstance()); - clang::HeaderSearchOptions& HOpts = CI->getHeaderSearchOpts(); +#include "xeus-cpp/xinterpreter.hpp" +#include "xeus-cpp/xmagics.hpp" - // Save the current number of entries - std::size_t Idx = HOpts.UserEntries.size(); - utils::AddIncludePath(Path, HOpts); +#include "xinput.hpp" +#include "xinspect.hpp" +#include "xmagics/os.hpp" +#include "xparser.hpp" +#include "xsystem.hpp" - clang::Preprocessor& PP = CI->getPreprocessor(); - clang::SourceManager& SM = CI->getSourceManager(); - clang::FileManager& FM = SM.getFileManager(); - clang::HeaderSearch& HSearch = PP.getHeaderSearchInfo(); - const bool isFramework = false; +using Args = std::vector; - // Add all the new entries into Preprocessor - for (; Idx < HOpts.UserEntries.size(); ++Idx) - { - const clang::HeaderSearchOptions::Entry& E = HOpts.UserEntries[Idx]; - if (auto DE = FM.getOptionalDirectoryRef(E.Path)) - { - HSearch.AddSearchPath( - clang::DirectoryLookup(*DE, clang::SrcMgr::C_User, isFramework), - E.Group == clang::frontend::Angled - ); - } - } +void* createInterpreter(const Args &ExtraArgs = {}) { + Args ClangArgs = {/*"-xc++"*/"-v"}; // ? {"-Xclang", "-emit-llvm-only", "-Xclang", "-diagnostic-log-file", "-Xclang", "-", "-xc++"}; + if (std::find(ExtraArgs.begin(), ExtraArgs.end(), "-resource-dir") != ExtraArgs.end()) { + std::string resource_dir = Cpp::DetectResourceDir(); + if (resource_dir.empty()) + std::cerr << "Failed to detect the resource-dir\n"; + ClangArgs.push_back("-resource-dir"); + ClangArgs.push_back(resource_dir.c_str()); + } + std::vector CxxSystemIncludes; + Cpp::DetectSystemCompilerIncludePaths(CxxSystemIncludes); + for (const std::string& CxxInclude : CxxSystemIncludes) { + ClangArgs.push_back("-isystem"); + ClangArgs.push_back(CxxInclude.c_str()); + } + ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); + // FIXME: We should process the kernel input options and conditionally pass + // the gpu args here. + return Cpp::CreateInterpreter(ClangArgs/*, {"-cuda"}*/); } using namespace std::placeholders; @@ -250,26 +61,48 @@ namespace xcpp { void interpreter::configure_impl() { - // todo: why is error_stream necessary - std::string error_message; - llvm::raw_string_ostream error_stream(error_message); - // Expose xinterpreter instance to interpreted C++ - process_code(*m_interpreter, "#include \"xeus/xinterpreter.hpp\"", error_stream); - std::string code = "xeus::register_interpreter(static_cast((void*)" - + std::to_string(intptr_t(this)) + "));"; - process_code(*m_interpreter, code.c_str(), error_stream); + xeus::register_interpreter(this); + } + + static std::string get_stdopt() + { + // We need to find what's the C++ version the interpreter runs with. + const char* code = R"( +int __get_cxx_version () { +#if __cplusplus > 202302L + return 26; +#elif __cplusplus > 202002L + return 23; +#elif __cplusplus > 201703L + return 20; +#elif __cplusplus > 201402L + return 17; +#elif __cplusplus > 201103L || (defined(_WIN32) && _MSC_VER >= 1900) + return 14; +#elif __cplusplus >= 201103L + return 11; +#else + return 0; +#endif + } +__get_cxx_version () + )"; + + long cxx_version = Cpp::Evaluate(code); + return std::to_string(cxx_version); } - interpreter::interpreter(int argc, const char* const* argv) - : m_interpreter(create_interpreter(Args() /*argv + 1, argv + argc)*/, DiagPrinter.get())) - , m_version(get_stdopt(argc, argv)) - , // Extract C++ language standard version from command-line option + + interpreter::interpreter(int argc, const char* const* argv) : xmagics() , p_cout_strbuf(nullptr) , p_cerr_strbuf(nullptr) , m_cout_buffer(std::bind(&interpreter::publish_stdout, this, _1)) , m_cerr_buffer(std::bind(&interpreter::publish_stderr, this, _1)) { + //NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) + createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); + m_version = get_stdopt(); redirect_output(); init_includes(); init_preamble(); @@ -320,15 +153,10 @@ namespace xcpp std::cerr.rdbuf(&null); } - // Scope guard performing the temporary redirection of input requests. - auto input_guard = input_redirection(allow_stdin); - std::string error_message; - llvm::raw_string_ostream error_stream(error_message); // Attempt normal evaluation - try { - compilation_result = process_code(*m_interpreter, code, error_stream); + compilation_result = Cpp::Process(code.c_str()); } catch (std::exception& e) { @@ -341,17 +169,14 @@ namespace xcpp errorlevel = 1; ename = "Error :"; } - + if (compilation_result) { errorlevel = 1; ename = "Error :"; - evalue = error_stream.str(); + evalue = "Compilation error!"; } - error_stream.str().clear(); - DiagnosticsOS.str().clear(); - // Flush streams std::cout << std::flush; std::cerr << std::flush; @@ -423,7 +248,7 @@ namespace xcpp auto inspect_request = is_inspect_request(code.substr(0, cursor_pos), re); if (inspect_request.first) { - inspect(inspect_request.second[0], kernel_res, *m_interpreter); + inspect(inspect_request.second[0], kernel_res); } return kernel_res; } @@ -454,8 +279,7 @@ namespace xcpp " > < __/ |_| \\__ \\\n" " /_/\\_\\___|\\__,_|___/\n" "\n" - " xeus-cpp: a C++ Jupyter kernel - based on Clang\n"; - banner.append(m_version); + " xeus-cpp: a C++ Jupyter kernel - based on Clang-repl\n"; result["banner"] = banner; result["language_info"]["name"] = "C++"; result["language_info"]["version"] = m_version; @@ -498,49 +322,6 @@ namespace xcpp return s; } - static int printf_jit(const char* format, ...) - { - std::va_list args; - va_start(args, format); - - std::string buf = c_format(format, args); - std::cout << buf; - - va_end(args); - - return buf.size(); - } - - static int fprintf_jit(std::FILE* stream, const char* format, ...) - { - std::va_list args; - va_start(args, format); - - int ret; - if (stream == stdout || stream == stderr) - { - std::string buf = c_format(format, args); - if (stream == stdout) - { - std::cout << buf; - } - else if (stream == stderr) - { - std::cerr << buf; - } - ret = buf.size(); - } - else - { - // Just forward to vfprintf. - ret = vfprintf(stream, format, args); - } - - va_end(args); - - return ret; - } - void interpreter::redirect_output() { p_cout_strbuf = std::cout.rdbuf(); @@ -548,20 +329,12 @@ namespace xcpp std::cout.rdbuf(&m_cout_buffer); std::cerr.rdbuf(&m_cerr_buffer); - - // Inject versions of printf and fprintf that output to std::cout - // and std::cerr (see implementation above). - inject_symbol("printf", llvm::pointerToJITTargetAddress(printf_jit), *m_interpreter); - inject_symbol("fprintf", llvm::pointerToJITTargetAddress(fprintf_jit), *m_interpreter); } void interpreter::restore_output() { std::cout.rdbuf(p_cout_strbuf); std::cerr.rdbuf(p_cerr_strbuf); - - // No need to remove the injected versions of [f]printf: As they forward - // to std::cout and std::cerr, these are handled implicitly. } void interpreter::publish_stdout(const std::string& s) @@ -576,38 +349,24 @@ namespace xcpp void interpreter::init_includes() { - AddIncludePath(*m_interpreter, xtl::prefix_path() + "/include/"); + Cpp::AddIncludePath((xtl::prefix_path() + "/include/").c_str()); } void interpreter::init_preamble() { - preamble_manager.register_preamble("introspection", new xintrospection(*m_interpreter)); + // FIXME: Make register_preamble take a unique_ptr. + //NOLINTBEGIN(cppcoreguidelines-owning-memory) + preamble_manager.register_preamble("introspection", new xintrospection()); preamble_manager.register_preamble("magics", new xmagics_manager()); preamble_manager.register_preamble("shell", new xsystem()); + //NOLINTEND(cppcoreguidelines-owning-memory) } void interpreter::init_magic() { - // preamble_manager["magics"].get_cast().register_magic("executable", - // executable(m_interpreter)); - // preamble_manager["magics"].get_cast().register_magic("file", writefile()); - // preamble_manager["magics"].get_cast().register_magic("timeit", - // timeit(&m_interpreter)); - } - - std::string interpreter::get_stdopt(int argc, const char* const* argv) - { - std::string res = "14"; - for (int i = 0; i < argc; ++i) - { - std::string tmp(argv[i]); - auto pos = tmp.find("-std=c++"); - if (pos != std::string::npos) - { - res = tmp.substr(pos + 8); - break; - } - } - return res; + // preamble_manager["magics"].get_cast().register_magic("executable", executable(m_interpreter)); + // preamble_manager["magics"].get_cast().register_magic("file", writefile()); + // preamble_manager["magics"].get_cast().register_magic("timeit", timeit(&m_interpreter)); + // preamble_manager["magics"].get_cast().register_magic("python", pythonexec()); } } diff --git a/test/test_interpreter.cpp b/test/test_interpreter.cpp index 506af5e1..3d1e1bd2 100644 --- a/test/test_interpreter.cpp +++ b/test/test_interpreter.cpp @@ -16,9 +16,26 @@ TEST_SUITE("execute_request") { + TEST_CASE("stl") + { + std::vector Args = {"stl-test-case", "-v"}; + xcpp::interpreter interpreter((int)Args.size(), Args.data()); + std::string code = "#include "; + nl::json user_expressions = nl::json::object(); + nl::json result = interpreter.execute_request( + code, + /*silent=*/false, + /*store_history=*/false, + user_expressions, + /*allow_stdin=*/false + ); + REQUIRE(result["status"] == "ok"); + } + TEST_CASE("fetch_documentation") { - xcpp::interpreter interpreter(0, nullptr); + std::vector Args = {/*"-v", "resource-dir", "....."*/}; + xcpp::interpreter interpreter((int)Args.size(), Args.data()); std::string code = "?std::vector"; std::string inspect_result = "https://en.cppreference.com/w/cpp/container/vector"; @@ -310,4 +327,4 @@ TEST_SUITE("xbuffer") REQUIRE(null_stream.good() == true); } -} \ No newline at end of file +}