diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..0dab1ed1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +build/**/* +build diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67c3f15a..d939f797 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,9 +15,9 @@ concurrency: jobs: unix: - + runs-on: ${{ matrix.os }} - + strategy: fail-fast: false matrix: @@ -74,14 +74,14 @@ jobs: shell: bash -l {0} run: | cd build/test + export CPLUS_INCLUDE_PATH=$CONDA_PREFIX/include:$CONDA_BUILD_SYSROOT:$CONDA_BUILD_SYSROOT/..:$CONDA_BUILD_SYSROOT/../x86_64-conda-linux-gnu/include/c++/12.3.0:$CONDA_BUILD_SYSROOT/../x86_64-conda-linux-gnu/include/c++/12.3.0/include:$CONDA_BUILD_SYSROOT/../include:$CONDA_BUILD_SYSROOT/usr/include:/home/runner/micromamba-root/envs/xeus-cpp/bin/../lib/gcc/x86_64-conda-linux-gnu/12.3.0/include:/home/runner/micromamba-root/envs/xeus-cpp/bin/../lib/gcc/x86_64-conda-linux-gnu/12.3.0/include-fixed:/home/runner/micromamba-root/envs/xeus-cpp/bin/../lib/gcc/x86_64-conda-linux-gnu/12.3.0/../../../../x86_64-conda-linux-gnu/include:/home/runner/micromamba-root/envs/xeus-cpp/bin/../x86_64-conda-linux-gnu/sysroot/usr/include ./test_xeus_cpp timeout-minutes: 4 - name: test shell: bash -l {0} run: | - cd test - pytest . --reruns 5 + $CONDA_PREFIX/bin/pytest test --reruns 5 - name: Prepare code coverage report if: ${{ success() && (matrix.coverage == true) }} @@ -109,10 +109,11 @@ jobs: uses: mxschmitt/action-tmate@v3 # When debugging increase to a suitable value! timeout-minutes: 30 + win: - + runs-on: ${{ matrix.os }} - + strategy: fail-fast: false matrix: @@ -177,7 +178,7 @@ jobs: emsdk activate ${{matrix.emsdk_ver}} source $CONDA_EMSDK_DIR/emsdk_env.sh micromamba create -f environment-wasm-host.yml --platform=emscripten-wasm32 - + mkdir build pushd build diff --git a/CMakeLists.txt b/CMakeLists.txt index 66833cb1..09a9def0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,10 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") set(XEUS_CPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +enable_language(CXX) +set(CMAKE_CXX_EXTENSIONS NO) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + # Versionning # =========== @@ -40,12 +44,52 @@ include(GNUInstallDirs) if (NOT DEFINED XEUS_CPP_KERNELSPEC_PATH) set(XEUS_CPP_KERNELSPEC_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/") + set(XEUS_CPP_RESOURCE_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/clang/17) endif () -configure_file ( - "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xcpp/kernel.json.in" - "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xcpp/kernel.json" -) +function(configure_kernel kernel) + set(CMAKE_CPLUS_INCLUDE_PATH "$ENV{CPLUS_INCLUDE_PATH}") + set(CMAKE_PATH "$ENV{PATH}") + set(CMAKE_LD_LIBRARY_PATH "$ENV{LD_LIBRARY_PATH}") + set(CMAKE_PYTHONPATH "$ENV{PYTHONPATH}") + set(CMAKE_VENV_PATH "$ENV{VENV}") + + message(STATUS "Debug: Replace in kernels") + message(STATUS "Debug: CMAKE_CPLUS_INCLUDE_PATH=${CMAKE_CPLUS_INCLUDE_PATH}") + message(STATUS "Debug: CMAKE_PATH=${CMAKE_PATH}") + message(STATUS "Debug: CMAKE_LD_LIBRARY_PATH=${CMAKE_LD_LIBRARY_PATH}") + message(STATUS "Debug: CMAKE_PYTHONPATH=${CMAKE_PYTHONPATH}") + message(STATUS "Debug: CMAKE_VENV_PATH=${CMAKE_VENV_PATH}") + message(STATUS "Debug: CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}") + message(STATUS "Debug: CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}") + + configure_file ( + "${CMAKE_CURRENT_SOURCE_DIR}/${kernel}/kernel.json.in" + "${CMAKE_CURRENT_BINARY_DIR}/${kernel}/kernel.json" + ) + file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/${kernel}/*.png") + foreach(file ${files}) + configure_file( + "${file}" + "${CMAKE_CURRENT_BINARY_DIR}/${kernel}/" + COPYONLY + ) + endforeach() + file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/${kernel}/*.svg") + foreach(file ${files}) + configure_file( + "${file}" + "${CMAKE_CURRENT_BINARY_DIR}/${kernel}/" + COPYONLY + ) + endforeach() +endfunction() + +file(GLOB _kernels LIST_DIRECTORIES true RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "share/jupyter/kernels/*") +foreach(_kernel IN LISTS _kernels) + message("Configure kernel: ${_kernel}") + configure_kernel("${_kernel}") +endforeach() # Build options # ============= @@ -54,8 +98,8 @@ option(XEUS_CPP_BUILD_STATIC "Build xeus-cpp static library" ON) OPTION(XEUS_CPP_BUILD_SHARED "Split xcpp build into executable and library" ON) OPTION(XEUS_CPP_BUILD_EXECUTABLE "Build the xcpp executable" ON) -OPTION(XEUS_CPP_USE_SHARED_XEUS "Link xcpp with the xeus shared library (instead of the static library)" ON) -OPTION(XEUS_CPP_USE_SHARED_XEUS_CPP "Link xcpp with the xeus shared library (instead of the static library)" ON) +OPTION(XEUS_CPP_USE_SHARED_XEUS "Link xcpp with the xeus shared library (instead of the static library)" ON) +OPTION(XEUS_CPP_USE_SHARED_XEUS_CPP "Link xcpp with the xeus shared library (instead of the static library)" ON) # Test options OPTION(XEUS_CPP_BUILD_TESTS "xeus-cpp test suite" ON) @@ -110,8 +154,11 @@ else() add_compile_options(-fexceptions) endif () -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Intel") - if(NOT EMSCRIPTEN) +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR + CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR + CMAKE_CXX_COMPILER_ID MATCHES "Intel") + + if(NOT XEUS_CPP_EMSCRIPTEN_WASM_BUILD) add_compile_options(-Wunused-parameter -Wextra -Wreorder) endif() endif () @@ -121,12 +168,15 @@ 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 CONFIG PATHS "${CMAKE_PREFIX_PATH}" "${CMAKE_PREFIX_PATH}/lib" "${CPPINTEROP_DIR}" "${CPPINTEROP_DIR}/lib") +if(CppInterOp_FOUND) + message(STATUS "Found CppInterOp: config=${CppInterOp_CONFIG} dir=${CppInterOp_DIR} (found version=${CppInterOp_VERSION})") +endif() + find_package(argparse REQUIRED) find_package(pugixml REQUIRED) +set(Python_FIND_VIRTUALENV ONLY) +find_package(Python COMPONENTS Interpreter Development) # Source files # ============ @@ -155,10 +205,18 @@ set(XEUS_CPP_SRC src/xoptions.cpp src/xparser.cpp src/xutils.cpp + src/xdemangle.hpp + src/xinspect.hpp + src/xsystem.hpp + src/xparser.hpp + src/xmagics/os.hpp + src/xmagics/os.cpp ) if(EMSCRIPTEN) list(APPEND XEUS_CPP_SRC src/xinterpreter_wasm.cpp) +else() + list(APPEND XEUS_CPP_SRC src/xmagics/pythonexec.hpp src/xmagics/pythonexec.cpp) endif() set(XEUS_CPP_MAIN_SRC @@ -172,7 +230,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) @@ -226,7 +285,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 @@ -262,7 +320,14 @@ 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 (EMSCRIPTEN) + # For some reason emscripten cannot find Python::Python. + target_link_libraries(${target_name} PUBLIC Development.Embed) + else() + target_link_libraries(${target_name} PUBLIC Python::Python) + endif() + if (WIN32 OR CYGWIN) # elseif (APPLE) @@ -272,7 +337,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 @@ -319,6 +384,30 @@ if (XEUS_CPP_BUILD_EXECUTABLE) xeus_cpp_set_common_options(xcpp) xeus_cpp_set_kernel_options(xcpp) target_link_libraries(xcpp PRIVATE xeus-zmq) + set_target_properties(xcpp PROPERTIES + ENABLE_EXPORTS 1 + CXX_STANDARD ${CMAKE_CXX_STANDARD}) + target_link_libraries(xcpp PUBLIC xeus-cpp pthread Python::Python) + + ##TODO: We may be need sse RPATH + set_target_properties(xcpp clangCppInterOp PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) + if(APPLE) + target_link_libraries(xcpp PUBLIC -Wl,-w -Wl,-bind_at_load -Wl,-undefined,dynamic_lookup) + elseif(NOT MSVC) + target_link_libraries(xcpp PUBLIC -Wl,--unresolved-symbols=ignore-in-object-files) + endif() + + target_include_directories(xeus-cpp PUBLIC ${Python_INCLUDE_DIRS}) + target_link_libraries(xeus-cpp PUBLIC ${PYTHON_LIBRARIES}) + target_link_libraries(xeus-cpp ${PYTHON_LIBRARIES_Development_Main}) + set_target_properties(xeus-cpp PROPERTIES + PUBLIC_HEADER "${XEUS_CPP_HEADERS}" + COMPILE_DEFINITIONS "XEUS_CPP_EXPORTS" + PREFIX "" + VERSION ${${PROJECT_NAME}_VERSION} + SOVERSION ${XEUS_CPP_VERSION_MAJOR} + OUTPUT_NAME "libxeus-cpp" + CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() if(EMSCRIPTEN) @@ -348,10 +437,10 @@ set(XEUS_CPP_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NA if (XEUS_CPP_BUILD_SHARED) install(TARGETS ${XEUS_CPP_TARGETS} EXPORT ${PROJECT_NAME}-targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xeus-cpp) + ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/xeus-cpp) # Makes the project importable from the build directory export(EXPORT ${PROJECT_NAME}-targets @@ -359,22 +448,23 @@ if (XEUS_CPP_BUILD_SHARED) endif () # Install xcpp +if ("${CMAKE_VENV_PATH}" STREQUAL "") + set(CMAKE_VENV_PATH "${CMAKE_INSTALL_PREFIX}") +endif() if (XEUS_CPP_BUILD_EXECUTABLE) install(TARGETS xcpp - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -endif() + RUNTIME DESTINATION ${CMAKE_VENV_PATH}/bin) +endif(XEUS_CPP_BUILD_EXECUTABLE) if(XEUS_CPP_BUILD_EXECUTABLE OR EMSCRIPTEN) # Configuration and data directories for jupyter and xeus-cpp 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) - # Extra path for installing Jupyter kernelspec if (XEXTRA_JUPYTER_DATA_DIR) install(DIRECTORY ${KERNELSPEC_DIR} @@ -439,3 +529,7 @@ if(EMSCRIPTEN) "$/xcpp.wasm" DESTINATION ${CMAKE_INSTALL_BINDIR}) endif () + +if(XEUS_CPP_INCLUDE_DOCS) + add_subdirectory(docs) +endif() diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..9fec1e7b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,280 @@ +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. + +# https://hub.docker.com/r/jupyter/base-notebook/tags +ARG BASE_CONTAINER=jupyter/base-notebook +ARG BASE_TAG=latest +ARG BUILD_TYPE=Debug + + +FROM $BASE_CONTAINER:$BASE_TAG + +LABEL maintainer="Xeus-cpp Project" + +SHELL ["/bin/bash", "--login", "-o", "pipefail", "-c"] + +USER root + +ENV TAG="$BASE_TAG" + +ENV LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + LANGUAGE=en_US.UTF-8 + +# Install all OS dependencies for notebook server that starts but lacks all +# features (e.g., download as all possible file formats) +RUN \ + set -x && \ + apt-get update --yes && \ + apt-get install --yes --no-install-recommends pciutils && \ + export _CUDA_="$(lspci -nn | grep '\[03' | grep NVIDIA)" && \ + apt-get install --yes --no-install-recommends \ + #fonts-liberation, pandoc, run-one are inherited from base-notebook container image + # Other "our" apt installs + unzip \ + curl \ + jq \ + ###libomp-dev \ + # Other "our" apt installs (development and testing) + build-essential \ + git \ + nano-tiny \ + less \ + gdb valgrind \ + emacs \ + # CUDA + #cuda \ + $([ -n "$_CUDA_" ] && echo nvidia-cuda-toolkit) \ + && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \ + locale-gen + +ENV LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + LANGUAGE=en_US.UTF-8 + +# Create alternative for nano -> nano-tiny +#RUN update-alternatives --install /usr/bin/nano nano /bin/nano-tiny 10 + +USER ${NB_UID} + +# Copy git repository to home directory of container +COPY --chown=${NB_UID}:${NB_GID} . "${HOME}"/ + +EXPOSE 9999 +ENV JUPYTER_PORT=9999 + +# Configure container startup +CMD ["start-notebook.sh", "--debug", "&>/home/jovyan/log.txt"] + +USER root + +# Fix start-notebook.sh +RUN sed -i '2 i source /home/jovyan/.conda.init && conda activate .venv' /usr/local/bin/start-notebook.sh + +### Make /home/runner directory and fix permisions +##RUN mkdir /home/runner && fix-permissions /home/runner + +# Switch back to jovyan to avoid accidental container runs as root +USER ${NB_UID} + +WORKDIR "${HOME}" + +ENV NB_PYTHON_PREFIX=${CONDA_DIR} \ + KERNEL_PYTHON_PREFIX=${CONDA_DIR} \ + VENV=${CONDA_DIR}/envs/.venv \ + # CUDA + NVIDIA_VISIBLE_DEVICES=all \ + NVIDIA_DRIVER_CAPABILITIES=compute,utility \ + NVIDIA_REQUIRE_CUDA="cuda>=12.1.1 driver>=530" \ + # + PATH=/opt/conda/envs/.venv/bin:/opt/conda/bin:/opt/conda/envs/.venv/bin:/opt/conda/condabin:/opt/conda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ + LD_LIBRARY_PATH=/home/jovyan/cppyy-backend/python/cppyy_backend/lib:/opt/conda/envs/.venv/lib:/opt/conda/lib:/home/jovyan/cppyy-backend/python/cppyy_backend/lib:/opt/conda/envs/.venv/lib \ + PYTHONPATH=/home/jovyan/CPyCppyy/build:/home/jovyan/cppyy-backend/python:/home/jovyan \ + CPLUS_INCLUDE_PATH=/opt/conda/envs/.venv/include:\ +/opt/conda/envs/.venv/include/python3.10:\ +/home/jovyan/clad/include:\ +/home/jovyan/CPyCppyy/include:\ +/home/jovyan/cppyy-backend/python/cppyy_backend/include:\ +/opt/conda/envs/.venv/include/llvm:\ +/opt/conda/envs/.venv/include/clang:\ +/opt/conda/include:\ +/home/jovyan/clad/include:\ +/home/jovyan/CppInterOp/include:\ +/opt/conda/include:\ +# +/opt/conda/envs/.venv/lib/gcc/x86_64-conda-linux-gnu/12.3.0/include:\ +/opt/conda/envs/.venv/lib/gcc/x86_64-conda-linux-gnu/12.3.0/include-fixed:\ +/opt/conda/envs/.venv/lib/gcc/x86_64-conda-linux-gnu/12.3.0/../../../../x86_64-conda-linux-gnu/include:\ +/opt/conda/envs/.venv/x86_64-conda-linux-gnu/include/c++/12.3.0:\ +/opt/conda/envs/.venv/x86_64-conda-linux-gnu/include/c++/12.3.0/x86_64-conda-linux-gnu:\ +/opt/conda/envs/.venv/x86_64-conda-linux-gnu/include/c++/12.3.0/backward:\ +/opt/conda/envs/.venv/x86_64-conda-linux-gnu/sysroot/usr/include:\ +# +/usr/include + +# VENV + +# Jupyter Notebook, Lab, and Hub are installed in base image +# ReGenerate a notebook server config +# Cleanup temporary files +# Correct permissions +# Do all this in a single RUN command to avoid duplicating all of the +# files across image layers when the permissions change +#RUN mamba update --all --quiet --yes -c conda-forge && \ +RUN \ + set -x && \ + # setup virtual environment + mamba create -y -n .venv python=3.10.6 && \ + # + #echo "echo \"@ @ @ PROFILE @ @ @ \"" >> ~/.profile && \ + #echo "echo \"@ @ @ BASHRC @ @ @ \"" >> /home/jovyan/.bashrc && \ + mv /home/jovyan/.bashrc /home/jovyan/.bashrc.tmp && \ + touch /home/jovyan/.bashrc && \ + conda init bash && \ + mv /home/jovyan/.bashrc /home/jovyan/.conda.init && \ + mv /home/jovyan/.bashrc.tmp /home/jovyan/.bashrc && \ + conda init bash && \ + echo "source /home/jovyan/.conda.init && conda activate .venv" >> /home/jovyan/.bashrc && \ + # + source /home/jovyan/.conda.init && \ + conda activate .venv && \ + fix-permissions "${CONDA_DIR}" && \ + # + mamba install --quiet --yes -c conda-forge \ + # notebook, jpyterhub, jupyterlab are inherited from base-notebook container image + # Other "our" conda installs + # + # Build dependencies + make \ + cmake \ + cxx-compiler \ + # Host dependencies + 'xeus-zmq>=1.0.2,<2.0' \ + nlohmann_json \ + cppzmq \ + xtl \ + 'clangdev>=17' \ + 'llvm-openmp' \ + pugixml \ + cpp-argparse \ + zlib \ + # + ipykernel \ + # Test dependencies + pytest \ + 'jupyter_kernel_test>=0.4.3' \ + nbval \ + pytest-rerunfailures \ + doctest \ + && \ + hash -r && \ + pip install ipython && \ + #rm /home/jovyan/.jupyter/jupyter_notebook_config.py && \ + jupyter notebook --generate-config -y && \ + mamba clean --all -f -y && \ + npm cache clean --force && \ + jupyter lab clean && \ + rm -rf "/home/${NB_USER}/.cache/yarn" && \ + fix-permissions "${CONDA_DIR}" && \ + fix-permissions "/home/${NB_USER}" + +### Post Build +RUN \ + set -x && \ + source /home/jovyan/.conda.init && \ + conda activate .venv && \ + source /home/jovyan/.conda.init && \ + conda activate .venv && \ + # + export PATH_TO_LLVM_BUILD=${VENV} && \ + ###export PATH=${VENV}/bin:${CONDA_DIR}/bin:$PATH_TO_LLVM_BUILD/bin:$PATH && \ + ##export PATH=${VENV}/bin:${CONDA_DIR}/bin:$PATH && \ + echo "export EDITOR=emacs" >> ~/.profile && \ + # + # Build CppInterOp + # + #sys_incs=$(LC_ALL=C c++ -xc++ -E -v /dev/null 2>&1 | LC_ALL=C sed -ne '/starts here/,/End of/p' | LC_ALL=C sed '/^ /!d' | cut -c2- | tr '\n' ':') && \ + git clone https://github.com/compiler-research/CppInterOp.git && \ + export CB_PYTHON_DIR="$PWD/cppyy-backend/python" && \ + export CPPINTEROP_DIR="$CB_PYTHON_DIR/cppyy_backend" && \ + cd CppInterOp && \ + mkdir build && \ + cd build && \ + export CPPINTEROP_BUILD_DIR=$PWD && \ + cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DUSE_CLING=OFF -DUSE_REPL=ON -DLLVM_DIR=$PATH_TO_LLVM_BUILD -DLLVM_CONFIG_EXTRA_PATH_HINTS=${PATH_TO_LLVM_BUILD}/lib -DLLVM_USE_LINKER=gold -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR .. && \ + cmake --build . --parallel $(nproc --all) && \ + #make install -j$(nproc --all) + cd ../.. && \ + # + # Build and Install cppyy-backend + # + git clone https://github.com/compiler-research/cppyy-backend.git && \ + cd cppyy-backend && \ + mkdir -p $CPPINTEROP_DIR/lib build && cd build && \ + # Install CppInterOp + (cd $CPPINTEROP_BUILD_DIR && cmake --build . --target install --parallel $(nproc --all)) && \ + # Build and Install cppyy-backend + cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCppInterOp_DIR=$CPPINTEROP_DIR .. && \ + cmake --build . --parallel $(nproc --all) && \ + cp libcppyy-backend.so $CPPINTEROP_DIR/lib/ && \ + cd ../.. && \ + # + # Build and Install CPyCppyy + # + # Install CPyCppyy + git clone https://github.com/compiler-research/CPyCppyy.git && \ + cd CPyCppyy && \ + mkdir build && cd build && \ + cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE .. && \ + cmake --build . --parallel $(nproc --all) && \ + export CPYCPPYY_DIR=$PWD && \ + cd ../.. && \ + # + # Build and Install cppyy + # + # Install cppyy + git clone https://github.com/compiler-research/cppyy.git && \ + cd cppyy && \ + python -m pip install --upgrade . --no-deps && \ + cd .. && \ + # Run cppyy + #TODO: Fix cppyy path (/home/jovyan) to path to installed module + ###python -c "import cppyy" && \ + # + # Build and Install xeus-cpp + # + mkdir build && \ + cd build && \ + cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH=$KERNEL_PYTHON_PREFIX -DCMAKE_INSTALL_PREFIX=$KERNEL_PYTHON_PREFIX -DCMAKE_INSTALL_LIBDIR=lib -DCPPINTEROP_DIR=$CPPINTEROP_BUILD_DIR .. && \ + make install -j$(nproc --all) && \ + cd .. && \ + # + # Build and Install Clad + # + git clone --depth=1 https://github.com/vgvassilev/clad.git && \ + cd clad && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE .. -DClang_DIR=${PATH_TO_LLVM_BUILD}/lib/cmake/clang/ -DLLVM_DIR=${PATH_TO_LLVM_BUILD}/lib/cmake/llvm/ -DCMAKE_INSTALL_PREFIX=${CONDA_DIR} -DLLVM_EXTERNAL_LIT="$(which lit)" && \ + #make -j$(nproc --all) && \ + make && \ + make install && \ + # + # xtensor + # + git clone https://github.com/xtensor-stack/xtensor.git && \ + cd xtensor && \ + mkdir build && cd build && \ + cmake -DCMAKE_INSTALL_PREFIX=$KERNEL_PYTHON_PREFIX .. && \ + make install && \ + # + # Fixes and patches + # + # Web password and token set to "" + echo "c.NotebookApp.token = ''" >> /home/jovyan/.jupyter/jupyter_notebook_config.py && \ + echo "c.NotebookApp.password = ''" >> /home/jovyan/.jupyter/jupyter_notebook_config.py && \ + # Patch /opt/conda/share/jupyter/kernels/python3/kernel.json to use .venv + k="/opt/conda/share/jupyter/kernels/python3/kernel.json" && \ + jq ".argv[0] = \"${VENV}/bin/python\"" $k > $k.$$.tmp && mv $k.$$.tmp $k diff --git a/bash-docker.sh b/bash-docker.sh new file mode 100755 index 00000000..52d413d2 --- /dev/null +++ b/bash-docker.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ "$#" -eq 0 ]; then + user="root" +else + user="$1" +fi + +docker exec -u "$user" -t -i xeus-cpp-c /bin/bash --login diff --git a/environment-dev.yml b/environment-dev.yml index 0c2df603..3c1622d6 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -11,10 +11,10 @@ dependencies: - nlohmann_json - cppzmq - xtl - - clangdev >=16,<17 - pugixml - - cpp-argparse + - cpp-argparse <3.0 - zlib + - CppInterOp # Test dependencies - pytest - jupyter_kernel_test>=0.4.3 diff --git a/environment-wasm-host.yml b/environment-wasm-host.yml index 4bb96d48..2434910c 100644 --- a/environment-wasm-host.yml +++ b/environment-wasm-host.yml @@ -7,6 +7,6 @@ dependencies: - xeus-lite - xeus >=3.0.5,<4.0 - xtl >=0.7,<0.8 - - llvm =16.0.6 - cpp-argparse - pugixml + - CppInterOp 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..c8cddca2 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -16,7 +16,8 @@ #include #include -#include +//#include +#include "clang/Interpreter/CppInterOp.h" // from CppInterOp package #include @@ -78,8 +79,6 @@ namespace xcpp 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/remove-docker.sh b/remove-docker.sh new file mode 100755 index 00000000..15a5b419 --- /dev/null +++ b/remove-docker.sh @@ -0,0 +1,3 @@ +#!/bin/bash +docker rm xeus-cpp-c -f +docker rmi xeus-cpp diff --git a/run-docker.sh b/run-docker.sh new file mode 100755 index 00000000..2d7f7506 --- /dev/null +++ b/run-docker.sh @@ -0,0 +1,15 @@ +#!/bin/bash +docker container run --rm -i hadolint/hadolint hadolint - < Dockerfile + +jupyter-repo2docker \ + --no-run \ + --user-name=jovyan \ + --image-name xeus-cpp \ + . + +#docker run --gpus all --publish 8888:8888 --name xeus-cpp-c -i -t xeus-cpp "start-notebook.sh" +docker run --rm --runtime=nvidia --gpus all --publish 9999:9999 --name xeus-cpp-c -i -t xeus-cpp "start-notebook.sh" + +# --editable \ +# --ref InterOpIntegration \ +# https://github.com/alexander-penev/xeus-cpp.git \ diff --git a/share/jupyter/kernels/clad-xcpp17/kernel.json.in b/share/jupyter/kernels/clad-xcpp17/kernel.json.in new file mode 100644 index 00000000..84b82da0 --- /dev/null +++ b/share/jupyter/kernels/clad-xcpp17/kernel.json.in @@ -0,0 +1,19 @@ +{ + "display_name": "C++17 (+Clad)", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-fplugin=/opt/conda/lib/clad.so", + "-std=c++17"@CMAKE_OMP@ + ], + "language": "C++17", + "metadata": {"debugger": false} +} diff --git a/share/jupyter/kernels/clad-xcpp17/logo-32x32.png b/share/jupyter/kernels/clad-xcpp17/logo-32x32.png new file mode 100644 index 00000000..c09c4585 Binary files /dev/null and b/share/jupyter/kernels/clad-xcpp17/logo-32x32.png differ diff --git a/share/jupyter/kernels/clad-xcpp17/logo-64x64.png b/share/jupyter/kernels/clad-xcpp17/logo-64x64.png new file mode 100644 index 00000000..396c2446 Binary files /dev/null and b/share/jupyter/kernels/clad-xcpp17/logo-64x64.png differ diff --git a/share/jupyter/kernels/clad-xcpp17/logo-svg.svg b/share/jupyter/kernels/clad-xcpp17/logo-svg.svg new file mode 100644 index 00000000..5e117077 --- /dev/null +++ b/share/jupyter/kernels/clad-xcpp17/logo-svg.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/share/jupyter/kernels/cppinterop-xcpp17/kernel.json.in b/share/jupyter/kernels/cppinterop-xcpp17/kernel.json.in new file mode 100644 index 00000000..45edb421 --- /dev/null +++ b/share/jupyter/kernels/cppinterop-xcpp17/kernel.json.in @@ -0,0 +1,19 @@ +{ + "display_name": "CppInterOp (C++17)", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-cuda", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-std=c++17"@CMAKE_OMP@ + ], + "language": "CUDA", + "metadata": {"debugger": false} +} diff --git a/share/jupyter/kernels/omp-xcpp17/kernel.json.in b/share/jupyter/kernels/omp-xcpp17/kernel.json.in new file mode 100644 index 00000000..fe906d03 --- /dev/null +++ b/share/jupyter/kernels/omp-xcpp17/kernel.json.in @@ -0,0 +1,19 @@ +{ + "display_name": "C++17 (+OpenMP)", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-fopenmp", + "-std=c++17" + ], + "language": "C++17", + "metadata": {"debugger": false} +} diff --git a/share/jupyter/kernels/omp-xcpp17/logo-32x32.png b/share/jupyter/kernels/omp-xcpp17/logo-32x32.png new file mode 100644 index 00000000..c09c4585 Binary files /dev/null and b/share/jupyter/kernels/omp-xcpp17/logo-32x32.png differ diff --git a/share/jupyter/kernels/omp-xcpp17/logo-64x64.png b/share/jupyter/kernels/omp-xcpp17/logo-64x64.png new file mode 100644 index 00000000..396c2446 Binary files /dev/null and b/share/jupyter/kernels/omp-xcpp17/logo-64x64.png differ diff --git a/share/jupyter/kernels/omp-xcpp17/logo-svg.svg b/share/jupyter/kernels/omp-xcpp17/logo-svg.svg new file mode 100644 index 00000000..5e117077 --- /dev/null +++ b/share/jupyter/kernels/omp-xcpp17/logo-svg.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/share/jupyter/kernels/xcpp/kernel.json.in b/share/jupyter/kernels/xcpp/kernel.json.in index f053f27c..ab65403a 100644 --- a/share/jupyter/kernels/xcpp/kernel.json.in +++ b/share/jupyter/kernels/xcpp/kernel.json.in @@ -1,11 +1,19 @@ { - "display_name": "cpp 14 (xcpp)", + "display_name": "C++17 (xcpp)", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, "argv": [ "@XEUS_CPP_KERNELSPEC_PATH@xcpp", "-f", - "{connection_file}" + "{connection_file}", + "-cuda", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-std=c++17"@CMAKE_OMP@ ], "language": "cpp", - "metadata": {"debugger": false - } + "metadata": {"debugger": false} } diff --git a/share/jupyter/kernels/xcpp11/kernel.json.in b/share/jupyter/kernels/xcpp11/kernel.json.in new file mode 100644 index 00000000..6cf70935 --- /dev/null +++ b/share/jupyter/kernels/xcpp11/kernel.json.in @@ -0,0 +1,18 @@ +{ + "display_name": "C++11", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-std=c++11"@CMAKE_OMP@ + ], + "language": "C++11", + "metadata": {"debugger": false} +} diff --git a/share/jupyter/kernels/xcpp11/logo-32x32.png b/share/jupyter/kernels/xcpp11/logo-32x32.png new file mode 100644 index 00000000..c09c4585 Binary files /dev/null and b/share/jupyter/kernels/xcpp11/logo-32x32.png differ diff --git a/share/jupyter/kernels/xcpp11/logo-64x64.png b/share/jupyter/kernels/xcpp11/logo-64x64.png new file mode 100644 index 00000000..396c2446 Binary files /dev/null and b/share/jupyter/kernels/xcpp11/logo-64x64.png differ diff --git a/share/jupyter/kernels/xcpp11/logo-svg.svg b/share/jupyter/kernels/xcpp11/logo-svg.svg new file mode 100644 index 00000000..5e117077 --- /dev/null +++ b/share/jupyter/kernels/xcpp11/logo-svg.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/share/jupyter/kernels/xcpp14/kernel.json.in b/share/jupyter/kernels/xcpp14/kernel.json.in new file mode 100644 index 00000000..a4dcf764 --- /dev/null +++ b/share/jupyter/kernels/xcpp14/kernel.json.in @@ -0,0 +1,21 @@ +{ + "display_name": "C++14", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-std=c++14", + "-fno-exceptions", + "-O2", + "-v"@CMAKE_OMP@ + ], + "language": "C++14", + "metadata": {"debugger": false} +} diff --git a/share/jupyter/kernels/xcpp14/logo-32x32.png b/share/jupyter/kernels/xcpp14/logo-32x32.png new file mode 100644 index 00000000..c09c4585 Binary files /dev/null and b/share/jupyter/kernels/xcpp14/logo-32x32.png differ diff --git a/share/jupyter/kernels/xcpp14/logo-64x64.png b/share/jupyter/kernels/xcpp14/logo-64x64.png new file mode 100644 index 00000000..396c2446 Binary files /dev/null and b/share/jupyter/kernels/xcpp14/logo-64x64.png differ diff --git a/share/jupyter/kernels/xcpp14/logo-svg.svg b/share/jupyter/kernels/xcpp14/logo-svg.svg new file mode 100644 index 00000000..5e117077 --- /dev/null +++ b/share/jupyter/kernels/xcpp14/logo-svg.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/share/jupyter/kernels/xcpp17/kernel.json.in b/share/jupyter/kernels/xcpp17/kernel.json.in new file mode 100644 index 00000000..c0852cfb --- /dev/null +++ b/share/jupyter/kernels/xcpp17/kernel.json.in @@ -0,0 +1,18 @@ +{ + "display_name": "C++17", + "env": { + "CPLUS_INCLUDE_PATH":"@CMAKE_CPLUS_INCLUDE_PATH@", + "PATH":"@CMAKE_PATH@", + "LD_LIBRARY_PATH":"@CMAKE_LD_LIBRARY_PATH@", + "PYTHONPATH":"@CMAKE_PYTHONPATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-std=c++17"@CMAKE_OMP@ + ], + "language": "C++17", + "metadata": {"debugger": false} +} diff --git a/share/jupyter/kernels/xcpp17/logo-32x32.png b/share/jupyter/kernels/xcpp17/logo-32x32.png new file mode 100644 index 00000000..c09c4585 Binary files /dev/null and b/share/jupyter/kernels/xcpp17/logo-32x32.png differ diff --git a/share/jupyter/kernels/xcpp17/logo-64x64.png b/share/jupyter/kernels/xcpp17/logo-64x64.png new file mode 100644 index 00000000..396c2446 Binary files /dev/null and b/share/jupyter/kernels/xcpp17/logo-64x64.png differ diff --git a/share/jupyter/kernels/xcpp17/logo-svg.svg b/share/jupyter/kernels/xcpp17/logo-svg.svg new file mode 100644 index 00000000..5e117077 --- /dev/null +++ b/share/jupyter/kernels/xcpp17/logo-svg.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main.cpp b/src/main.cpp index 83f6f73f..31a18aba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,73 @@ #include "xeus-cpp/xinterpreter.hpp" #include "xeus-cpp/xutils.hpp" +#ifdef __GNUC__ +void handler(int sig) +{ + void* array[10]; + + // get void*'s for all entries on the stack + std::size_t size = backtrace(array, 10); + + // print out all the frames to stderr + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); + exit(1); +} +#endif + +void stop_handler(int /*sig*/) +{ + exit(0); +} + +bool should_print_version(int argc, char* argv[]) +{ + for (int i = 0; i < argc; ++i) + { + if (std::string(argv[i]) == "--version") + { + return true; + } + } + return false; +} + +std::string extract_filename(int& argc, char* argv[]) +{ + std::string res = ""; + for (int i = 0; i < argc; ++i) + { + if ((std::string(argv[i]) == "-f") && (i + 1 < argc)) + { + res = argv[i + 1]; + for (int j = i; j < argc - 2; ++j) + { + argv[j] = argv[j + 2]; + } + argc -= 2; + break; + } + } + return res; +} + +using interpreter_ptr = std::unique_ptr; + +interpreter_ptr build_interpreter(int argc, char** argv) +{ + std::vector interpreter_args; + for (int i = 1; i < argc; i++) { + if (argv[i] == "-f") { + i++; // skip the value of -f which is a json file. + continue; + } + interpreter_args.push_back(argv[i]); + } + interpreter_ptr interp_ptr = interpreter_ptr(new xcpp::interpreter(interpreter_args.size(), interpreter_args.data())); + return interp_ptr; +} + int main(int argc, char* argv[]) { if (xcpp::should_print_version(argc, argv)) @@ -97,8 +164,8 @@ int main(int argc, char* argv[]) xeus::make_xserver_zmq, xeus::make_in_memory_history_manager(), xeus::make_console_logger( - xeus::xlogger::msg_type, - xeus::make_file_logger(xeus::xlogger::content, "xeus.log") + xeus::xlogger::msg_type, + xeus::make_file_logger(xeus::xlogger::content, "xeus.log") ) ); diff --git a/src/xinspect.hpp b/src/xinspect.hpp index 15b8d523..b7c22c8c 100644 --- a/src/xinspect.hpp +++ b/src/xinspect.hpp @@ -9,10 +9,10 @@ #ifndef XEUS_CPP_INSPECT_HPP #define XEUS_CPP_INSPECT_HPP +#include #include #include - #include #include @@ -23,8 +23,11 @@ #include "xdemangle.hpp" #include "xparser.hpp" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" +//#include "llvm/Support/FileSystem.h" +//#include "llvm/Support/Path.h" + +//#include "clang/Interpreter/CppInterOp.h" + namespace xcpp { @@ -81,7 +84,27 @@ namespace xcpp } }; - std::string find_type(const std::string& expression, clang::Interpreter& interpreter) + + 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); + + // Here we might need to deal with integral types such as 3.14. + + std::string id = "__Xeus_GetType_" + std::to_string(var_count++); + std::string using_clause = "using " + id + " = __typeof__(" + expression + ");\n"; + + if (!Cpp::Declare(using_clause.c_str(), /*silent=*/false)) { + Cpp::TCppScope_t lookup = Cpp::GetNamed(id, 0); + Cpp::TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup); + return Cpp::GetQualifiedCompleteName(Cpp::GetCanonicalType(lookup_ty)); + } + return ""; + } +/* + std::string find_type(const std::string& expression) { auto PTU = interpreter.Parse(expression + ";"); if (llvm::Error Err = PTU.takeError()) { @@ -89,19 +112,29 @@ namespace xcpp return ""; } - clang::Decl *D = *PTU->TUPart->decls_begin(); - if (!llvm::isa(D)) - return ""; + clang::Decl *D = *PTU->TUPart->decls_begin(); + if (!llvm::isa(D)) + return ""; - clang::Expr *E = llvm::cast(llvm::cast(D)->getStmt()); + clang::Expr *E = llvm::cast(llvm::cast(D)->getStmt()); - clang::QualType QT = E->getType(); + clang::QualType QT = E->getType(); return QT.getAsString(); } - +*/ static nl::json read_tagconfs(const char* path) { nl::json result = nl::json::array(); + for (auto &entry: std::filesystem::directory_iterator(path)) { + if (entry.path().extension() != ".json") + continue; + std::ifstream i(entry.path()); + nl::json json_entry; + i >> json_entry; + result.emplace_back(std::move(json_entry)); + } + return result; +/* std::error_code EC; for (llvm::sys::fs::directory_iterator File(path, EC), FileEnd; File != FileEnd && !EC; File.increment(EC)) { @@ -115,19 +148,19 @@ namespace xcpp result.emplace_back(std::move(entry)); } return result; +*/ } - std::pair is_inspect_request(const std::string code, std::regex re) + std::pair is_inspect_request(const std::string code, std::regex re) { std::smatch inspect; 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; @@ -150,7 +183,7 @@ 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 typename_ = find_type_slow(method[1]); if (!typename_.empty()) { @@ -160,7 +193,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()); class_member_predicate predicate{typename_, "function", method[2]}; auto node = doc.find_node(predicate); if (!node.empty()) @@ -184,7 +217,7 @@ namespace xcpp } else { - std::string typename_ = find_type(to_inspect, interpreter); + std::string typename_ = find_type_slow(to_inspect); find_string = (typename_.empty()) ? to_inspect : typename_; } @@ -194,7 +227,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}; @@ -272,8 +305,7 @@ namespace xcpp using xpreamble::pattern; const std::string spattern = R"(^\?)"; - xintrospection(clang::Interpreter& p) - : m_interpreter{p} + xintrospection() { pattern = spattern; } @@ -283,17 +315,14 @@ 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 diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 09fb27ce..257016b8 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -7,241 +7,42 @@ * 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/executable.hpp" +// #include "xmagics/execution.hpp" +#include "xmagics/os.hpp" +#include "xmagics/pythonexec.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++"*/}; // ? {"-Xclang", "-emit-llvm-only", "-Xclang", "-diagnostic-log-file", "-Xclang", "-", "-xc++"}; + 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,19 +51,11 @@ 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); } 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)) + : m_version(get_stdopt(argc, argv)) , // Extract C++ language standard version from command-line option xmagics() , p_cout_strbuf(nullptr) @@ -270,7 +63,9 @@ namespace xcpp , m_cout_buffer(std::bind(&interpreter::publish_stdout, this, _1)) , m_cerr_buffer(std::bind(&interpreter::publish_stderr, this, _1)) { + createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); redirect_output(); + // Bootstrap the execution engine init_includes(); init_preamble(); init_magic(); @@ -322,13 +117,19 @@ namespace xcpp // 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); + + std::string err; + std::string out; + // Attempt normal evaluation - try { - compilation_result = process_code(*m_interpreter, code, error_stream); + Cpp::BeginStdStreamCapture(Cpp::kStdErr); + Cpp::BeginStdStreamCapture(Cpp::kStdOut); + compilation_result = Cpp::Process(code.c_str()); + out = Cpp::EndStdStreamCapture(); + err = Cpp::EndStdStreamCapture(); + std::cout << out; } catch (std::exception& e) { @@ -341,17 +142,14 @@ namespace xcpp errorlevel = 1; ename = "Error :"; } - + if (compilation_result) { errorlevel = 1; - ename = "Error :"; - evalue = error_stream.str(); + ename = "Error"; + std::cerr << err; } - error_stream.str().clear(); - DiagnosticsOS.str().clear(); - // Flush streams std::cout << std::flush; std::cerr << std::flush; @@ -423,7 +221,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; } @@ -498,49 +296,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 +303,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,23 +323,21 @@ namespace xcpp void interpreter::init_includes() { - AddIncludePath(*m_interpreter, xtl::prefix_path() + "/include/"); } void interpreter::init_preamble() { - preamble_manager.register_preamble("introspection", new xintrospection(*m_interpreter)); + preamble_manager.register_preamble("introspection", new xintrospection()); preamble_manager.register_preamble("magics", new xmagics_manager()); preamble_manager.register_preamble("shell", new xsystem()); } 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)); + // 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()); } std::string interpreter::get_stdopt(int argc, const char* const* argv) diff --git a/src/xmagics/pythonexec.cpp b/src/xmagics/pythonexec.cpp new file mode 100644 index 00000000..03d5a488 --- /dev/null +++ b/src/xmagics/pythonexec.cpp @@ -0,0 +1,208 @@ +//===------------ pythonexec.hpp - Python/C++ Interoperability ------------===// +// +// Licensed under the Apache License v2.0. +// SPDX-License-Identifier: Apache-2.0 +// +// The full license is in the file LICENSE, distributed with this software. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Python/C++ interoperability in which two cells within +// the same notebook can be in a either language. +// +//===----------------------------------------------------------------------===// + +#include "Python.h" + +#include "pythonexec.hpp" +#include "../xparser.hpp" + +#include +#include +#include +#include +#include +#include + +namespace xcpp { +static PyObject *gMainDict = 0; + +void pythonexec::startup() { + static bool isInitialized = false; + if (isInitialized) + return; + + Py_Initialize(); + + PyRun_SimpleString("import sys\nprint(sys.path)"); + + // Import cppyy module + PyObject* cppyyModule = PyImport_ImportModule("cppyy"); + if (!cppyyModule) { + PyErr_Print(); + Py_Finalize(); + return; // Handle import error as needed + } + + PyObject* mainModule = PyImport_AddModule("__main__"); + PyObject_SetAttrString(mainModule, "cppyy", cppyyModule); + Py_XDECREF(cppyyModule); + isInitialized = true; + // gMainDict = PyModule_GetDict(mainModule); + // Py_INCREF(gMainDict); + // if (!gMainDict) + // printf("Could not add module __main__"); + + // // Retrieve the dictionary of cppyy module + // PyObject* cppyyDict = PyModule_GetDict(cppyyModule); + // Py_DECREF(cppyyModule); + // if (!cppyyDict) { + // PyErr_Print(); + // Py_Finalize(); + // return; // Handle retrieval error as needed + // } + + // // Add cppyyDict to gMainDict (if needed for further usage) + // PyDict_Update(gMainDict, cppyyDict); + + // Py_DECREF(cppyyDict); + // PyRun_SimpleString("import cppyy"); +} + +void pythonexec::get_options(argparser &argpars) { +} + +void pythonexec::execute(std::string &line, std::string &cell) { + // std::istringstream iss(line); + // std::vector results((std::istream_iterator(iss)), + // std::istream_iterator()); + startup(); + +// argparser argpars{"python", "Start executing Python Cell"}; +// auto argpars = get_options(); +// argpars.parse(line); + + std::string code; + + code += cell; + if (trim(code).empty()) + return; + // PyRun_SimpleString( + // "globals_copy_lists = " + // "globals().copy()\nfirst_dict_ints={k:globals_copy_lists[k] for k in " + // "set(globals_copy_lists) if type(globals_copy_lists[k]) == " + // "int}\nfirst_dict_lists={k:globals_copy_lists[k] for k in " + // "set(globals_copy_lists) if type(globals_copy_lists[k]) == list}"); + + // PyRun_SimpleString("tmp = globals().copy()\nvars = [f'int {k} = {v};' for + // k,v in tmp.items() if type(v) == int and not k.startswith('_') and k!='tmp' + // and k!='In' and k!='Out' and k!='sys' and not hasattr(v, + // '__call__')]\nprint(vars)"); PyRun_SimpleString("b = + // globals().copy()\nnew_ints = ' '.join([f'int {k} = {b[k]};' for k in set(b) + // - set(first_dict) if type(b[k]) == int])\nprint('new_ints: ', new_ints)"); + + Cpp::BeginStdStreamCapture(Cpp::kStdErr); + Cpp::BeginStdStreamCapture(Cpp::kStdOut); + + PyRun_SimpleString(code.c_str()); + + std::cout << Cpp::EndStdStreamCapture(); + std::cerr << Cpp::EndStdStreamCapture(); + + // PyObject* objectsRepresentation = PyObject_Repr(gMainDict); + // const char* s = PyUnicode_AsUTF8(objectsRepresentation); + // printf("REPR of global dict: %s\n", s); +} + +void pythonexec::update_python_dict_var(const char *name, int value) { + if (!gMainDict) + startup(); + PyObject *s; + s = PyLong_FromLong(value); + PyDict_SetItemString(gMainDict, name, s); + Py_DECREF(s); +} + +void pythonexec::update_python_dict_var_vector(const char *name, + std::vector &data) { + if (!gMainDict) + startup(); + PyObject *listObj = PyList_New(data.size()); + if (!listObj) + throw std::logic_error("Unable to allocate memory for Python list"); + for (unsigned int i = 0; i < data.size(); i++) { + PyObject *num = PyLong_FromLong((int)data[i]); + if (!num) { + Py_DECREF(listObj); + throw std::logic_error("Unable to allocate memory for Python list"); + } + PyList_SET_ITEM(listObj, i, num); + } + PyDict_SetItemString(gMainDict, name, listObj); +} + +// check python globals +void pythonexec::check_python_globals() { + if (!Py_IsInitialized()) + return; + // execute the command + PyRun_SimpleString("print(globals())"); +} + +// execute a python comand +void pythonexec::exec_python_simple_command(const std::string code) { + if (!Py_IsInitialized()) + return; + // execute the command + PyRun_SimpleString(code.c_str()); +} + +std::string pythonexec::transfer_python_ints_utility() { + if (!Py_IsInitialized()) + return " "; + // transfer ints utility + PyRun_SimpleString( + "def getNewInts():\n glob_ints_utils = globals().copy()\n new_ints " + "= ' '.join([f'int {k} = {glob_ints_utils[k]};' for k in " + "set(glob_ints_utils) - set(first_dict_ints) if type(glob_ints_utils[k]) " + "== int])\n return new_ints"); + PyObject *ints_result = + PyObject_CallFunction(PyDict_GetItemString(gMainDict, "getNewInts"), 0); + if (!ints_result) { + printf("Could not retrieve Python integers!\n"); + return " "; + } else { + std::string newPythonInts = PyUnicode_AsUTF8(ints_result); + // printf("new ints %s\n", PyUnicode_AsUTF8(ints_result)); + Py_DECREF(ints_result); + return newPythonInts; + } +} + +std::string pythonexec::transfer_python_lists_utility() { + if (!Py_IsInitialized()) + return " "; + // transfer lists utility + PyRun_SimpleString("def getNewLists():\n l = globals().copy()\n " + "new_lists = ' '.join([f'int {k}() = {l[k]};' for k in " + "set(l) - set(first_dict_lists) if type(l[k]) == " + "list]).replace('[','{').replace(']','}').replace('(','[')" + ".replace(')',']')\n return new_lists"); + PyObject *lists_result = + PyObject_CallFunction(PyDict_GetItemString(gMainDict, "getNewLists"), 0); + if (!lists_result) { + printf("Could not retrieve Python lists!\n"); + return " "; + } else { + std::string newPythonLists = PyUnicode_AsUTF8(lists_result); + Py_DECREF(lists_result); + return newPythonLists; + } +} + +bool pythonexec::python_check_for_initialisation() { + if (!Py_IsInitialized()) + return false; + return true; +} +} // namespace xcpp diff --git a/src/xmagics/pythonexec.hpp b/src/xmagics/pythonexec.hpp new file mode 100644 index 00000000..bd8a69ce --- /dev/null +++ b/src/xmagics/pythonexec.hpp @@ -0,0 +1,55 @@ +//===------- pythonexec.hpp - Python/C++ Interoperability -------*- C++ -*-===// +// +// Licensed under the Apache License v2.0. +// SPDX-License-Identifier: Apache-2.0 +// +// The full license is in the file LICENSE, distributed with this software. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Python/C++ interoperability in which two cells within +// the same notebook can be in a either language. +// +//===----------------------------------------------------------------------===// + +#ifndef XMAGICS_PYTHONEXEC_HPP +#define XMAGICS_PYTHONEXEC_HPP + +#include "xeus-cpp/xbuffer.hpp" +#include "xeus-cpp/xinterpreter.hpp" +#include "xeus-cpp/xmagics.hpp" +#include "xeus-cpp/xoptions.hpp" + +#include +#include + +namespace xcpp { + + class pythonexec: public xmagic_cell + { + public: + + void get_options(argparser &argpars); + virtual void operator()(const std::string& line, const std::string& cell) override { + std::string cline = line; + std::string ccell = cell; + execute(cline, ccell); + }; + + static void update_python_dict_var(const char *name, int value); + static void update_python_dict_var_vector(const char *name, + std::vector &data); + void check_python_globals(); + void exec_python_simple_command(const std::string code); + static std::string transfer_python_ints_utility(); + static std::string transfer_python_lists_utility(); + static bool python_check_for_initialisation(); + + private: + + static void startup(); + void execute(std::string &line, std::string &cell); + }; + +} // namespace xcpp +#endif diff --git a/src/xsystem.hpp b/src/xsystem.hpp index 99d67b1e..c43abfc4 100644 --- a/src/xsystem.hpp +++ b/src/xsystem.hpp @@ -32,6 +32,8 @@ namespace xcpp std::smatch to_execute; std::regex_search(code, to_execute, re); + int ret = 1; // ??? + // Redirection of stderr to stdout std::string command = to_execute.str(1) + " 2>&1"; @@ -43,6 +45,9 @@ namespace xcpp if (shell_result) { char buff[512]; + + ret = 0; // ??? + while (fgets(buff, sizeof(buff), shell_result)) { std::cout << buff; diff --git a/test/pytest.ini b/test/pytest.ini index 5aacf83b..e580d2bc 100644 --- a/test/pytest.ini +++ b/test/pytest.ini @@ -1,3 +1,4 @@ [pytest] testpaths = test -addopts = --nbval --current-env +#addopts = --nbval --current-env +addopts = --nbval --nbval-current-env diff --git a/test/test_interpreter.cpp b/test/test_interpreter.cpp index 392b58d9..e422b3ae 100644 --- a/test/test_interpreter.cpp +++ b/test/test_interpreter.cpp @@ -14,11 +14,13 @@ TEST_SUITE("execute_request") { TEST_CASE("fetch_documentation") { - - xcpp::interpreter interpreter(0, nullptr); + std::vector Args = {/*"-v", "resource-dir", "....."*/}; + xcpp::interpreter interpreter(Args.size(), Args.data()); std::string code = "?std::vector"; std::string inspect_result = "https://en.cppreference.com/w/cpp/container/vector"; + //std::string code = "#include \\n#include \"xcpp/xdisplay.hpp\"\\nstd::string test(\"foobar\");\\nxcpp::display(test);"; + //std::string inspect_result = "foobar"; nl::json user_expressions = nl::json::object(); nl::json result = interpreter.execute_request(