├── VERSION ├── patches └── llvm │ ├── README.md │ ├── emscripten-clang19-3-remove-zdefs.patch │ ├── emscripten-clang19-2-shift-temporary-files-to-tmp-dir.patch │ ├── emscripten-clang20-2-shift-temporary-files-to-tmp-dir.patch │ ├── cling1.2-LookupHelper.patch │ ├── emscripten-clang20-3-enable_exception_handling.patch │ └── emscripten-clang19-4-enable_exception_handling.patch ├── lib ├── CMakeLists.txt └── CppInterOp │ ├── exports.ld │ ├── Paths.h │ ├── CMakeLists.txt │ └── DynamicLibraryManager.h ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 02-feature-request.yml │ ├── 03-documentation.yml │ └── 01-bug-report.yml ├── pull_request_template.md ├── workflows │ ├── clang-tidy-review-post.yml │ ├── markdown-linter.yml │ ├── clang-tidy-review.yml │ ├── clang-format.yml │ ├── deploy-pages.yml │ └── main.yml └── actions │ ├── Miscellaneous │ ├── Select_Default_Build_Type │ │ └── action.yml │ ├── Install_Dependencies │ │ └── action.yml │ ├── Save_PR_Info │ │ └── action.yml │ └── Setup_Compiler │ │ └── action.yml │ ├── Build_and_Test_cppyy │ └── action.yml │ ├── Build_and_Test_CppInterOp │ └── action.yml │ └── Build_LLVM │ └── action.yml ├── .markdownlint.json ├── unittests ├── CppInterOp │ ├── TestSharedLib │ │ ├── TestSharedLib.cpp │ │ ├── TestSharedLib.h │ │ └── CMakeLists.txt │ ├── main.cpp │ ├── CUDATest.cpp │ ├── Utils.cpp │ ├── Utils.h │ ├── DynamicLibraryManagerTest.cpp │ ├── JitTest.cpp │ ├── CMakeLists.txt │ └── EnumReflectionTest.cpp ├── .clang-tidy └── CMakeLists.txt ├── scripts └── browser_tests_safari.py ├── .readthedocs.yaml ├── environment-wasm.yml ├── docs ├── doxygen.footer ├── doxygen.header ├── FAQ.rst ├── index.rst ├── CMakeLists.txt ├── doxygen-mainpage.dox ├── conf.py ├── ReleaseNotes.md ├── reference.rst ├── DebuggingCppInterOp.rst ├── manpage.css ├── doxygen.css └── tutorials.rst ├── .clang-format ├── .gitignore ├── .codecov.yml ├── include ├── clang │ └── Interpreter │ │ └── CppInterOp.h └── clang-c │ └── CXCppInterOp.h ├── cmake ├── CppInterOp │ ├── CppInterOpConfigVersion.cmake.in │ └── CppInterOpConfig.cmake.in ├── FindSphinx.cmake ├── CreateSphinxTarget.cmake └── modules │ └── GoogleTest.cmake ├── discord.svg ├── .clang-tidy ├── etc └── clang20-valgrind.supp └── CONTRIBUTING.md /VERSION: -------------------------------------------------------------------------------- 1 | 1.8.0;dev 2 | -------------------------------------------------------------------------------- /patches/llvm/README.md: -------------------------------------------------------------------------------- 1 | LLVM/Clang patches 2 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(CppInterOp) 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "no-duplicate-heading": false 3 | } 4 | -------------------------------------------------------------------------------- /unittests/CppInterOp/TestSharedLib/TestSharedLib.cpp: -------------------------------------------------------------------------------- 1 | #include "TestSharedLib.h" 2 | 3 | int ret_zero() { return 0; } 4 | -------------------------------------------------------------------------------- /unittests/CppInterOp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | ::testing::InitGoogleTest(&argc, argv); 5 | return RUN_ALL_TESTS(); 6 | } -------------------------------------------------------------------------------- /scripts/browser_tests_safari.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | from selenium import webdriver 4 | 5 | driver = webdriver.Safari() 6 | driver.get("http://localhost:6931/" + sys.argv[1]) 7 | 8 | time.sleep(60) 9 | driver.quit() 10 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | configuration: docs/conf.py 5 | builder: html 6 | 7 | build: 8 | os: "ubuntu-24.04" 9 | tools: 10 | python: "3.11" 11 | apt_packages: 12 | - clang-18 13 | - cmake 14 | - libclang-18-dev 15 | - llvm-18-dev 16 | - llvm-18-tools 17 | -------------------------------------------------------------------------------- /environment-wasm.yml: -------------------------------------------------------------------------------- 1 | name: CppInterOp-wasm 2 | channels: 3 | - https://prefix.dev/emscripten-forge-4x 4 | - https://prefix.dev/conda-forge 5 | dependencies: 6 | - emscripten-abi==4.0.9 7 | - nlohmann_json 8 | - nlohmann_json-abi 9 | - xeus-lite 10 | - xeus 11 | - cpp-argparse 12 | - pugixml 13 | - doctest 14 | -------------------------------------------------------------------------------- /docs/doxygen.footer: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | Language: Cpp 4 | Standard: c++17 5 | PointerAlignment: Left 6 | 7 | IncludeCategories: 8 | - Regex: '^"[^/]+\"' 9 | Priority: 10 10 | - Regex: '^"cling/' 11 | Priority: 20 12 | - Regex: '^"clang/' 13 | Priority: 30 14 | - Regex: '^"llvm/' 15 | Priority: 40 16 | - Regex: '^<' 17 | Priority: 50 18 | -------------------------------------------------------------------------------- /unittests/.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -misc-use-internal-linkage, 3 | -modernize-pass-by-value, 4 | -cppcoreguidelines-macro-usage, 5 | -cppcoreguidelines-pro-type-cstyle-cast, 6 | -cppcoreguidelines-avoid-non-const-global-variables, 7 | -cppcoreguidelines-avoid-c-arrays, 8 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 9 | -performance-unnecessary-value-param, 10 | -performance-no-int-to-ptr, 11 | -bugprone-multi-level-implicit-pointer-conversion, 12 | -------------------------------------------------------------------------------- /unittests/CppInterOp/TestSharedLib/TestSharedLib.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTESTS_CPPINTEROP_TESTSHAREDLIB_TESTSHAREDLIB_H 2 | #define UNITTESTS_CPPINTEROP_TESTSHAREDLIB_TESTSHAREDLIB_H 3 | 4 | // Avoid having to mangle/demangle the symbol name in tests 5 | #ifdef _WIN32 6 | extern "C" __declspec(dllexport) int ret_zero(); 7 | #else 8 | extern "C" int __attribute__((visibility("default"))) ret_zero(); 9 | #endif 10 | 11 | #endif // UNITTESTS_CPPINTEROP_TESTSHAREDLIB_TESTSHAREDLIB_H 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of changes, motivation and context for this PR. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please tick all options which are relevant. 10 | 11 | - [ ] Bug fix 12 | - [ ] New feature 13 | - [ ] Requires documentation updates 14 | 15 | ## Testing 16 | 17 | Please describe the test(s) that you added and ran to verify your changes. 18 | 19 | ## Checklist 20 | 21 | - [ ] I have read the contribution guide recently 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Directories 31 | build 32 | install 33 | 34 | # CLion files 35 | .idea 36 | 37 | # VSCode files 38 | .vscode 39 | 40 | # Default Virtual Environments 41 | .venv 42 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: no 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "75...100" 8 | 9 | status: 10 | project: yes 11 | patch: yes 12 | changes: no 13 | 14 | parsers: 15 | gcov: 16 | branch_detection: 17 | conditional: yes 18 | loop: yes 19 | method: no 20 | macro: no 21 | 22 | comment: 23 | layout: "reach, diff, flags, tree, files" 24 | behavior: default 25 | require_changes: no 26 | 27 | github_checks: 28 | annotations: true # Deprecated but very useful 29 | -------------------------------------------------------------------------------- /docs/doxygen.header: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CppInterOp: $title 7 | 8 | 9 |

CppInterOp API Documentation

10 | -------------------------------------------------------------------------------- /include/clang/Interpreter/CppInterOp.h: -------------------------------------------------------------------------------- 1 | #ifndef CLANG_CPPINTEROP_H 2 | #define CLANG_CPPINTEROP_H 3 | 4 | #include "CppInterOp/CppInterOp.h" 5 | #if defined(_MSC_VER) 6 | #pragma message( \ 7 | "#include is deprecated; use #include ") 8 | #else 9 | #warning \ 10 | "#include is deprecated; use #include " 11 | #endif 12 | 13 | #endif // CLANG_CPPINTEROP_H 14 | -------------------------------------------------------------------------------- /cmake/CppInterOp/CppInterOpConfigVersion.cmake.in: -------------------------------------------------------------------------------- 1 | set(PACKAGE_VERSION "@CPPINTEROP_VERSION@") 2 | 3 | if("${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}" VERSION_LESS "@CPPINTEROP_VERSION_MAJOR@.@CPPINTEROP_VERSION_MINOR@" 4 | AND NOT "@CPPINTEROP_VERSION_PATCH@" VERSION_LESS "${PACKAGE_FIND_VERSION_PATCH}") 5 | set(PACKAGE_VERSION_COMPATIBLE 1) 6 | endif() 7 | if("@CPPINTEROP_VERSION_MAJOR@.@CPPINTEROP_VERSION_MINOR@.@CPPINTEROP_VERSION_PATCH@" VERSION_EQUAL "${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}.${PACKAGE_FIND_VERSION_PATCH}") 8 | set(PACKAGE_VERSION_EXACT 1) 9 | endif() 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request Form 2 | description: Form for requesting a new feature in CppInterOp. 3 | title: "[Feature Request]: " 4 | labels: ["enhancement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "Thanks for taking the time to fill out this feature request form!" 9 | - type: textarea 10 | attributes: 11 | label: "Description of new feature" 12 | description: Tell us what the feature is that you would like. 13 | value: "Replace with description of the feature you would like." 14 | validations: 15 | required: true 16 | -------------------------------------------------------------------------------- /unittests/CppInterOp/TestSharedLib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_llvm_library(TestSharedLib 2 | SHARED 3 | DISABLE_LLVM_LINK_LLVM_DYLIB 4 | BUILDTREE_ONLY 5 | TestSharedLib.cpp) 6 | # Put TestSharedLib next to the unit test executable. 7 | set_output_directory(TestSharedLib 8 | BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/unittests/bin/$/ 9 | LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/unittests/bin/$/ 10 | ) 11 | 12 | 13 | if (EMSCRIPTEN) 14 | set_target_properties(TestSharedLib 15 | PROPERTIES NO_SONAME 1 16 | ) 17 | target_link_options(TestSharedLib 18 | PRIVATE "SHELL: -s WASM_BIGINT" 19 | PRIVATE "SHELL: -s SIDE_MODULE=1" 20 | ) 21 | endif() 22 | 23 | set_target_properties(TestSharedLib PROPERTIES FOLDER "Tests") 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/03-documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Improvement Form 2 | description: Form for requesting a new feature in CppInterOp. 3 | title: "[Documentation Improvement]: " 4 | labels: ["documentation"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "Thanks for taking the time to fill out this documenation improvement form!" 9 | - type: textarea 10 | attributes: 11 | label: "Description of issue with documention/Way documentation can be improved" 12 | description: Tell use what the issue with the documentation is, or how it could be improved. 13 | value: "Replace text with description of issue with documentation, or improvement that can be made." 14 | validations: 15 | required: true 16 | -------------------------------------------------------------------------------- /.github/workflows/clang-tidy-review-post.yml: -------------------------------------------------------------------------------- 1 | name: Post clang-tidy review comments 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["clang-tidy-review"] 6 | types: 7 | - completed 8 | 9 | permissions: 10 | checks: write 11 | pull-requests: write 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-22.04 20 | 21 | steps: 22 | - name: Post review comments 23 | id: post-review 24 | uses: ZedThree/clang-tidy-review/post@v0.21.0 25 | with: 26 | max_comments: 10 27 | 28 | # If there are any comments, fail the check 29 | - if: steps.post-review.outputs.total_comments > 0 30 | run: exit 1 -------------------------------------------------------------------------------- /patches/llvm/emscripten-clang19-3-remove-zdefs.patch: -------------------------------------------------------------------------------- 1 | diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake 2 | index 5ca580fbb..cff186b7b 100644 3 | --- a/llvm/cmake/modules/HandleLLVMOptions.cmake 4 | +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake 5 | @@ -302,7 +302,7 @@ endif() 6 | 7 | # Pass -Wl,-z,defs. This makes sure all symbols are defined. Otherwise a DSO 8 | # build might work on ELF but fail on MachO/COFF. 9 | -if(NOT (CMAKE_SYSTEM_NAME MATCHES "Darwin|FreeBSD|OpenBSD|DragonFly|AIX|OS390" OR 10 | +if(NOT (CMAKE_SYSTEM_NAME MATCHES "Darwin|FreeBSD|OpenBSD|DragonFly|AIX|OS390|Emscripten" OR 11 | WIN32 OR CYGWIN) AND 12 | NOT LLVM_USE_SANITIZER) 13 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs") 14 | -------------------------------------------------------------------------------- /docs/FAQ.rst: -------------------------------------------------------------------------------- 1 | FAQ 2 | ---- 3 | 4 | 1. **What is CppInterOp?**: 5 | 6 | - CppInterOp is a Clang-based C++ Interoperability library. 7 | 8 | 2. **Why should you use CppInterOp?**: 9 | 10 | - It can help you to integrate C++ code with other languages, which can make your 11 | code more portable and reusable. 12 | 13 | - It can help you to prototype environments for C++, which can make it easier to 14 | experiment with new ideas and developments. 15 | 16 | - It can help you to develop scientific applications that use C++ and other 17 | languages, which can make your applications more powerful and flexible. 18 | 19 | 3. **How can we integrate C++ code with other languages using CppInterOp?**: 20 | - You can refer the guides/tutorials here for:- 21 | 22 | - Installation And Usage Guide-:doc:`Installation and usage ` 23 | 24 | - Tutorials-:doc:`Tutorials ` -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. CppInterOp documentation master file, created by 2 | sphinx-quickstart on Sat Jun 17 13:01:46 2023. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | 7 | Welcome to CppInterOp's documentation! 8 | ====================================== 9 | 10 | 11 | The CppInterOp library (previously LibInterOp) provides a minimalist approach 12 | for other languages to identify C++ entities (variables, classes, etc.). This 13 | enables interoperability with C++ code, bringing the speed and efficiency of 14 | C++ to simpler, more interactive languages like Python. 15 | 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | :caption: Contents: 20 | 21 | 22 | InstallationAndUsage 23 | Emscripten-build-instructions 24 | UsingCppInterOp 25 | reference 26 | tutorials 27 | DebuggingCppInterOp 28 | FAQ 29 | DevelopersDocumentation 30 | -------------------------------------------------------------------------------- /patches/llvm/emscripten-clang19-2-shift-temporary-files-to-tmp-dir.patch: -------------------------------------------------------------------------------- 1 | diff --git a/clang/lib/Interpreter/Wasm.cpp b/clang/lib/Interpreter/Wasm.cpp 2 | index aa10b160ccf8..184867e2b55f 100644 3 | --- a/clang/lib/Interpreter/Wasm.cpp 4 | +++ b/clang/lib/Interpreter/Wasm.cpp 5 | @@ -76,8 +76,8 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { 6 | llvm::TargetMachine *TargetMachine = Target->createTargetMachine( 7 | PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_); 8 | PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); 9 | - std::string ObjectFileName = PTU.TheModule->getName().str() + ".o"; 10 | - std::string BinaryFileName = PTU.TheModule->getName().str() + ".wasm"; 11 | + std::string ObjectFileName = "/tmp/" + PTU.TheModule->getName().str() + ".o"; 12 | + std::string BinaryFileName = "/tmp/" + PTU.TheModule->getName().str() + ".wasm"; 13 | 14 | std::error_code Error; 15 | llvm::raw_fd_ostream ObjectFileOutput(llvm::StringRef(ObjectFileName), Error); 16 | -------------------------------------------------------------------------------- /patches/llvm/emscripten-clang20-2-shift-temporary-files-to-tmp-dir.patch: -------------------------------------------------------------------------------- 1 | diff --git a/clang/lib/Interpreter/Wasm.cpp b/clang/lib/Interpreter/Wasm.cpp 2 | index aa10b160ccf8..184867e2b55f 100644 3 | --- a/clang/lib/Interpreter/Wasm.cpp 4 | +++ b/clang/lib/Interpreter/Wasm.cpp 5 | @@ -76,8 +76,8 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { 6 | llvm::TargetMachine *TargetMachine = Target->createTargetMachine( 7 | PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_); 8 | PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); 9 | - std::string ObjectFileName = PTU.TheModule->getName().str() + ".o"; 10 | - std::string BinaryFileName = PTU.TheModule->getName().str() + ".wasm"; 11 | + std::string ObjectFileName = "/tmp/" + PTU.TheModule->getName().str() + ".o"; 12 | + std::string BinaryFileName = "/tmp/" + PTU.TheModule->getName().str() + ".wasm"; 13 | 14 | std::error_code Error; 15 | llvm::raw_fd_ostream ObjectFileOutput(llvm::StringRef(ObjectFileName), Error); 16 | -------------------------------------------------------------------------------- /cmake/FindSphinx.cmake: -------------------------------------------------------------------------------- 1 | # CMake find_package() Module for Sphinx documentation generator 2 | # http://sphinx-doc.org/ 3 | # 4 | # Example usage: 5 | # 6 | # find_package(Sphinx) 7 | # 8 | # If successful the following variables will be defined 9 | # SPHINX_FOUND 10 | # SPHINX_EXECUTABLE 11 | 12 | find_program(SPHINX_EXECUTABLE 13 | NAMES sphinx-build sphinx-build2 14 | DOC "Path to sphinx-build executable") 15 | 16 | # Handle REQUIRED and QUIET arguments 17 | # this will also set SPHINX_FOUND to true if SPHINX_EXECUTABLE exists 18 | include(FindPackageHandleStandardArgs) 19 | find_package_handle_standard_args(Sphinx 20 | "Failed to locate sphinx-build executable" 21 | SPHINX_EXECUTABLE) 22 | 23 | # Provide options for controlling different types of output 24 | option(SPHINX_OUTPUT_HTML "Output standalone HTML files" ON) 25 | option(SPHINX_OUTPUT_MAN "Output man pages" ON) 26 | 27 | option(SPHINX_WARNINGS_AS_ERRORS "When building documentation treat warnings as errors" ON) -------------------------------------------------------------------------------- /.github/actions/Miscellaneous/Select_Default_Build_Type/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Select Default Build Type' 2 | description: 'This action selects the default build typose' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Select default build type of Unix Systems 8 | if: runner.os != 'Windows' 9 | shell: bash 10 | run: | 11 | echo "BUILD_TYPE=Release" >> $GITHUB_ENV 12 | echo "CODE_COVERAGE=0" >> $GITHUB_ENV 13 | if [[ "${{ matrix.os }}" != "macos"* ]]; then 14 | echo "ncpus=$(nproc --all)" >> $GITHUB_ENV 15 | else 16 | echo "ncpus=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV 17 | fi 18 | 19 | - name: Select default build type on Windows 20 | if: runner.os == 'Windows' 21 | shell: powershell 22 | run: | 23 | echo "BUILD_TYPE=Release" >> $env:GITHUB_ENV 24 | echo "CODE_COVERAGE=0" >> $env:GITHUB_ENV 25 | $env:ncpus=$([Environment]::ProcessorCount) 26 | echo "ncpus=$env:ncpus" >> $env:GITHUB_ENV 27 | 28 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen REQUIRED) 2 | 3 | set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in) 4 | set(DOXYFILE ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg) 5 | 6 | set(docs_srcdir ${CMAKE_CURRENT_SOURCE_DIR}) 7 | set(docs_builddir ${CMAKE_CURRENT_BINARY_DIR}) 8 | set(cppinterop_srcdir ${CMAKE_SOURCE_DIR}) 9 | # file(READ ${CMAKE_SOURCE_DIR}/VERSION PACKAGE_VERSION) 10 | 11 | configure_file(${DOXYFILE_IN} ${DOXYFILE} @ONLY) 12 | 13 | add_custom_target(doxygen-cppinterop 14 | COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE} 15 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 16 | COMMENT "Generate CppInterOp documentation with Doxygen" 17 | VERBATIM) 18 | 19 | 20 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 21 | include(CreateSphinxTarget) 22 | 23 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/conf.py 24 | ${CMAKE_CURRENT_BINARY_DIR}/conf.py 25 | @ONLY 26 | ) 27 | 28 | create_sphinx_target( 29 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} 30 | TARGET_NAME sphinx-cppinterop 31 | ) -------------------------------------------------------------------------------- /.github/workflows/markdown-linter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Markdown-Linter 3 | 4 | on: 5 | pull_request: 6 | branches: [main] 7 | paths: 8 | - '**.md' 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | build: 16 | name: Linter 17 | runs-on: ubuntu-22.04 18 | 19 | permissions: 20 | contents: read 21 | packages: read 22 | # To report GitHub Actions status checks 23 | statuses: write 24 | 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v5 28 | with: 29 | # super-linter needs the full git history to get the 30 | # list of files that changed across commits 31 | fetch-depth: 0 32 | 33 | - name: Super-linter 34 | uses: super-linter/super-linter@v7.2.0 35 | env: 36 | VALIDATE_ALL_CODEBASE: false 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | VALIDATE_MARKDOWN: true 39 | DEFAULT_BRANCH: ${{ github.base_ref }} 40 | MARKDOWN_LINT_CONFIG_FILE: .markdownlint.json 41 | -------------------------------------------------------------------------------- /docs/doxygen-mainpage.dox: -------------------------------------------------------------------------------- 1 | /// @mainpage CppInterOp 2 | /// 3 | /// @section main_intro Introduction 4 | /// A Clang-based C++ Interoperability library, which allow C++ code to be accessed and used from other programming languages. 5 | /// This involves creating a bridge or wrapper that exposes C++ functionality through a language-specific API. 6 | /// The document explains the detailings of the API of the language interoperability layer. This library allows different languages 7 | /// to interoperate with C++ code, instantiate a template and execute it. 8 | /// 9 | /// This documentation describes the @b internal software that makes 10 | /// up CppInterOp, not the @b external use of CppInterOp. There are no complete instructions 11 | /// here on how to use CppInterOp, only the APIs that make up the software. For 12 | /// usage instructions, please see the programmer's guide or reference 13 | /// manual. 14 | /// 15 | /// @section main_caveat Caveat 16 | /// This documentation is generated directly from the source code with doxygen. 17 | /// Since CppInterOp is constantly under active development, what you're about to 18 | /// read is out of date! 19 | -------------------------------------------------------------------------------- /cmake/CreateSphinxTarget.cmake: -------------------------------------------------------------------------------- 1 | # Implementation of 'create_sphinx_target' in this file is copied from 2 | # llvm implementation of 'AddSphinxTarget'. 3 | # https://github.com/llvm/llvm-project/blob/main/llvm/cmake/modules/AddSphinxTarget.cmake 4 | 5 | find_package(Sphinx REQUIRED) 6 | 7 | function(create_sphinx_target) 8 | cmake_parse_arguments(SPHINX 9 | "" # options 10 | "SOURCE_DIR;TARGET_NAME" 11 | "" # multi-value keywords 12 | ${ARGN} 13 | ) 14 | set(SPHINX_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/build) 15 | set(SPHINX_DOC_TREE_DIR ${CMAKE_CURRENT_BINARY_DIR}/_doctrees) 16 | 17 | add_custom_target(${SPHINX_TARGET_NAME} 18 | COMMAND 19 | ${SPHINX_EXECUTABLE} -b html -d ${SPHINX_DOC_TREE_DIR} -q ${SPHINX_SOURCE_DIR} ${SPHINX_BUILD_DIR} 20 | COMMENT 21 | "Generating sphinx user documentation into \"${SPHINX_BUILD_DIR}\"" 22 | VERBATIM 23 | ) 24 | message(STATUS "Added ${SPHINX_TARGET_NAME} target") 25 | endfunction() -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/01-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report. 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "Thanks for taking the time to fill out this bug report!" 9 | - type: textarea 10 | attributes: 11 | label: "Description of bug" 12 | description: "Tell us what the bug is, and what you result you had expected" 13 | value: "Replace with description of bug." 14 | validations: 15 | required: true 16 | - type: dropdown 17 | attributes: 18 | label: "What operating system was you using when the bug occured?" 19 | multiple: true 20 | options: 21 | - Ubuntu 22 | - MacOS 23 | - Windows 24 | - Other 25 | validations: 26 | required: false 27 | - type: dropdown 28 | attributes: 29 | label: "What is the architechture of the cpu on your system?" 30 | multiple: true 31 | options: 32 | - X86 33 | - ARM 34 | - Other 35 | validations: 36 | required: false 37 | - type: dropdown 38 | attributes: 39 | label: "What did you build CppInterOp against?" 40 | multiple: true 41 | options: 42 | - Clang-repl (LLVM19) 43 | - Clang-repl (LLVM18) 44 | - Clang-repl (LLVM17) 45 | - Clang-repl (LLVM16) 46 | - Cling 1.0 47 | validations: 48 | required: false 49 | -------------------------------------------------------------------------------- /.github/workflows/clang-tidy-review.yml: -------------------------------------------------------------------------------- 1 | name: clang-tidy-review 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.h' 7 | - '**.cpp' 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | review: 15 | runs-on: ubuntu-22.04 16 | steps: 17 | - name: Checkout PR branch 18 | uses: actions/checkout@v5 19 | 20 | - name: Setup Python 21 | uses: actions/setup-python@v6 22 | with: 23 | python-version: "3.11" 24 | 25 | - name: Install LLVM and Clang 26 | uses: KyleMayes/install-llvm-action@v2.0.7 27 | with: 28 | version: "20.1.4" 29 | 30 | - name: install lit 31 | run: pip install lit 32 | 33 | - name: Run clang-tidy 34 | uses: ZedThree/clang-tidy-review@v0.21.0 35 | id: review 36 | with: 37 | build_dir: build 38 | apt_packages: cmake,libxml2,libxml2-dev,libtinfo-dev,zlib1g-dev,libzstd-dev 39 | split_workflow: true 40 | config_file: .clang-tidy 41 | cmake_command: > 42 | cmake . -B build -DCMAKE_BUILD_TYPE="Release" 43 | -DCMAKE_C_COMPILER="$GITHUB_WORKSPACE/llvm/bin/clang" 44 | -DCMAKE_CXX_COMPILER="$GITHUB_WORKSPACE/llvm/bin/clang++" 45 | -DLLVM_DIR="$GITHUB_WORKSPACE/llvm" 46 | -DBUILD_SHARED_LIBS=ON 47 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON && 48 | cd build && 49 | cmake --build . --target googletest --parallel $(nproc --all) 50 | 51 | - name: Upload artifacts 52 | uses: ZedThree/clang-tidy-review/upload@v0.21.0 53 | -------------------------------------------------------------------------------- /.github/actions/Miscellaneous/Install_Dependencies/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Install Dependencies' 2 | description: 'This PR installs the dependencies needed' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | 8 | - name: Install dependencies on MacOS 9 | if: runner.os == 'macOS' 10 | shell: bash 11 | run: | 12 | brew update --force 13 | if [[ "$(uname -m)" == "x86_64" ]]; then 14 | brew remove swiftlint 15 | else 16 | brew remove unxip 17 | fi 18 | # workaround for https://github.com/actions/setup-python/issues/577 19 | for pkg in $(brew list | grep '^python@'); do 20 | brew unlink "$pkg" 21 | brew link --overwrite "$pkg" 22 | done 23 | brew install ninja 24 | brew install eigen 25 | brew install boost 26 | brew install gnu-sed 27 | pip install distro pytest 28 | 29 | - name: Install dependencies on Linux 30 | if: runner.os == 'Linux' 31 | shell: bash 32 | run: | 33 | # Install deps 34 | sudo apt-get update 35 | sudo apt-get install valgrind ninja-build 36 | sudo apt-get install git g++ debhelper devscripts gnupg python3 doxygen graphviz python3-sphinx 37 | sudo apt-get install -y libc6-dbg 38 | sudo apt-get install valgrind 39 | sudo apt autoremove 40 | sudo apt clean 41 | # Install libraries used by the cppyy test suite 42 | sudo apt install libeigen3-dev 43 | sudo apt install libboost-all-dev 44 | 45 | 46 | - name: Install dependencies on Windows 47 | if: runner.os == 'Windows' 48 | shell: powershell 49 | run: | 50 | choco install findutils 51 | $env:PATH="C:\Program Files (x86)\GnuWin32\bin;$env:PATH" 52 | 53 | -------------------------------------------------------------------------------- /patches/llvm/cling1.2-LookupHelper.patch: -------------------------------------------------------------------------------- 1 | diff --git a/include/cling/Interpreter/LookupHelper.h b/include/cling/Interpreter/LookupHelper.h 2 | index 6e6e2814..cd79b2a6 100644 3 | --- a/include/cling/Interpreter/LookupHelper.h 4 | +++ b/include/cling/Interpreter/LookupHelper.h 5 | @@ -56,7 +56,7 @@ namespace cling { 6 | WithDiagnostics 7 | }; 8 | private: 9 | - std::unique_ptr m_Parser; 10 | + clang::Parser* m_Parser; 11 | Interpreter* m_Interpreter; // we do not own. 12 | std::array m_StringTy = {{}}; 13 | /// A map containing the hash of the lookup buffer. This allows us to avoid 14 | diff --git a/lib/Interpreter/Interpreter.cpp b/lib/Interpreter/Interpreter.cpp 15 | index 6af90108..89ca360b 100644 16 | --- a/lib/Interpreter/Interpreter.cpp 17 | +++ b/lib/Interpreter/Interpreter.cpp 18 | @@ -265,13 +265,6 @@ namespace cling { 19 | } 20 | 21 | Sema& SemaRef = getSema(); 22 | - Preprocessor& PP = SemaRef.getPreprocessor(); 23 | - 24 | - m_LookupHelper.reset(new LookupHelper(new Parser(PP, SemaRef, 25 | - /*SkipFunctionBodies*/false, 26 | - /*isTemp*/true), this)); 27 | - if (!m_LookupHelper) 28 | - return; 29 | 30 | if (!isInSyntaxOnlyMode() && !m_Opts.CompilerOpts.CUDADevice) { 31 | m_Executor.reset(new IncrementalExecutor(SemaRef.Diags, *getCI(), 32 | @@ -317,6 +310,10 @@ namespace cling { 33 | return; 34 | } 35 | 36 | + m_LookupHelper.reset(new LookupHelper(m_IncrParser->getParser(), this)); 37 | + if (!m_LookupHelper) 38 | + return; 39 | + 40 | // When not using C++ modules, we now have a PCH and we can safely setup 41 | // our callbacks without fearing that they get overwritten by clang code. 42 | // The modules setup is handled above. 43 | -------------------------------------------------------------------------------- /discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | project = 'CppInterOp' 10 | copyright = '2023, Vassil Vassilev' 11 | author = 'Vassil Vassilev' 12 | release = 'Dev' 13 | 14 | # -- General configuration --------------------------------------------------- 15 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 16 | 17 | extensions = [] 18 | 19 | templates_path = ['_templates'] 20 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 21 | 22 | 23 | 24 | # -- Options for HTML output ------------------------------------------------- 25 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 26 | 27 | html_theme = 'alabaster' 28 | 29 | html_theme_options = { 30 | "github_user": "compiler-research", 31 | "github_repo": "CppInterOp", 32 | "github_banner": True, 33 | "fixed_sidebar": True, 34 | } 35 | 36 | highlight_language = "C++" 37 | 38 | todo_include_todos = True 39 | 40 | mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" 41 | 42 | # Add latex physics package 43 | mathjax3_config = { 44 | "loader": {"load": ["[tex]/physics"]}, 45 | "tex": {"packages": {"[+]": ["physics"]}}, 46 | } 47 | 48 | import os 49 | CPPINTEROP_ROOT = os.path.abspath('..') 50 | html_extra_path = [CPPINTEROP_ROOT + '/build/docs/'] 51 | 52 | import subprocess 53 | command = 'mkdir {0}/build; cd {0}/build; cmake ../ -DClang_DIR=/usr/lib/llvm-16/build/lib/cmake/clang\ 54 | -DLLVM_DIR=/usr/lib/llvm-16/build/lib/cmake/llvm -DCPPINTEROP_ENABLE_DOXYGEN=ON\ 55 | -DCPPINTEROP_INCLUDE_DOCS=ON'.format(CPPINTEROP_ROOT) 56 | subprocess.call(command, shell=True) 57 | subprocess.call('doxygen {0}/build/docs/doxygen.cfg'.format(CPPINTEROP_ROOT), shell=True) 58 | -------------------------------------------------------------------------------- /docs/ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | This document contains the release notes for the language interoperability 4 | library CppInterOp, release 1.8.0. CppInterOp is built on top of 5 | [Clang](http://clang.llvm.org) and [LLVM](http://llvm.org%3E) compiler 6 | infrastructure. Here we describe the status of CppInterOp in some detail, 7 | including major improvements from the previous release and new feature work. 8 | Note that if you are reading this file from a git checkout, this document 9 | applies to the *next* release, not the current one. 10 | 11 | CppInterOp exposes API from Clang and LLVM in a backward compatibe way. The API 12 | support downstream tools that utilize interactive C++ by using the compiler as 13 | a service. That is, embed Clang and LLVM as a libraries in their codebases. The 14 | API are designed to be minimalistic and aid non-trivial tasks such as language 15 | interoperability on the fly. In such scenarios CppInterOp can be used to provide 16 | the necessary introspection information to the other side helping the language 17 | cross talk. 18 | 19 | ## What's New in CppInterOp 1.8.0? 20 | 21 | Some of the major new features and improvements to CppInterOp are listed here. 22 | Generic improvements to CppInterOp as a whole or to its underlying 23 | infrastructure are described first. 24 | 25 | ## External Dependencies 26 | 27 | - CppInterOp now works with: 28 | - llvm20 29 | 30 | ## Introspection 31 | 32 | - 33 | 34 | ## Just-in-Time Compilation 35 | 36 | - 37 | 38 | ## Incremental C++ 39 | 40 | - 41 | 42 | ## Misc 43 | 44 | - 45 | 46 | ## Fixed Bugs 47 | 48 | [XXX](https://github.com/compiler-research/CppInterOp/issues/XXX) 49 | 50 | 53 | 54 | ## Special Kudos 55 | 56 | This release wouldn't have happened without the efforts of our contributors, 57 | listed in the form of Firstname Lastname (#contributions): 58 | 59 | FirstName LastName (#commits) 60 | 61 | A B (N) 62 | 63 | 67 | -------------------------------------------------------------------------------- /docs/reference.rst: -------------------------------------------------------------------------------- 1 | Reference 2 | --------- 3 | 4 | [1] Results of US Research Software Sustainability Institute (URSSI) 5 | programming language survey, private communication from Dan Katz (UIUC/NCSA), 6 | https://spectrum.ieee. org/computing/software/the-2017-top-programming-languages 7 | (Visited August 2021). 8 | 9 | [2] Beazley, David M. “SWIG: An Easy to Use Tool for Integrating Scripting 10 | Languages with C and C++.” Tcl/Tk Workshop. Vol. 43. 1996. 11 | 12 | [3] SIP software home page, https://www.riverbankcomputing.com/ software/sip/intro (Visited Sep 2021) 13 | 14 | [4] Boost.Python Reference Manual (for software version 1.72), 15 | https: //www.boost.org/doc/libs/1_65_1/libs/python/doc/html/reference/index.html, (Visited August 2021) 16 | 17 | [5] Pybind11 project homepage, http://pybind11.readthedocs.io/ (Visited August 2021). 18 | 19 | [6] ​​Vassilev, Vassil, et al. “Cling–the new interactive interpreter for ROOT 6. 20 | ” Journal of Physics: Conference Series. Vol. 396. No. 5. IOP Publishing, 2012. 21 | 22 | [7] Cxx.jl GitHub repository, https://github.com/JuliaInterop/Cxx.jl (Visited September 2021) 23 | 24 | [8] Wim Lavrijsen, cppyy, presentation, https://compiler-research.org/meetings/#caas_02Sep2021 (Visited September 2021) 25 | 26 | [9] Cppyy Philosophy, https://cppyy.readthedocs.io/en/latest/philosophy.html#run-time-v-s-compile-time (Visited August 2021) 27 | 28 | [10] Clang: a C language family frontend for LLVM, https://clang.llvm.org/, (Visited August 2021) 29 | 30 | [11] Alexandru Militaru, Calling C++ libraries from a D-written DSL: A cling/cppyy-based approach, presentation, 31 | https://compiler-research.org/meetings/#caas_04Feb2021, (Visited August 2021) 32 | 33 | [12] Keno Fischer, A brief history of Cxx.jl, https://compiler-research.org/meetings/#caas_05Aug2021, (Visited August 2021) 34 | 35 | [13] Template instantiation not happening in indirectly called function, 36 | https://bitbucket.org/wlav/cppyy/issues/369/template-instantiation-not-happening-in (Visited August 2021). 37 | 38 | [14] Extend clang AST to provide information for the type as written in template instantiations, 39 | https://llvm.org/OpenProjects.html#clang-template-instantiation-sugar (Visited August 2021). -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | name: clang-format 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.h' 7 | - '**.cpp' 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | precheckin: 15 | runs-on: ubuntu-22.04 16 | steps: 17 | - name: Checkout PR branch 18 | uses: actions/checkout@v5 19 | with: 20 | ref: ${{ github.event.pull_request.head.sha }} 21 | fetch-depth: 0 22 | 23 | - name: Setup Python 24 | uses: actions/setup-python@v6 25 | with: 26 | python-version: "3.11" 27 | 28 | - name: Install clang-format 29 | run: | 30 | curl https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 31 | os_codename="`cat /etc/os-release | grep UBUNTU_CODENAME | cut -d = -f 2`" 32 | echo "deb https://apt.llvm.org/${os_codename}/ llvm-toolchain-${os_codename}-18 main" | sudo tee -a /etc/apt/sources.list 33 | sudo apt update 34 | sudo apt install -y clang-format-18 35 | 36 | - name: Run git-clang-format 37 | run: | 38 | PR_BASE=$(git rev-list ${{ github.event.pull_request.head.sha }} ^${{ github.event.pull_request.base.sha }} | tail --lines 1 | xargs -I {} git rev-parse {}~1) 39 | echo "running git clang-format against $PR_BASE commit" 40 | git \ 41 | -c color.ui=always \ 42 | -c diff.wsErrorHighlight=all \ 43 | -c color.diff.whitespace='red reverse' \ 44 | clang-format-18 --diff --binary clang-format-18 --commit $PR_BASE -- include/ lib/ || \ 45 | (echo "Please run the following git-clang-format locally to fix the formatting: \n 46 | git-clang-format HEAD~\n 47 | for multiple commits we should place the formatting changes in the related commit with:\n 48 | \t\tgit rebase -i -x \"git-clang-format-18 main && git commit -a --allow-empty --fixup=HEAD\" --strategy-option=theirs origin/main\n 49 | \t\t Then inspect the results with: git log --oneline\n 50 | \t\t Then squash without poluting the history with: git rebase --autosquash -i main\n" && exit 1) 51 | -------------------------------------------------------------------------------- /unittests/CppInterOp/CUDATest.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include "CppInterOp/CppInterOp.h" 4 | 5 | #include "clang/Basic/Version.h" 6 | 7 | #include "gtest/gtest.h" 8 | 9 | using namespace TestUtils; 10 | 11 | static bool HasCudaSDK() { 12 | auto supportsCudaSDK = []() { 13 | #ifdef CPPINTEROP_USE_CLING 14 | // FIXME: Enable this for cling. 15 | return false; 16 | #endif 17 | if (!Cpp::CreateInterpreter({}, {"--cuda"})) 18 | return false; 19 | return Cpp::Declare("__global__ void test_func() {}" 20 | "test_func<<<1,1>>>();") == 0; 21 | }; 22 | static bool hasCuda = supportsCudaSDK(); 23 | return hasCuda; 24 | } 25 | 26 | static bool HasCudaRuntime() { 27 | auto supportsCuda = []() { 28 | #ifdef CPPINTEROP_USE_CLING 29 | // FIXME: Enable this for cling. 30 | return false; 31 | #endif 32 | if (!HasCudaSDK()) 33 | return false; 34 | 35 | if (!Cpp::CreateInterpreter({}, {"--cuda"})) 36 | return false; 37 | if (Cpp::Declare("__global__ void test_func() {}" 38 | "test_func<<<1,1>>>();")) 39 | return false; 40 | intptr_t result = Cpp::Evaluate("(bool)cudaGetLastError()"); 41 | return !(bool)result; 42 | }; 43 | static bool hasCuda = supportsCuda(); 44 | return hasCuda; 45 | } 46 | 47 | #ifdef CPPINTEROP_USE_CLING 48 | TEST(DISABLED_CUDATest, Sanity) { 49 | #else 50 | TEST(CUDATest, Sanity) { 51 | #endif 52 | #ifdef _WIN32 53 | GTEST_SKIP() << "Disabled on Windows. Needs fixing."; 54 | #endif 55 | if (!HasCudaSDK()) 56 | GTEST_SKIP() << "Skipping CUDA tests as CUDA SDK not found"; 57 | EXPECT_TRUE(Cpp::CreateInterpreter({}, {"--cuda"})); 58 | } 59 | 60 | TEST(CUDATest, CUDAH) { 61 | #ifdef _WIN32 62 | GTEST_SKIP() << "Disabled on Windows. Needs fixing."; 63 | #endif 64 | if (!HasCudaSDK()) 65 | GTEST_SKIP() << "Skipping CUDA tests as CUDA SDK not found"; 66 | 67 | Cpp::CreateInterpreter({}, {"--cuda"}); 68 | bool success = !Cpp::Declare("#include "); 69 | EXPECT_TRUE(success); 70 | } 71 | 72 | TEST(CUDATest, CUDARuntime) { 73 | #ifdef _WIN32 74 | GTEST_SKIP() << "Disabled on Windows. Needs fixing."; 75 | #endif 76 | if (!HasCudaRuntime()) 77 | GTEST_SKIP() << "Skipping CUDA tests as CUDA runtime not found"; 78 | 79 | EXPECT_TRUE(HasCudaRuntime()); 80 | } 81 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -*, 3 | bugprone-*, 4 | clang-diagnostic-*, 5 | clang-analyzer-*, 6 | cppcoreguidelines-*, 7 | llvm-*, 8 | misc-*, 9 | modernize-*, 10 | performance-*, 11 | portability-*, 12 | readability-*, 13 | -bugprone-narrowing-conversions, 14 | -bugprone-easily-swappable-parameters, 15 | -bugprone-implicit-widening-of-multiplication-result, 16 | -bugprone-unchecked-optional-access, 17 | -misc-const-correctness, 18 | -misc-unused-parameters, 19 | -misc-non-private-member-variables-in-classes, 20 | -misc-no-recursion, 21 | -misc-use-anonymous-namespace, 22 | -modernize-return-braced-init-list, 23 | -modernize-use-trailing-return-type, 24 | -readability-braces-around-statements, 25 | -readability-identifier-length, 26 | -readability-implicit-bool-conversion, 27 | -readability-magic-numbers, 28 | -readability-named-parameter, 29 | -readability-function-cognitive-complexity, 30 | -readability-redundant-access-specifiers, 31 | -cppcoreguidelines-avoid-magic-numbers, 32 | -cppcoreguidelines-init-variables, 33 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 34 | -cppcoreguidelines-pro-type-member-init, 35 | -cppcoreguidelines-macro-usage, 36 | -llvm-namespace-comment 37 | 38 | CheckOptions: 39 | - key: readability-identifier-naming.ClassCase 40 | value: aNy_CasE 41 | - key: readability-identifier-naming.FunctionCase 42 | value: aNy_CasE 43 | - key: readability-identifier-naming.MemberCase 44 | value: aNy_CasE 45 | - key: readability-identifier-naming.ParameterCase 46 | value: aNy_CasE 47 | - key: readability-identifier-naming.UnionCase 48 | value: CamelCase 49 | - key: readability-identifier-naming.VariableCase 50 | value: aNy_CasE 51 | - key: readability-identifier-naming.IgnoreMainLikeFunctions 52 | value: 1 53 | - key: cppcoreguidelines-avoid-magic-numbers.IgnoreMacros 54 | value: 1 55 | - key: cppcoreguidelines-pro-bounds-pointer-arithmetic.Pessimistic 56 | value: 1 57 | - key: cppcoreguidelines-pro-type-member-init.InitWithEquals 58 | value: 1 59 | - key: llvm-namespace-comment.Spaces 60 | value: 2 61 | -------------------------------------------------------------------------------- /etc/clang20-valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | Clang 20 Out of Process 3 | Memcheck:Param 4 | write(buf) 5 | fun:__libc_write 6 | fun:write 7 | fun:_ZN4llvm3orc26FDSimpleRemoteEPCTransport11sendMessageENS0_21SimpleRemoteEPCOpcodeEmNS0_12ExecutorAddrENS_8ArrayRefIcEE 8 | fun:_ZN4llvm3orc15SimpleRemoteEPC11sendMessageENS0_21SimpleRemoteEPCOpcodeEmNS0_12ExecutorAddrENS_8ArrayRefIcEE 9 | fun:_ZN4llvm3orc15SimpleRemoteEPC16callWrapperAsyncENS0_12ExecutorAddrENS0_22ExecutorProcessControl18IncomingWFRHandlerENS_8ArrayRefIcEE 10 | fun:_ZN4llvm3orc30EPCGenericJITLinkMemoryManager13InFlightAlloc8finalizeENS_15unique_functionIFvNS_8ExpectedINS_7jitlink20JITLinkMemoryManager14FinalizedAllocEEEEEE 11 | fun:_ZN4llvm7jitlink13JITLinkerBase10linkPhase3ESt10unique_ptrIS1_St14default_deleteIS1_EENS_8ExpectedINS_8DenseMapINS_3orc15SymbolStringPtrENS8_17ExecutorSymbolDefENS_12DenseMapInfoIS9_vEENS_6detail12DenseMapPairIS9_SA_EEEEEE 12 | fun:_ZZN4llvm7jitlink24createLookupContinuationIZNS0_13JITLinkerBase10linkPhase2ESt10unique_ptrIS2_St14default_deleteIS2_EENS_8ExpectedIS3_INS0_20JITLinkMemoryManager13InFlightAllocES4_IS9_EEEEEUlNS7_INS_8DenseMapINS_3orc15SymbolStringPtrENSE_17ExecutorSymbolDefENS_12DenseMapInfoISF_vEENS_6detail12DenseMapPairISF_SG_EEEEEEE_EES3_INS0_30JITLinkAsyncLookupContinuationES4_ISP_EET_EN4Impl3runESN_ 13 | fun:_ZZN4llvm3orc21LinkGraphLinkingLayer10JITLinkCtx6lookupERKNS_8DenseMapINS0_15SymbolStringPtrENS_7jitlink17SymbolLookupFlagsENS_12DenseMapInfoIS4_vEENS_6detail12DenseMapPairIS4_S6_EEEESt10unique_ptrINS5_30JITLinkAsyncLookupContinuationESt14default_deleteISG_EEENUlNS_8ExpectedINS3_IS4_NS0_17ExecutorSymbolDefES8_NSA_IS4_SL_EEEEEEE0_clESO_ 14 | fun:_ZN4llvm6detail18UniqueFunctionBaseIvJNS_8ExpectedINS_8DenseMapINS_3orc15SymbolStringPtrENS4_17ExecutorSymbolDefENS_12DenseMapInfoIS5_vEENS0_12DenseMapPairIS5_S6_EEEEEEEE8CallImplIZNS4_21LinkGraphLinkingLayer10JITLinkCtx6lookupERKNS3_IS5_NS_7jitlink17SymbolLookupFlagsES8_NS9_IS5_SI_EEEESt10unique_ptrINSH_30JITLinkAsyncLookupContinuationESt14default_deleteISO_EEEUlSC_E0_EEvPvRSC_ 15 | fun:_ZZN4llvm3orc23AsynchronousSymbolQuery14handleCompleteERNS0_16ExecutionSessionEEN20RunQueryCompleteTask3runEv 16 | fun:_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJZN4llvm3orc31DynamicThreadPoolTaskDispatcher8dispatchESt10unique_ptrINS4_4TaskESt14default_deleteIS7_EEEUlvE_EEEEE6_M_runEv 17 | obj:/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30 18 | } 19 | -------------------------------------------------------------------------------- /unittests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CTEST_BUILD_NAME 2 | ${CMAKE_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}-${CMAKE_BUILD_TYPE}) 3 | enable_testing() 4 | 5 | # LLVM builds (not installed llvm) provides gtest. 6 | if (NOT TARGET GTest::gtest AND NOT TARGET gtest) 7 | include(GoogleTest) 8 | endif() 9 | 10 | if(EMSCRIPTEN) 11 | if (TARGET GTest::gtest) 12 | # Target names in CMake >= v3.23 13 | set(gtest_libs GTest::gtest GTest::gmock) 14 | else() 15 | set(gtest_libs gtest gmock) 16 | endif() 17 | else() 18 | if (TARGET GTest::gtest) 19 | # Target names in CMake >= v3.23 20 | set(gtest_libs GTest::gtest GTest::gmock GTest::gtest_main) 21 | else() 22 | set(gtest_libs gtest gtest_main gmock) 23 | endif() 24 | set(link_pthreads_lib pthread) 25 | endif() 26 | 27 | add_custom_target(CppInterOpUnitTests) 28 | set_target_properties(CppInterOpUnitTests PROPERTIES FOLDER "CppInterOp tests") 29 | add_dependencies(CppInterOpUnitTests clangCppInterOp) 30 | 31 | set (TIMEOUT_VALUE 2400) 32 | function(add_cppinterop_unittest name) 33 | add_executable(${name} EXCLUDE_FROM_ALL ${ARGN}) 34 | add_dependencies(CppInterOpUnitTests ${name}) 35 | target_include_directories(${name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${GTEST_INCLUDE_DIR}) 36 | set_property(TARGET ${name} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 37 | if(WIN32) 38 | target_link_libraries(${name} PUBLIC ${ARG_LIBRARIES} ${gtest_libs}) 39 | set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS "${MSVC_EXPORTS}") 40 | else() 41 | target_link_libraries(${name} PRIVATE ${ARG_LIBRARIES} ${gtest_libs} ${link_pthreads_lib}) 42 | endif() 43 | if(EMSCRIPTEN) 44 | # Without this cmake will try and get node to run the html file. 45 | # This guarantees that it runs the js file, and uses emsdks node. 46 | add_test(NAME cppinterop-${name} COMMAND $ENV{EMSDK_NODE} ${name}.js) 47 | else() 48 | add_test(NAME cppinterop-${name} COMMAND ${name}) 49 | endif() 50 | set_tests_properties(cppinterop-${name} PROPERTIES 51 | TIMEOUT "${TIMEOUT_VALUE}" 52 | ENVIRONMENT "CPLUS_INCLUDE_PATH=${CMAKE_BINARY_DIR}/etc" 53 | LABELS 54 | DEPENDS) 55 | # FIXME: Just call gtest_add_tests this function is available. 56 | #gtest_add_tests(${name} "${Arguments}" AUTO) 57 | endfunction() 58 | 59 | add_subdirectory(CppInterOp) 60 | 61 | add_custom_target(check-cppinterop COMMAND ${CMAKE_CTEST_COMMAND} -V --build-config $ 62 | DEPENDS CppInterOpUnitTests WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 63 | 64 | set_target_properties(check-cppinterop PROPERTIES FOLDER "CppInterOp tests") 65 | -------------------------------------------------------------------------------- /patches/llvm/emscripten-clang20-3-enable_exception_handling.patch: -------------------------------------------------------------------------------- 1 | diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp 2 | index 3b81f9d70..2704edb8a 100644 3 | --- a/clang/lib/Interpreter/Interpreter.cpp 4 | +++ b/clang/lib/Interpreter/Interpreter.cpp 5 | @@ -142,6 +142,48 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { 6 | return std::move(Clang); 7 | } 8 | 9 | +static llvm::Error HandleFrontendOptions(const CompilerInstance &CI) { 10 | + const auto &FrontendOpts = CI.getFrontendOpts(); 11 | + 12 | + if (FrontendOpts.ShowHelp) { 13 | + driver::getDriverOptTable().printHelp( 14 | + llvm::outs(), "clang -cc1 [options] file...", 15 | + "LLVM 'Clang' Compiler: http://clang.llvm.org", 16 | + /*ShowHidden=*/false, /*ShowAllAliases=*/false, 17 | + llvm::opt::Visibility(driver::options::CC1Option)); 18 | + return llvm::createStringError(llvm::errc::not_supported, "Help displayed"); 19 | + } 20 | + 21 | + if (FrontendOpts.ShowVersion) { 22 | + llvm::cl::PrintVersionMessage(); 23 | + return llvm::createStringError(llvm::errc::not_supported, 24 | + "Version displayed"); 25 | + } 26 | + 27 | + if (!FrontendOpts.LLVMArgs.empty()) { 28 | + unsigned NumArgs = FrontendOpts.LLVMArgs.size(); 29 | + auto Args = std::make_unique(NumArgs + 2); 30 | + Args[0] = "clang-repl (LLVM option parsing)"; 31 | + for (unsigned i = 0; i != NumArgs; ++i) { 32 | + Args[i + 1] = FrontendOpts.LLVMArgs[i].c_str(); 33 | + // remove the leading '-' from the option name 34 | + if (Args[i + 1][0] == '-') { 35 | + auto *option = static_cast *>( 36 | + llvm::cl::getRegisteredOptions()[Args[i + 1] + 1]); 37 | + if (option) { 38 | + option->setInitialValue(true); 39 | + } else { 40 | + llvm::errs() << "Unknown LLVM option: " << Args[i + 1] << "\n"; 41 | + } 42 | + } 43 | + } 44 | + Args[NumArgs + 1] = nullptr; 45 | + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); 46 | + } 47 | + 48 | + return llvm::Error::success(); 49 | +} 50 | + 51 | } // anonymous namespace 52 | 53 | namespace clang { 54 | @@ -456,7 +498,12 @@ const char *const Runtimes = R"( 55 | 56 | llvm::Expected> 57 | Interpreter::create(std::unique_ptr CI) { 58 | - llvm::Error Err = llvm::Error::success(); 59 | + 60 | + llvm::Error Err = HandleFrontendOptions(*CI); 61 | + if (Err) { 62 | + return std::move(Err); 63 | + } 64 | + 65 | auto Interp = 66 | std::unique_ptr(new Interpreter(std::move(CI), Err)); 67 | if (Err) 68 | -------------------------------------------------------------------------------- /patches/llvm/emscripten-clang19-4-enable_exception_handling.patch: -------------------------------------------------------------------------------- 1 | diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp 2 | index 985d0b7c0..20a727893 100644 3 | --- a/clang/lib/Interpreter/Interpreter.cpp 4 | +++ b/clang/lib/Interpreter/Interpreter.cpp 5 | @@ -135,6 +135,48 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { 6 | return std::move(Clang); 7 | } 8 | 9 | +static llvm::Error HandleFrontendOptions(const CompilerInstance &CI) { 10 | + const auto &FrontendOpts = CI.getFrontendOpts(); 11 | + 12 | + if (FrontendOpts.ShowHelp) { 13 | + driver::getDriverOptTable().printHelp( 14 | + llvm::outs(), "clang -cc1 [options] file...", 15 | + "LLVM 'Clang' Compiler: http://clang.llvm.org", 16 | + /*ShowHidden=*/false, /*ShowAllAliases=*/false, 17 | + llvm::opt::Visibility(driver::options::CC1Option)); 18 | + return llvm::createStringError(llvm::errc::not_supported, "Help displayed"); 19 | + } 20 | + 21 | + if (FrontendOpts.ShowVersion) { 22 | + llvm::cl::PrintVersionMessage(); 23 | + return llvm::createStringError(llvm::errc::not_supported, 24 | + "Version displayed"); 25 | + } 26 | + 27 | + if (!FrontendOpts.LLVMArgs.empty()) { 28 | + unsigned NumArgs = FrontendOpts.LLVMArgs.size(); 29 | + auto Args = std::make_unique(NumArgs + 2); 30 | + Args[0] = "clang-repl (LLVM option parsing)"; 31 | + for (unsigned i = 0; i != NumArgs; ++i) { 32 | + Args[i + 1] = FrontendOpts.LLVMArgs[i].c_str(); 33 | + // remove the leading '-' from the option name 34 | + if (Args[i + 1][0] == '-') { 35 | + auto *option = static_cast *>( 36 | + llvm::cl::getRegisteredOptions()[Args[i + 1] + 1]); 37 | + if (option) { 38 | + option->setInitialValue(true); 39 | + } else { 40 | + llvm::errs() << "Unknown LLVM option: " << Args[i + 1] << "\n"; 41 | + } 42 | + } 43 | + } 44 | + Args[NumArgs + 1] = nullptr; 45 | + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); 46 | + } 47 | + 48 | + return llvm::Error::success(); 49 | +} 50 | + 51 | } // anonymous namespace 52 | 53 | llvm::Expected> 54 | @@ -306,7 +348,12 @@ const char *const Runtimes = R"( 55 | 56 | llvm::Expected> 57 | Interpreter::create(std::unique_ptr CI) { 58 | - llvm::Error Err = llvm::Error::success(); 59 | + 60 | + llvm::Error Err = HandleFrontendOptions(*CI); 61 | + if (Err) { 62 | + return std::move(Err); 63 | + } 64 | + 65 | auto Interp = 66 | std::unique_ptr(new Interpreter(std::move(CI), Err)); 67 | if (Err) 68 | -------------------------------------------------------------------------------- /.github/actions/Miscellaneous/Save_PR_Info/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Save PR Info' 2 | description: 'This PR saves the PR Info for the job' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | 8 | - name: Save PR Info on Unix Systems 9 | if: ${{ runner.os != 'Windows' }} 10 | shell: bash 11 | run: | 12 | mkdir -p ./pr 13 | echo ${{ github.event.number }} > ./pr/NR 14 | echo ${{ github.repository }} > ./pr/REPO 15 | 16 | cling_on=$(echo "${{ matrix.cling }}" | tr '[:lower:]' '[:upper:]') 17 | if [[ "$cling_on" == "ON" ]]; then 18 | export CLING_HASH=$(git ls-remote https://github.com/root-project/cling.git refs/tags/v${{ matrix.cling-version }} | tr '\t' '-') 19 | export LLVM_HASH=$(git ls-remote https://github.com/root-project/llvm-project.git cling-llvm${{ matrix.clang-runtime}} | tr '\t' '-') 20 | else 21 | export CLING_HASH="Repl" 22 | # May need to revert back to both having same llvm_hash, as below cause llvm to be rebuilt everytime commit is made to llvm/llvm-project for release a.x 23 | # which could be quite often for new releases 24 | export LLVM_HASH=$(git ls-remote https://github.com/llvm/llvm-project.git refs/heads/release/${{ matrix.clang-runtime}}.x | tr '\t' '-') 25 | fi 26 | 27 | echo "CLING_HASH=$CLING_HASH" >> $GITHUB_ENV 28 | echo "LLVM_HASH=$LLVM_HASH" >> $GITHUB_ENV 29 | 30 | - name: Save PR Info on Windows systems 31 | if: runner.os == 'Windows' 32 | shell: powershell 33 | run: | 34 | #can be found 35 | mkdir ./pr 36 | echo "${{ github.event.number }}" > ./pr/NR 37 | echo ${{ github.repository }} > ./pr/REPO 38 | 39 | if ( "${{ matrix.cling }}" -imatch "On" ) 40 | { 41 | $env:CLING_HASH_TEMP = ( git ls-remote https://github.com/root-project/cling.git refs/tags/v${{ matrix.cling-version }} ) 42 | $env:CLING_HASH = $env:CLING_HASH_TEMP -replace "\t","-" 43 | } 44 | else 45 | { 46 | $env:CLING_HASH="Repl" 47 | # May need to revert back to both having same llvm_hash, as below cause llvm to be rebuilt everytime commit is made to llvm/llvm-project for release a.x 48 | # which could be quite often for new releases 49 | $env:LLVM_HASH_TEMP = (git ls-remote https://github.com/llvm/llvm-project.git refs/heads/release/${{ matrix.clang-runtime}}.x ) 50 | $env:LLVM_HASH = $env:LLVM_HASH_TEMP -replace "\t","-" 51 | } 52 | 53 | echo "CLING_HASH=$env:CLING_HASH" 54 | echo "LLVM_HASH=$env:LLVM_HASH" 55 | 56 | echo "CLING_HASH=$env:CLING_HASH" >> $GITHUB_ENV 57 | echo "LLVM_HASH=$env:LLVM_HASH" >> $GITHUB_ENV 58 | -------------------------------------------------------------------------------- /unittests/CppInterOp/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include "CppInterOp/CppInterOp.h" 4 | 5 | #include "clang/AST/Decl.h" 6 | #include "clang/AST/DeclCXX.h" 7 | #include "clang/Basic/Version.h" 8 | #include "clang/Frontend/CompilerInstance.h" 9 | #include "clang/Interpreter/PartialTranslationUnit.h" 10 | #include "clang/Sema/Lookup.h" 11 | #include "clang/Sema/Sema.h" 12 | 13 | #include "llvm/TargetParser/Triple.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace clang; 20 | using namespace llvm; 21 | 22 | namespace TestUtils { 23 | TestConfig current_config; 24 | std::vector GetInterpreterArgs( 25 | const std::vector& base_args) { 26 | auto args = base_args; 27 | if (current_config.use_oop_jit) { 28 | args.push_back("--use-oop-jit"); 29 | } 30 | return args; 31 | } 32 | } 33 | 34 | void TestUtils::GetAllTopLevelDecls( 35 | const std::string& code, std::vector& Decls, 36 | bool filter_implicitGenerated /* = false */, 37 | const std::vector& interpreter_args /* = {} */) { 38 | Cpp::CreateInterpreter(interpreter_args); 39 | #ifdef CPPINTEROP_USE_CLING 40 | cling::Transaction *T = nullptr; 41 | Interp->declare(code, &T); 42 | 43 | for (auto DCI = T->decls_begin(), E = T->decls_end(); DCI != E; ++DCI) { 44 | if (DCI->m_Call != cling::Transaction::kCCIHandleTopLevelDecl) 45 | continue; 46 | for (Decl *D : DCI->m_DGR) { 47 | if (filter_implicitGenerated && D->isImplicit()) 48 | continue; 49 | Decls.push_back(D); 50 | } 51 | } 52 | #else 53 | PartialTranslationUnit *T = nullptr; 54 | Interp->process(code, /*Value*/nullptr, &T); 55 | for (auto *D : T->TUPart->decls()) { 56 | if (filter_implicitGenerated && D->isImplicit()) 57 | continue; 58 | Decls.push_back(D); 59 | } 60 | #endif 61 | } 62 | 63 | void TestUtils::GetAllSubDecls(Decl *D, std::vector& SubDecls, 64 | bool filter_implicitGenerated /* = false */) { 65 | if (!isa_and_nonnull(D)) 66 | return; 67 | DeclContext *DC = cast(D); 68 | for (auto *Di : DC->decls()) { 69 | if (filter_implicitGenerated && Di->isImplicit()) 70 | continue; 71 | SubDecls.push_back(Di); 72 | } 73 | } 74 | 75 | bool IsTargetX86() { 76 | #ifndef CPPINTEROP_USE_CLING 77 | llvm::Triple triple(Interp->getCompilerInstance()->getTargetOpts().Triple); 78 | #else 79 | llvm::Triple triple(Interp->getCI()->getTargetOpts().Triple); 80 | #endif 81 | return triple.isX86(); 82 | } 83 | 84 | const char* get_c_string(CXString string) { 85 | return static_cast(string.data); 86 | } 87 | 88 | void dispose_string(CXString string) { 89 | if (string.private_flags == 1 && string.data) 90 | free(const_cast(string.data)); 91 | } 92 | 93 | CXScope make_scope(const clang::Decl* D, const CXInterpreter I) { 94 | return {CXCursor_UnexposedDecl, 0, {D, nullptr, I}}; 95 | } 96 | -------------------------------------------------------------------------------- /cmake/CppInterOp/CppInterOpConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # This file allows users to call find_package(CppInterOp) and pick up our targets. 2 | 3 | # Compute the installation prefix from this CppInterOpConfig.cmake file location. 4 | get_filename_component(CPPINTEROP_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) 5 | get_filename_component(CPPINTEROP_INSTALL_PREFIX "${CPPINTEROP_INSTALL_PREFIX}" PATH) 6 | get_filename_component(CPPINTEROP_INSTALL_PREFIX "${CPPINTEROP_INSTALL_PREFIX}" PATH) 7 | get_filename_component(CPPINTEROP_INSTALL_PREFIX "${CPPINTEROP_INSTALL_PREFIX}" PATH) 8 | 9 | # Determine CMAKE_SHARED_LIBRARY_SUFFIX based on operating system 10 | include(CMakeSystemSpecificInformation) 11 | 12 | if(MSVC) 13 | set(shared_lib_dir bin) 14 | else() 15 | set(shared_lib_dir lib) 16 | endif() 17 | 18 | ### build/install workaround 19 | if (@BUILD_SHARED_LIBS@) 20 | set(_lib_suffix ${CMAKE_SHARED_LIBRARY_SUFFIX}) 21 | set(_lib_prefix ${CMAKE_SHARED_LIBRARY_PREFIX}) 22 | else() 23 | set(_lib_suffix ${CMAKE_STATIC_LIBRARY_SUFFIX}) 24 | set(_lib_prefix ${CMAKE_STATIC_LIBRARY_PREFIX}) 25 | endif() 26 | 27 | if (IS_DIRECTORY "${CPPINTEROP_INSTALL_PREFIX}/include") 28 | set(_include "${CPPINTEROP_INSTALL_PREFIX}/include") 29 | set(_lib "${CPPINTEROP_INSTALL_PREFIX}/${shared_lib_dir}/${_lib_prefix}clangCppInterOp${_lib_suffix}") 30 | set(_cmake "${CPPINTEROP_INSTALL_PREFIX}/${shared_lib_dir}/cmake/CppInterOp") 31 | else() 32 | set(_include "@CMAKE_CURRENT_SOURCE_DIR@/include") 33 | set(_lib "@CMAKE_CURRENT_BINARY_DIR@/${shared_lib_dir}/${_lib_prefix}clangCppInterOp${_lib_suffix}") 34 | set(_cmake "@CMAKE_CURRENT_BINARY_DIR@/${shared_lib_dir}/cmake/CppInterOp") 35 | endif() 36 | 37 | ### 38 | 39 | set(CPPINTEROP_EXPORTED_TARGETS "clangCppInterOp") 40 | set(CPPINTEROP_CMAKE_DIR "${_cmake}") 41 | set(CPPINTEROP_INCLUDE_DIRS "${_include}") 42 | set(CPPINTEROP_LIBRARIES "${_lib}") 43 | 44 | set(CPPINTEROP_LLVM_VERSION "@LLVM_VERSION@") 45 | set(CPPINTEROP_LLVM_VERSION_MAJOR "@LLVM_VERSION_MAJOR@") 46 | set(CPPINTEROP_LLVM_VERSION_MINOR "@LLVM_VERSION_MINOR@") 47 | set(CPPINTEROP_LLVM_VERSION_PATCH "@LLVM_VERSION_PATCH@") 48 | set(CPPINTEROP_LLVM_VERSION_SUFFIX "@LLVM_VERSION_SUFFIX@") 49 | set(CPPINTEROP_LLVM_PACKAGE_VERSION "@CPPINTEROP_LLVM_VERSION@") 50 | set(CPPINTEROP_VERSION "@CPPINTEROP_VERSION@") 51 | 52 | # Provide all our library targets to users. 53 | if (@BUILD_SHARED_LIBS@) 54 | add_library(clangCppInterOp SHARED IMPORTED) 55 | else() 56 | add_library(clangCppInterOp STATIC IMPORTED) 57 | endif() 58 | set_target_properties(clangCppInterOp PROPERTIES 59 | INTERFACE_INCLUDE_DIRECTORIES ${_include} 60 | IMPORTED_LOCATION ${_lib} 61 | ) 62 | if (MSVC) 63 | if (IS_DIRECTORY "${CPPINTEROP_INSTALL_PREFIX}/include") 64 | set(_static_lib "${CPPINTEROP_INSTALL_PREFIX}/lib/${_lib_prefix}clangCppInterOp.lib") 65 | else() 66 | set(_static_lib "@CMAKE_CURRENT_BINARY_DIR@/lib/${_lib_prefix}clangCppInterOp.lib") 67 | endif() 68 | 69 | set_target_properties(clangCppInterOp PROPERTIES 70 | IMPORTED_IMPLIB ${_static_lib} 71 | ) 72 | endif(MSVC) 73 | 74 | unset(_lib_prefix) 75 | unset(_lib_suffix) 76 | unset(_cmake) 77 | unset(_include) 78 | unset(_lib) 79 | -------------------------------------------------------------------------------- /.github/actions/Miscellaneous/Setup_Compiler/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Compiler' 2 | description: 'This PR sets up the compiler for the job' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | 8 | - name: Setup Compiler on MacOS 9 | if: runner.os == 'macOS' 10 | shell: bash 11 | run: | 12 | vers="${compiler#*-}" 13 | if [[ "${{ matrix.compiler }}" == *"gcc"* ]]; then 14 | brew install "gcc@$vers" 15 | echo "CC=gcc-${vers}" >> $GITHUB_ENV 16 | echo "CXX=g++-${vers}" >> $GITHUB_ENV 17 | else 18 | brew install llvm@15 19 | if [[ "$(uname -m)" == "x86_64" ]]; then 20 | echo "CC=/usr/local/opt/llvm@15/bin/clang" >> $GITHUB_ENV 21 | echo "CXX=/usr/local/opt/llvm@15/bin/clang++" >> $GITHUB_ENV 22 | else 23 | echo "CC=/opt/homebrew/opt/llvm@15/bin/clang" >> $GITHUB_ENV 24 | echo "CXX=/opt/homebrew/opt/llvm@15/bin/clang++" >> $GITHUB_ENV 25 | fi 26 | fi 27 | echo "SDKROOT=$(xcrun --sdk macosx --show-sdk-path)" >> $GITHUB_ENV 28 | env: 29 | compiler: ${{ matrix.compiler }} 30 | 31 | - name: Setup Compiler on Linux 32 | if: runner.os == 'Linux' 33 | shell: bash 34 | run: | 35 | # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html 36 | vers="${compiler#*-}" 37 | os_codename="`cat /etc/os-release | grep UBUNTU_CODENAME | cut -d = -f 2`" 38 | if [[ "${{ matrix.compiler }}" == *"gcc"* ]]; then 39 | sudo apt install -y gcc-${vers} g++-${vers} lld 40 | echo "CC=gcc-${vers}" >> $GITHUB_ENV 41 | echo "CXX=g++-${vers}" >> $GITHUB_ENV 42 | else 43 | if ! sudo apt install -y clang-${vers}; then 44 | curl https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 45 | echo "deb https://apt.llvm.org/${os_codename}/ llvm-toolchain-${os_codename}-${vers} main" | sudo tee -a /etc/apt/sources.list 46 | sudo apt-get update 47 | sudo apt-get install -y clang-${vers} 48 | fi 49 | echo "CC=clang-${vers}" >> $GITHUB_ENV 50 | echo "CXX=clang++-${vers}" >> $GITHUB_ENV 51 | fi 52 | env: 53 | compiler: ${{ matrix.compiler }} 54 | 55 | - name: Save Compiler on Windows Systems 56 | if: runner.os == 'Windows' 57 | shell: powershell 58 | run: | 59 | if ( "${{ matrix.compiler }}" -imatch "clang" ) 60 | { 61 | $ver="${{ matrix.compiler }}".split("-")[1] 62 | choco install llvm --version=$ver --no-progress -my 63 | clang --version 64 | # 65 | $env:CC="clang" 66 | $env:CXX="clang++" 67 | echo "CC=clang" >> $env:GITHUB_ENV 68 | echo "CXX=clang++" >> $env:GITHUB_ENV 69 | } 70 | elseif ( "${{ matrix.compiler }}" -imatch "msvc" ) 71 | { 72 | # MSVC is builtin in container image 73 | } 74 | else 75 | { 76 | echo "Unsupported compiler - fix YAML file" 77 | } 78 | -------------------------------------------------------------------------------- /lib/CppInterOp/exports.ld: -------------------------------------------------------------------------------- 1 | -Wl,--export=_ZN4llvm11raw_ostream14flush_nonemptyEv 2 | -Wl,--export=_ZN4llvm11raw_ostream16SetBufferAndModeEPcmNS0_10BufferKindE 3 | -Wl,--export=_ZN4llvm11raw_ostream5writeEPKcm 4 | -Wl,--export=_ZN4llvm11raw_ostreamD2Ev 5 | -Wl,--export=_ZN4llvm11raw_ostreamlsEm 6 | -Wl,--export=_ZN4llvm15SmallVectorBaseIjE8grow_podEPvmm 7 | -Wl,--export=_ZN4llvm15allocate_bufferEmm 8 | -Wl,--export=_ZN4llvm21logAllUnhandledErrorsENS_5ErrorERNS_11raw_ostreamENS_5TwineE 9 | -Wl,--export=_ZN4llvm25llvm_unreachable_internalEPKcS1_j 10 | -Wl,--export=_ZN4llvm3sys17RunningOnValgrindEv 11 | -Wl,--export=_ZN4llvm4dbgsEv 12 | -Wl,--export=_ZN4llvm4errsEv 13 | -Wl,--export=_ZN4llvm5APIntC1EjNS_8ArrayRefIyEE 14 | -Wl,--export=_ZN5clang10ASTContext19createMangleContextEPKNS_10TargetInfoE 15 | -Wl,--export=_ZN5clang11DeclContext7classofEPKNS_4DeclE 16 | -Wl,--export=_ZN5clang11Interpreter19getCompilerInstanceEv 17 | -Wl,--export=_ZN5clang11Interpreter5ParseEN4llvm9StringRefE 18 | -Wl,--export=_ZN5clang11Interpreter6createENSt3__210unique_ptrINS_16CompilerInstanceENS1_14default_deleteIS3_EEEE 19 | -Wl,--export=_ZN5clang11Interpreter7ExecuteERNS_22PartialTranslationUnitE 20 | -Wl,--export=_ZN5clang13MangleContext10mangleNameENS_10GlobalDeclERN4llvm11raw_ostreamE 21 | -Wl,--export=_ZN5clang13MangleContext20shouldMangleDeclNameEPKNS_9NamedDeclE 22 | -Wl,--export=_ZN5clang26IncrementalCompilerBuilder9CreateCppEv 23 | -Wl,--export=_ZN5clang4Decl17castToDeclContextEPKS0_ 24 | -Wl,--export=_ZNK4llvm5APInt13compareSignedERKS0_ 25 | -Wl,--export=_ZNK4llvm5APInt4sextEj 26 | -Wl,--export=_ZNK4llvm5APInt4zextEj 27 | -Wl,--export=_ZNK4llvm5APInt7compareERKS0_ 28 | -Wl,--export=_ZNK4llvm5Error19fatalUncheckedErrorEv 29 | -Wl,--export=_ZNK5clang10ASTContext22getLValueReferenceTypeENS_8QualTypeEb 30 | -Wl,--export=_ZNK5clang11DeclContext11decls_beginEv 31 | -Wl,--export=_ZNK5clang12FunctionDecl29getTemplateSpecializationArgsEv 32 | -Wl,--export=_ZNK5clang12FunctionDecl31getTemplateInstantiationPatternEb 33 | -Wl,--export=_ZNK5clang29VarTemplateSpecializationDecl22getSpecializedTemplateEv 34 | -Wl,--export=_ZNK5clang4Decl13getASTContextEv 35 | -Wl,--export=_ZNK5clang4Decl15hasDefiningAttrEv 36 | -Wl,--export=_ZNK5clang4Decl8getAttrsEv 37 | -Wl,--export=_ZNK5clang4Type27getUnqualifiedDesugaredTypeEv 38 | -Wl,--export=_ZNK5clang7TagType7getDeclEv 39 | -Wl,--export=_ZNK5clang7VarDecl28isThisDeclarationADefinitionERNS_10ASTContextE 40 | -Wl,--export=_ZTVN4llvm18raw_string_ostreamE 41 | -Wl,--export=_ZN4llvm13StringMapImpl11RehashTableEj 42 | -Wl,--export=_ZN4llvm13StringMapImpl15LookupBucketForENS_9StringRefEj 43 | -Wl,--export=_ZN4llvm13StringMapImpl4hashENS_9StringRefE 44 | -Wl,--export=_ZNK5clang10ASTContext14getComplexTypeENS_8QualTypeE 45 | -Wl,--export=_ZNK5clang10ASTContext19getTypeDeclTypeSlowEPKNS_8TypeDeclE 46 | -Wl,--export=_ZNK5clang10RecordDecl19isInjectedClassNameEv 47 | -Wl,--export=_ZNK5clang11DeclContext6lookupENS_15DeclarationNameE 48 | -Wl,--export=_ZNK5clang17ClassTemplateDecl18getSpecializationsEv 49 | -Wl,--export=_ZNK5clang4Sema15getStdNamespaceEv 50 | -Wl,--export=_ZNK5clang4Type14isFloatingTypeEv 51 | -Wl,--export=_ZNK5clang12FunctionDecl12getNumParamsEv 52 | -Wl,--export=__clang_Interpreter_SetValueNoAlloc 53 | -Wl,--export=__clang_Interpreter_SetValueWithAlloc 54 | -Wl,--export=_ZN4llvm15SmallVectorBaseIjE8set_sizeEm -------------------------------------------------------------------------------- /unittests/CppInterOp/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPINTEROP_UNITTESTS_LIBCPPINTEROP_UTILS_H 2 | #define CPPINTEROP_UNITTESTS_LIBCPPINTEROP_UTILS_H 3 | 4 | #include "../../lib/CppInterOp/Compatibility.h" 5 | 6 | #include "clang-c/CXCppInterOp.h" 7 | #include "clang-c/CXString.h" 8 | #include "CppInterOp/CppInterOp.h" 9 | 10 | #include "llvm/Support/Valgrind.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "gtest/gtest.h" 17 | 18 | using namespace clang; 19 | using namespace llvm; 20 | 21 | namespace clang { 22 | class Decl; 23 | } 24 | #define Interp (static_cast(Cpp::GetInterpreter())) 25 | namespace TestUtils { 26 | 27 | struct TestConfig { 28 | std::string name; 29 | bool use_oop_jit; 30 | 31 | TestConfig(bool oop_jit, const std::string& n) 32 | : name(std::move(n)), use_oop_jit(oop_jit) {} 33 | 34 | TestConfig() 35 | : name("InProcessJIT"), use_oop_jit(false) {} 36 | }; 37 | 38 | extern TestConfig current_config; 39 | 40 | // Helper to get interpreter args with current config 41 | std::vector 42 | GetInterpreterArgs(const std::vector& base_args = {}); 43 | 44 | void GetAllTopLevelDecls(const std::string& code, 45 | std::vector& Decls, 46 | bool filter_implicitGenerated = false, 47 | const std::vector& interpreter_args = {}); 48 | void GetAllSubDecls(clang::Decl* D, std::vector& SubDecls, 49 | bool filter_implicitGenerated = false); 50 | } // end namespace TestUtils 51 | 52 | const char* get_c_string(CXString string); 53 | 54 | void dispose_string(CXString string); 55 | 56 | CXScope make_scope(const clang::Decl* D, const CXInterpreter I); 57 | 58 | bool IsTargetX86(); 59 | 60 | // Define type tags for each configuration 61 | struct InProcessJITConfig { 62 | static constexpr bool isOutOfProcess = false; 63 | static constexpr const char* name = "InProcessJIT"; 64 | }; 65 | 66 | #ifdef LLVM_BUILT_WITH_OOP_JIT 67 | struct OutOfProcessJITConfig { 68 | static constexpr bool isOutOfProcess = true; 69 | static constexpr const char* name = "OutOfProcessJIT"; 70 | }; 71 | #endif 72 | 73 | // Define typed test fixture 74 | template 75 | class CppInterOpTest : public ::testing::Test { 76 | protected: 77 | void SetUp() override { 78 | TestUtils::current_config = 79 | TestUtils::TestConfig{Config::isOutOfProcess, Config::name}; 80 | } 81 | 82 | public: 83 | static TInterp_t CreateInterpreter(const std::vector& Args = {}, 84 | const std::vector& GpuArgs = {}) { 85 | auto mergedArgs = TestUtils::GetInterpreterArgs(Args); 86 | return Cpp::CreateInterpreter(mergedArgs, GpuArgs); 87 | } 88 | 89 | bool IsOutOfProcess() { 90 | return Config::isOutOfProcess; 91 | } 92 | }; 93 | 94 | struct JITConfigNameGenerator { 95 | template 96 | static std::string GetName(int) { 97 | return T::name; 98 | } 99 | }; 100 | 101 | #ifdef LLVM_BUILT_WITH_OOP_JIT 102 | using CppInterOpTestTypes = ::testing::Types; 103 | #else 104 | using CppInterOpTestTypes = ::testing::Types; 105 | #endif 106 | 107 | TYPED_TEST_SUITE(CppInterOpTest, CppInterOpTestTypes, JITConfigNameGenerator); 108 | 109 | 110 | #endif // CPPINTEROP_UNITTESTS_LIBCPPINTEROP_UTILS_H 111 | -------------------------------------------------------------------------------- /unittests/CppInterOp/DynamicLibraryManagerTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include "CppInterOp/CppInterOp.h" 3 | 4 | #include "clang/Basic/Version.h" 5 | 6 | #include "llvm/Support/FileSystem.h" 7 | #include "llvm/Support/Path.h" 8 | 9 | #include "gtest/gtest.h" 10 | 11 | // This function isn't referenced outside its translation unit, but it 12 | // can't use the "static" keyword because its address is used for 13 | // GetMainExecutable (since some platforms don't support taking the 14 | // address of main, and some platforms can't implement GetMainExecutable 15 | // without being given the address of a function in the main executable). 16 | std::string GetExecutablePath(const char* Argv0) { 17 | // This just needs to be some symbol in the binary; C++ doesn't 18 | // allow taking the address of ::main however. 19 | void* MainAddr = (void*)intptr_t(GetExecutablePath); 20 | return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); 21 | } 22 | 23 | TYPED_TEST(CppInterOpTest, DynamicLibraryManagerTestSanity) { 24 | #ifdef EMSCRIPTEN 25 | GTEST_SKIP() << "Test fails for Emscipten builds"; 26 | #endif 27 | 28 | #if CLANG_VERSION_MAJOR == 18 && defined(CPPINTEROP_USE_CLING) && \ 29 | defined(_WIN32) 30 | GTEST_SKIP() << "Test fails with Cling on Windows"; 31 | #endif 32 | if (TypeParam::isOutOfProcess) 33 | GTEST_SKIP() << "Test fails for OOP JIT builds"; 34 | 35 | EXPECT_TRUE(TestFixture::CreateInterpreter()); 36 | EXPECT_FALSE(Cpp::GetFunctionAddress("ret_zero")); 37 | 38 | std::string BinaryPath = GetExecutablePath(/*Argv0=*/nullptr); 39 | llvm::StringRef Dir = llvm::sys::path::parent_path(BinaryPath); 40 | Cpp::AddSearchPath(Dir.str().c_str()); 41 | 42 | // FIXME: dlsym on mach-o takes the C-level name, however, the macho-o format 43 | // adds an additional underscore (_) prefix to the lowered names. Figure out 44 | // how to harmonize that API. 45 | #ifdef __APPLE__ 46 | std::string PathToTestSharedLib = 47 | Cpp::SearchLibrariesForSymbol("_ret_zero", /*system_search=*/false); 48 | #else 49 | std::string PathToTestSharedLib = 50 | Cpp::SearchLibrariesForSymbol("ret_zero", /*system_search=*/false); 51 | #endif // __APPLE__ 52 | 53 | EXPECT_STRNE("", PathToTestSharedLib.c_str()) 54 | << "Cannot find: '" << PathToTestSharedLib << "' in '" << Dir.str() 55 | << "'"; 56 | 57 | EXPECT_TRUE(Cpp::LoadLibrary(PathToTestSharedLib.c_str())); 58 | // Force ExecutionEngine to be created. 59 | Cpp::Process(""); 60 | // FIXME: Conda returns false to run this code on osx. 61 | #ifndef __APPLE__ 62 | EXPECT_TRUE(Cpp::GetFunctionAddress("ret_zero")); 63 | #endif //__APPLE__ 64 | 65 | Cpp::UnloadLibrary("TestSharedLib"); 66 | // We have no reliable way to check if it was unloaded because posix does not 67 | // require the library to be actually unloaded but just the handle to be 68 | // invalidated... 69 | // EXPECT_FALSE(Cpp::GetFunctionAddress("ret_zero")); 70 | } 71 | 72 | TYPED_TEST(CppInterOpTest, DynamicLibraryManagerTestBasicSymbolLookup) { 73 | #ifndef EMSCRIPTEN 74 | GTEST_SKIP() << "This test is only intended for Emscripten builds."; 75 | #else 76 | #if CLANG_VERSION_MAJOR < 20 77 | GTEST_SKIP() << "Support for loading shared libraries was added in LLVM 20."; 78 | #endif 79 | #endif 80 | if (TypeParam::isOutOfProcess) 81 | GTEST_SKIP() << "Test fails for OOP JIT builds"; 82 | 83 | ASSERT_TRUE(TestFixture::CreateInterpreter()); 84 | EXPECT_FALSE(Cpp::GetFunctionAddress("ret_zero")); 85 | 86 | // Load the library manually. Use known preload path (MEMFS path) 87 | const char* wasmLibPath = "libTestSharedLib.so"; // Preloaded path in MEMFS 88 | EXPECT_TRUE(Cpp::LoadLibrary(wasmLibPath, false)); 89 | 90 | Cpp::Process(""); 91 | 92 | void* Addr = Cpp::GetFunctionAddress("ret_zero"); 93 | EXPECT_NE(Addr, nullptr) << "Symbol 'ret_zero' not found after dlopen."; 94 | 95 | using RetZeroFn = int (*)(); 96 | auto Fn = reinterpret_cast(Addr); 97 | EXPECT_EQ(Fn(), 0); 98 | } 99 | -------------------------------------------------------------------------------- /unittests/CppInterOp/JitTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include "CppInterOp/CppInterOp.h" 4 | 5 | #include "gtest/gtest.h" 6 | 7 | using namespace TestUtils; 8 | 9 | static int printf_jit(const char* format, ...) { 10 | llvm::errs() << "printf_jit called!\n"; 11 | return 0; 12 | } 13 | 14 | TYPED_TEST(CppInterOpTest, JitTestInsertOrReplaceJitSymbol) { 15 | #ifdef EMSCRIPTEN 16 | GTEST_SKIP() << "Test fails for Emscipten builds"; 17 | #endif 18 | if (llvm::sys::RunningOnValgrind()) 19 | GTEST_SKIP() << "XFAIL due to Valgrind report"; 20 | #ifdef _WIN32 21 | GTEST_SKIP() << "Disabled on Windows. Needs fixing."; 22 | #endif 23 | if (TypeParam::isOutOfProcess) 24 | GTEST_SKIP() << "Test fails for OOP JIT builds"; 25 | std::vector Decls; 26 | std::string code = R"( 27 | extern "C" int printf(const char*,...); 28 | )"; 29 | 30 | GetAllTopLevelDecls(code, Decls); 31 | 32 | EXPECT_FALSE(Cpp::InsertOrReplaceJitSymbol("printf", (uint64_t)&printf_jit)); 33 | 34 | testing::internal::CaptureStderr(); 35 | Cpp::Process("printf(\"Blah\");"); 36 | std::string cerrs = testing::internal::GetCapturedStderr(); 37 | EXPECT_STREQ(cerrs.c_str(), "printf_jit called!\n"); 38 | 39 | EXPECT_TRUE( 40 | Cpp::InsertOrReplaceJitSymbol("non_existent", (uint64_t)&printf_jit)); 41 | EXPECT_TRUE(Cpp::InsertOrReplaceJitSymbol("non_existent", 0)); 42 | } 43 | 44 | TYPED_TEST(CppInterOpTest, JitTestStreamRedirect) { 45 | if (TypeParam::isOutOfProcess) 46 | GTEST_SKIP() << "Test fails for OOP JIT builds"; 47 | // printf and etc are fine here. 48 | // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) 49 | Cpp::BeginStdStreamCapture(Cpp::kStdOut); 50 | Cpp::BeginStdStreamCapture(Cpp::kStdErr); 51 | printf("StdOut is captured\n"); 52 | fprintf(stderr, "StdErr is captured\n"); 53 | std::cout << "Out captured" 54 | << "\n"; 55 | std::cerr << "Err captured" 56 | << "\n"; 57 | std::string CapturedStringErr = Cpp::EndStdStreamCapture(); 58 | std::string CapturedStringOut = Cpp::EndStdStreamCapture(); 59 | EXPECT_STREQ(CapturedStringOut.c_str(), "StdOut is captured\nOut captured\n"); 60 | EXPECT_STREQ(CapturedStringErr.c_str(), "StdErr is captured\nErr captured\n"); 61 | 62 | testing::internal::CaptureStdout(); 63 | std::cout << "Out" 64 | << "\n"; 65 | printf("StdOut\n"); 66 | std::string outs = testing::internal::GetCapturedStdout(); 67 | EXPECT_STREQ(outs.c_str(), "Out\nStdOut\n"); 68 | 69 | testing::internal::CaptureStderr(); 70 | std::cerr << "Err" 71 | << "\n"; 72 | fprintf(stderr, "StdErr\n"); 73 | std::string cerrs = testing::internal::GetCapturedStderr(); 74 | EXPECT_STREQ(cerrs.c_str(), "Err\nStdErr\n"); 75 | // NOLINTEND(cppcoreguidelines-pro-type-vararg) 76 | } 77 | 78 | TYPED_TEST(CppInterOpTest, JitTestStreamRedirectJIT) { 79 | #ifdef EMSCRIPTEN 80 | GTEST_SKIP() << "Test fails for Emscipten builds"; 81 | #endif 82 | if (llvm::sys::RunningOnValgrind()) 83 | GTEST_SKIP() << "XFAIL due to Valgrind report"; 84 | #ifdef _WIN32 85 | GTEST_SKIP() << "Disabled on Windows. Needs fixing."; 86 | #endif 87 | #ifdef CPPINTEROP_USE_CLING 88 | GTEST_SKIP() << "Test fails for cling builds"; 89 | #endif 90 | TestFixture::CreateInterpreter(); 91 | Interp->process(R"( 92 | #include 93 | printf("%s\n", "Hello World"); 94 | fprintf(stderr, "%s\n", "Hello Err"); 95 | fflush(nullptr); 96 | )"); 97 | 98 | Cpp::BeginStdStreamCapture(Cpp::kStdOut); 99 | Cpp::BeginStdStreamCapture(Cpp::kStdErr); 100 | Interp->process(R"( 101 | #include 102 | printf("%s\n", "Hello World"); 103 | fprintf(stderr, "%s\n", "Hello Err"); 104 | fflush(nullptr); 105 | )"); 106 | std::string CapturedStringErr = Cpp::EndStdStreamCapture(); 107 | std::string CapturedStringOut = Cpp::EndStdStreamCapture(); 108 | 109 | EXPECT_STREQ(CapturedStringOut.c_str(), "Hello World\n"); 110 | EXPECT_STREQ(CapturedStringErr.c_str(), "Hello Err\n"); 111 | } 112 | 113 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Thank you for investing your time in contributing to our project! There are 4 | numbers of ways to contribute to the project and we appreciate all of them. If 5 | you like the project please give CppInterOp a star. 6 | 7 | Any contribution to open source makes a difference! 8 | 9 | ## Are you new to open source, git or GitHub? 10 | 11 | To get an overview of the project, read the [README](README.md). Here are some 12 | resources to help you get started with open source contributions: 13 | 14 | - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) 15 | - [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) 16 | - [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) 17 | - [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) 18 | 19 | ## Are you a contributor looking for a challenging summer project? 20 | 21 | Various opportunities such as information about google summer of code is 22 | generally published on the [Compiler Research Open Projects page](https://compiler-research.org/open_projects). 23 | If you have used CppInterOp and you have particular project proposal please reach out. 24 | 25 | ## Ways to contribute 26 | 27 | ### Submit a bug report 28 | 29 | If something does not seem right [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments) 30 | in [CppInterOps issue tracker](https://github.com/compiler-research/CppInterOp/issues). If a related issue doesn't exist, you can open a 31 | new issue using a relevant [issue form](https://github.com/compiler-research/CppInterOp/issues/new). 32 | 33 | ### Good first issues 34 | 35 | Some issues have been marked as ["good first issues"](https://github.com/compiler-research/CppInterOp/labels/good%20first%20issue). 36 | These are intended to be a good place to start contributing. 37 | 38 | ### Write documentation 39 | 40 | Documentation is critical for any open source project, especially for complex 41 | projects such as CppInterOp. We have our documentation in the repository which is then 42 | rendered in the [CppInterOp.readthedocs](https://cppinterop.readthedocs.io/en/latest/) website. 43 | Documentation modifications happen by proposing a pull request. 44 | 45 | ## Creating a successful pull request 46 | 47 | To propose a code modification we use the pull requests. Pull requests which 48 | review quickly and successfully share several common traits: 49 | 50 | - Sharp -- intends to fix a concrete problem. Usually the pull request addresses 51 | an already opened issue; 52 | - Atomic -- has one or more commits that can be reverted without any unwanted 53 | side effects or regressions, aside from what you’d expect based on its 54 | message. [More on atomic commits in git](https://www.aleksandrhovhannisyan.com/blog/atomic-git-commits/). 55 | - Descriptive -- has a good description in what is being solved. This 56 | information is usually published as part of the pull request description and 57 | as part of the commit message. Writing good commit messages are critical. More 58 | [here](https://github.blog/2022-06-30-write-better-commits-build-better-projects/) 59 | and [here](https://cbea.ms/git-commit/). If your pull request fixes an existing 60 | issue from the bug tracker make sure that the commit log and the pull request 61 | description mentions `Fixes: #`. That will link both and will 62 | close the issue automatically upon merging. 63 | - Tested -- has a set of tests making sure that the issue will not resurface 64 | without a notice. Usually the codecov bots annotate the code paths that are 65 | not tested in the pull request after being run. 66 | - Documented -- has good amount of code comment. The test cases are also a good 67 | source of documentation. [Here](https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/) 68 | is a guideline about how write good code comments. [Here](https://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered) 69 | are examples of what *not* to write as a code comment. 70 | 71 | ### Developer Documentation 72 | 73 | We have documented several useful hints that usually help when addressing issues 74 | as they come during development time in our [developer documentation](https://cppinterop.readthedocs.io/en/latest/InstallationAndUsage.html). 75 | -------------------------------------------------------------------------------- /lib/CppInterOp/Paths.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------*- C++ -*- 2 | // CLING - the C++ LLVM-based InterpreterG :) 3 | // author: 4 | // 5 | // This file is dual-licensed: you can choose to license it under the University 6 | // of Illinois Open Source License or the GNU Lesser General Public License. See 7 | // LICENSE.TXT for details. 8 | //------------------------------------------------------------------------------ 9 | 10 | #ifndef CPPINTEROP_UTILS_PATHS_H 11 | #define CPPINTEROP_UTILS_PATHS_H 12 | 13 | #include "llvm/ADT/SmallVector.h" 14 | #include "llvm/ADT/StringRef.h" 15 | #include 16 | #include 17 | 18 | namespace llvm { 19 | class raw_ostream; 20 | } 21 | 22 | namespace clang { 23 | class HeaderSearchOptions; 24 | class FileManager; 25 | } // namespace clang 26 | 27 | namespace Cpp { 28 | namespace utils { 29 | 30 | namespace platform { 31 | ///\brief Platform specific delimiter for splitting environment variables. 32 | /// ':' on Unix, and ';' on Windows 33 | extern const char* const kEnvDelim; 34 | 35 | /// 36 | bool GetSystemLibraryPaths(llvm::SmallVectorImpl& Paths); 37 | 38 | ///\brief Returns a normalized version of the given Path 39 | /// 40 | std::string NormalizePath(const std::string& Path); 41 | 42 | ///\brief Open a handle to a shared library. On Unix the lib is opened with 43 | /// RTLD_LAZY|RTLD_GLOBAL flags. 44 | /// 45 | /// \param [in] Path - Library to open 46 | /// \param [out] Err - Write errors to this string when given 47 | /// 48 | /// \returns the library handle 49 | /// 50 | void* DLOpen(const std::string& Path, std::string* Err = nullptr); 51 | 52 | ///\brief Close a handle to a shared library. 53 | /// 54 | /// \param [in] Lib - Handle to library from previous call to DLOpen 55 | /// \param [out] Err - Write errors to this string when given 56 | /// 57 | /// \returns the library handle 58 | /// 59 | void DLClose(void* Lib, std::string* Err = nullptr); 60 | } // namespace platform 61 | 62 | enum SplitMode { 63 | kPruneNonExistent, ///< Don't add non-existent paths into output 64 | kFailNonExistent, ///< Fail on any non-existent paths 65 | kAllowNonExistent ///< Add all paths whether they exist or not 66 | }; 67 | 68 | ///\brief Collect the constituent paths from a PATH string. 69 | /// /bin:/usr/bin:/usr/local/bin -> {/bin, /usr/bin, /usr/local/bin} 70 | /// 71 | /// All paths returned existed at the time of the call 72 | /// \param [in] PathStr - The PATH string to be split 73 | /// \param [out] Paths - All the paths in the string that exist 74 | /// \param [in] Mode - If any path doesn't exist stop and return false 75 | /// \param [in] Delim - The delimiter to use 76 | /// \param [in] Verbose - Whether to print out details as 'clang -v' would 77 | /// 78 | /// \return true if all paths existed, otherwise false 79 | /// 80 | bool SplitPaths(llvm::StringRef PathStr, 81 | llvm::SmallVectorImpl& Paths, 82 | SplitMode Mode = kPruneNonExistent, 83 | llvm::StringRef Delim = Cpp::utils::platform::kEnvDelim, 84 | bool Verbose = false); 85 | 86 | ///\brief Adds multiple include paths separated by a delimiter into the 87 | /// given HeaderSearchOptions. This adds the paths but does no further 88 | /// processing. See Interpreter::AddIncludePaths or CIFactory::createCI 89 | /// for examples of what needs to be done once the paths have been added. 90 | /// 91 | ///\param[in] PathStr - Path(s) 92 | ///\param[in] Opts - HeaderSearchOptions to add paths into 93 | ///\param[in] Delim - Delimiter to separate paths or NULL if a single path 94 | /// 95 | void AddIncludePaths(llvm::StringRef PathStr, clang::HeaderSearchOptions& HOpts, 96 | const char* Delim = Cpp::utils::platform::kEnvDelim); 97 | 98 | ///\brief Write to cling::errs that directory does not exist in a format 99 | /// matching what 'clang -v' would do 100 | /// 101 | void LogNonExistentDirectory(llvm::StringRef Path); 102 | 103 | ///\brief Copies the current include paths into the HeaderSearchOptions. 104 | /// 105 | ///\param[in] Opts - HeaderSearchOptions to read from 106 | ///\param[out] Paths - Vector to output elements into 107 | ///\param[in] WithSystem - if true, incpaths will also contain system 108 | /// include paths (framework, STL etc). 109 | ///\param[in] WithFlags - if true, each element in incpaths will be prefixed 110 | /// with a "-I" or similar, and some entries of incpaths will signal 111 | /// a new include path region (e.g. "-cxx-isystem"). Also, flags 112 | /// defining header search behavior will be included in incpaths, e.g. 113 | /// "-nostdinc". 114 | /// 115 | void CopyIncludePaths(const clang::HeaderSearchOptions& Opts, 116 | llvm::SmallVectorImpl& Paths, 117 | bool WithSystem, bool WithFlags); 118 | 119 | } // namespace utils 120 | } // namespace Cpp 121 | 122 | #endif // CPPINTEROP_UTILS_PATHS_H 123 | -------------------------------------------------------------------------------- /cmake/modules/GoogleTest.cmake: -------------------------------------------------------------------------------- 1 | set(_gtest_byproduct_binary_dir 2 | ${CMAKE_BINARY_DIR}/downloads/googletest-prefix/src/googletest-build) 3 | set(_gtest_byproducts 4 | ${_gtest_byproduct_binary_dir}/lib/libgtest.a 5 | ${_gtest_byproduct_binary_dir}/lib/libgtest_main.a 6 | ${_gtest_byproduct_binary_dir}/lib/libgmock.a 7 | ${_gtest_byproduct_binary_dir}/lib/libgmock_main.a 8 | ) 9 | 10 | if(WIN32) 11 | set(EXTRA_GTEST_OPTS 12 | -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=${_gtest_byproduct_binary_dir}/lib/ 13 | -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL:PATH=${_gtest_byproduct_binary_dir}/lib/ 14 | -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=${_gtest_byproduct_binary_dir}/lib/ 15 | -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO:PATH=${_gtest_byproduct_binary_dir}/lib/ 16 | -Dgtest_force_shared_crt=ON) 17 | elseif(APPLE) 18 | set(EXTRA_GTEST_OPTS -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}) 19 | endif() 20 | 21 | include(ExternalProject) 22 | if (EMSCRIPTEN) 23 | set(config_cmd emcmake${EMCC_SUFFIX} cmake) 24 | if(CMAKE_GENERATOR STREQUAL "Ninja") 25 | set(build_cmd emmake${EMCC_SUFFIX} ninja) 26 | else() 27 | set(build_cmd emmake${EMCC_SUFFIX} make) 28 | endif() 29 | else() 30 | set(config_cmd ${CMAKE_COMMAND}) 31 | set(build_cmd ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/unittests/googletest-prefix/src/googletest-build/ --config $) 32 | endif() 33 | 34 | ExternalProject_Add( 35 | googletest 36 | GIT_REPOSITORY https://github.com/google/googletest.git 37 | GIT_SHALLOW FALSE 38 | GIT_TAG fa8438ae6b70c57010177de47a9f13d7041a6328 39 | UPDATE_COMMAND "" 40 | # # Force separate output paths for debug and release builds to allow easy 41 | # # identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands 42 | # CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs 43 | # -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs 44 | # -Dgtest_force_shared_crt=ON 45 | CONFIGURE_COMMAND ${config_cmd} -G ${CMAKE_GENERATOR} 46 | -S ${CMAKE_BINARY_DIR}/unittests/googletest-prefix/src/googletest/ 47 | -B ${CMAKE_BINARY_DIR}/unittests/googletest-prefix/src/googletest-build/ 48 | -DCMAKE_BUILD_TYPE=$ 49 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 50 | -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} 51 | -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} 52 | -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} 53 | -DCMAKE_AR=${CMAKE_AR} 54 | -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} 55 | ${EXTRA_GTEST_OPTS} 56 | BUILD_COMMAND ${build_cmd} 57 | # Disable install step 58 | INSTALL_COMMAND "" 59 | BUILD_BYPRODUCTS ${_gtest_byproducts} 60 | # Wrap download, configure and build steps in a script to log output 61 | LOG_DOWNLOAD ON 62 | LOG_CONFIGURE ON 63 | LOG_BUILD ON 64 | TIMEOUT 600 65 | ) 66 | 67 | # Specify include dirs for gtest and gmock 68 | ExternalProject_Get_Property(googletest source_dir) 69 | set(GTEST_INCLUDE_DIR ${source_dir}/googletest/include) 70 | set(GMOCK_INCLUDE_DIR ${source_dir}/googlemock/include) 71 | # Create the directories. Prevents bug https://gitlab.kitware.com/cmake/cmake/issues/15052 72 | file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) 73 | 74 | # Libraries 75 | ExternalProject_Get_Property(googletest binary_dir) 76 | if(WIN32) 77 | set(_G_LIBRARY_PATH ${_gtest_byproduct_binary_dir}/lib) 78 | else() 79 | set(_G_LIBRARY_PATH ${binary_dir}/lib/) 80 | endif() 81 | 82 | # Use gmock_main instead of gtest_main because it initializes gtest as well. 83 | # Note: The libraries are listed in reverse order of their dependencies. 84 | foreach(lib gtest gtest_main gmock gmock_main) 85 | add_library(${lib} IMPORTED STATIC GLOBAL) 86 | set_target_properties(${lib} PROPERTIES 87 | IMPORTED_LOCATION "${_G_LIBRARY_PATH}${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX}" 88 | INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIRS}" 89 | ) 90 | add_dependencies(${lib} googletest) 91 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND 92 | ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER_EQUAL 9) 93 | target_compile_options(${lib} INTERFACE -Wno-deprecated-copy) 94 | endif() 95 | endforeach() 96 | target_include_directories(gtest INTERFACE ${GTEST_INCLUDE_DIR}) 97 | target_include_directories(gmock INTERFACE ${GMOCK_INCLUDE_DIR}) 98 | 99 | set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}) 100 | set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}) 101 | set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}) 102 | set_property(TARGET gmock_main PROPERTY IMPORTED_LOCATION ${_G_LIBRARY_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}) 103 | -------------------------------------------------------------------------------- /lib/CppInterOp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(EMSCRIPTEN) 2 | set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE) 3 | set(LLVM_LINK_COMPONENTS "") 4 | else() 5 | set(LLVM_LINK_COMPONENTS 6 | ${LLVM_TARGETS_TO_BUILD} 7 | BinaryFormat 8 | Core 9 | Object 10 | OrcJit 11 | Support 12 | ) 13 | # FIXME: Investigate why this needs to be conditionally included. 14 | if ("LLVMFrontendDriver" IN_LIST LLVM_AVAILABLE_LIBS) 15 | list(APPEND LLVM_LINK_COMPONENTS FrontendDriver) 16 | endif() 17 | if ("LLVMOrcDebugging" IN_LIST LLVM_AVAILABLE_LIBS) 18 | list(APPEND LLVM_LINK_COMPONENTS OrcDebugging) 19 | endif() 20 | endif() 21 | set(DLM 22 | DynamicLibraryManager.cpp 23 | DynamicLibraryManagerSymbol.cpp 24 | Paths.cpp 25 | ) 26 | if (CPPINTEROP_USE_CLING) 27 | set(LLVM_OPTIONAL_SOURCES ${LLVM_OPTIONAL_SOURCES} ${DLM}) 28 | set(DLM) 29 | endif(CPPINTEROP_USE_CLING) 30 | if (CPPINTEROP_USE_REPL) 31 | #Use DML optional sources 32 | endif(CPPINTEROP_USE_REPL) 33 | 34 | if (CPPINTEROP_USE_CLING) 35 | set(cling_clang_interp clingInterpreter) 36 | endif() 37 | if (CPPINTEROP_USE_REPL) 38 | set(cling_clang_interp clangInterpreter) 39 | endif() 40 | 41 | if(EMSCRIPTEN) 42 | set(link_libs 43 | ${cling_clang_interp} 44 | ) 45 | else() 46 | set(link_libs 47 | ${cling_clang_interp} 48 | clangAST 49 | clangBasic 50 | clangFrontend 51 | clangLex 52 | clangSema 53 | ) 54 | endif() 55 | 56 | if(NOT WIN32 AND NOT EMSCRIPTEN) 57 | list(APPEND link_libs dl) 58 | endif() 59 | 60 | # Get rid of libLLVM-X.so which is appended to the list of static libraries. 61 | if (LLVM_LINK_LLVM_DYLIB) 62 | set(new_libs ${link_libs}) 63 | set(libs ${new_libs}) 64 | while(NOT "${new_libs}" STREQUAL "") 65 | foreach(lib ${new_libs}) 66 | if(TARGET ${lib}) 67 | get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES) 68 | if (NOT transitive_libs) 69 | continue() 70 | endif() 71 | foreach(transitive_lib ${transitive_libs}) 72 | if(NOT TARGET ${transitive_lib}) 73 | continue() 74 | endif() 75 | get_target_property(lib_type ${transitive_lib} TYPE) 76 | if("${lib_type}" STREQUAL "STATIC_LIBRARY") 77 | list(APPEND static_transitive_libs ${transitive_lib}) 78 | else() 79 | # Filter our libLLVM.so and friends. 80 | continue() 81 | endif() 82 | if(NOT ${transitive_lib} IN_LIST libs) 83 | list(APPEND newer_libs ${transitive_lib}) 84 | list(APPEND libs ${transitive_lib}) 85 | endif() 86 | endforeach(transitive_lib) 87 | # Update the target properties with the list of only static libraries. 88 | set_target_properties(${lib} PROPERTIES INTERFACE_LINK_LIBRARIES "${static_transitive_libs}") 89 | set(static_transitive_libs "") 90 | endif() 91 | endforeach(lib) 92 | set(new_libs ${newer_libs}) 93 | set(newer_libs "") 94 | endwhile() 95 | # We just got rid of the libLLVM.so and other components shipped as shared 96 | # libraries, we need to make up for the missing dependency. 97 | list(APPEND LLVM_LINK_COMPONENTS 98 | Coverage 99 | FrontendHLSL 100 | LTO 101 | ) 102 | # We will need to append the missing dependencies to pull in the right 103 | # LLVM library dependencies. 104 | list(APPEND link_libs 105 | clangCodeGen 106 | clangStaticAnalyzerCore 107 | ) 108 | endif(LLVM_LINK_LLVM_DYLIB) 109 | add_llvm_library(clangCppInterOp 110 | DISABLE_LLVM_LINK_LLVM_DYLIB 111 | CppInterOp.cpp 112 | CXCppInterOp.cpp 113 | ${DLM} 114 | LINK_LIBS 115 | ${link_libs} 116 | ) 117 | 118 | 119 | if(EMSCRIPTEN) 120 | if(BUILD_SHARED_LIBS) 121 | # FIXME: When dynamically linking the Emscripten shared library to the 122 | # unit tests main_module you get errors due to undefined symbols. The reading of the file 123 | # below into a SYMBOLS_LIST variable is a temporary workaround that exports the undefined 124 | # symbols from the shared library, until it can be determined why they are not being exported already. 125 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/exports.ld" SYMBOLS_LIST) 126 | 127 | # Replace newlines with spaces 128 | string(REPLACE "\n" " " SYMBOLS_LIST "${SYMBOLS_LIST}") 129 | 130 | set_target_properties(clangCppInterOp 131 | PROPERTIES NO_SONAME 1 132 | ) 133 | target_compile_options(clangCppInterOp 134 | PRIVATE "SHELL: -Oz" 135 | PRIVATE "SHELL: -flto" 136 | ) 137 | target_link_options(clangCppInterOp 138 | PRIVATE "SHELL: -s WASM_BIGINT" 139 | PRIVATE "SHELL: -s SIDE_MODULE=1" 140 | PRIVATE "SHELL: ${SYMBOLS_LIST}" 141 | PRIVATE "SHELL: -Oz" 142 | PRIVATE "SHELL: -flto" 143 | ) 144 | else() 145 | target_link_options(clangCppInterOp 146 | PRIVATE "SHELL: -s WASM_BIGINT" 147 | ) 148 | endif() 149 | if (CPPINTEROP_ENABLE_TESTING) 150 | # When compiling Emscripten tests the CppInterOp library it links to is expected to be in the same folder as the compiled Javascript 151 | add_custom_command(TARGET clangCppInterOp POST_BUILD 152 | COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_BINARY_DIR}/unittests/CppInterOp/ 153 | ) 154 | endif(CPPINTEROP_ENABLE_TESTING) 155 | 156 | endif() 157 | 158 | target_compile_definitions(clangCppInterOp PUBLIC "_CINDEX_LIB_") # workaround for the use of `CINDEX_LINKAGE` 159 | 160 | string(REPLACE ";" "\;" _VER CPPINTEROP_VERSION) 161 | set_source_files_properties(CppInterOp.cpp PROPERTIES COMPILE_DEFINITIONS 162 | "LLVM_BINARY_DIR=\"${LLVM_BINARY_DIR}\";CPPINTEROP_VERSION=\"${_VAR}\"" 163 | ) 164 | -------------------------------------------------------------------------------- /.github/actions/Build_and_Test_cppyy/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Builds and test cppyy' 2 | description: 'This action builds and tests cppyy for native platforms' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Build and Install cppyy-backend 8 | if: ${{ matrix.cppyy == 'On' && runner.os != 'Windows' }} 9 | shell: bash 10 | run: | 11 | # Download cppyy-backend 12 | git clone --depth=1 https://github.com/compiler-research/cppyy-backend.git 13 | cd cppyy-backend 14 | mkdir -p $CPPINTEROP_DIR/lib build && cd build 15 | # Install CppInterOp 16 | (cd $CPPINTEROP_BUILD_DIR && cmake --build . --target install --parallel ${{ env.ncpus }} ) 17 | # Build and Install cppyy-backend 18 | cmake -DCppInterOp_DIR=$CPPINTEROP_DIR .. 19 | cmake --build . --parallel ${{ env.ncpus }} 20 | os="${{ matrix.os }}" 21 | if [[ "${os}" == "macos"* ]]; then 22 | cp libcppyy-backend.dylib $CPPINTEROP_DIR/lib/ 23 | else 24 | cp libcppyy-backend.so $CPPINTEROP_DIR/lib/ 25 | fi 26 | cd .. 27 | 28 | - name: Install CPyCppyy 29 | if: ${{ matrix.cppyy == 'On' && runner.os != 'Windows' }} 30 | shell: bash 31 | run: | 32 | python3 -m venv .venv 33 | source .venv/bin/activate 34 | # Install CPyCppyy 35 | git clone --depth=1 https://github.com/compiler-research/CPyCppyy.git 36 | mkdir CPyCppyy/build 37 | cd CPyCppyy/build 38 | cmake .. 39 | cmake --build . --parallel ${{ env.ncpus }} 40 | # 41 | export CPYCPPYY_DIR=$PWD 42 | cd ../.. 43 | # We need CPYCPPYY_DIR later 44 | echo "CPYCPPYY_DIR=$CPYCPPYY_DIR" >> $GITHUB_ENV 45 | 46 | - name: Install cppyy 47 | if: ${{ matrix.cppyy == 'On' && runner.os != 'Windows' }} 48 | shell: bash 49 | run: | 50 | # source virtual environment 51 | source .venv/bin/activate 52 | # Install cppyy 53 | git clone --depth=1 https://github.com/compiler-research/cppyy.git 54 | cd cppyy 55 | python -m pip install --upgrade . --no-deps 56 | cd .. 57 | 58 | - name: Run cppyy 59 | if: ${{ matrix.cppyy == 'On' && runner.os != 'Windows' }} 60 | shell: bash 61 | run: | 62 | # Run cppyy 63 | source .venv/bin/activate 64 | export PYTHONPATH=$PYTHONPATH:$CPYCPPYY_DIR:$CB_PYTHON_DIR 65 | python -c "import cppyy" 66 | # We need PYTHONPATH later 67 | echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV 68 | 69 | - name: Run the tests 70 | if: ${{ matrix.cppyy == 'On' && runner.os != 'Windows' }} 71 | shell: bash 72 | run: | 73 | # Run the tests 74 | source .venv/bin/activate 75 | cd cppyy/test 76 | echo ::group::Prepare For Testing 77 | make all 78 | python -m pip install --upgrade pip 79 | python -m pip install pytest 80 | python -m pip install pytest-xdist 81 | python -m pip install numba==0.61.2 82 | echo ::endgroup:: 83 | echo ::group::Run complete test suite 84 | set -o pipefail 85 | python -m pytest -sv -ra | tee complete_testrun.log 2>&1 86 | set +o pipefail 87 | echo ::group::Crashing Test Logs 88 | # See if we don't have a crash that went away 89 | # Comment out all xfails but the ones that have a run=False condition. 90 | find . -name "*.py" -exec sed -i '/run=False/!s/^ *@mark.xfail\(.*\)/#&/' {} \; 91 | python -m pytest -n 1 -m "xfail" --runxfail -sv -ra --max-worker-restart 512 | tee test_crashed.log 2>&1 || true 92 | git checkout . 93 | echo ::endgroup:: 94 | echo ::group::XFAIL Test Logs 95 | # Rewrite all xfails that have a run clause to skipif. This way we will 96 | # avoid conditionally crashing xfails 97 | find . -name "*.py" -exec sed -i -E 's/(^ *)@mark.xfail\(run=(.*)/\1@mark.skipif(condition=not \2/g' {} \; 98 | # See if we don't have an xfail that went away 99 | python -m pytest --runxfail -sv -ra | tee test_xfailed.log 2>&1 || true 100 | git checkout . 101 | echo ::endgroup:: 102 | echo ::group::Passing Test Logs 103 | 104 | # Run the rest of the non-crashing tests. 105 | declare -i RETCODE=0 106 | 107 | set -o pipefail 108 | os="${{ matrix.os }}" 109 | if [[ "${os}" != "macos"* && "${{ matrix.Valgrind }}" == "On" ]]; then 110 | echo "Running valgrind on passing tests" 111 | CLANG_VERSION="${{ matrix.clang-runtime }}" 112 | 113 | if [[ "${{ matrix.cling }}" == "On" ]]; then 114 | CLANG_INTERPRETER="cling" 115 | else 116 | CLANG_INTERPRETER="clang" 117 | fi 118 | 119 | if [[ "${{ matrix.os }}" == *"arm"* ]]; then 120 | SUPPRESSION_FILE="../etc/${CLANG_INTERPRETER}${CLANG_VERSION}-valgrind_arm.supp" 121 | else 122 | SUPPRESSION_FILE="../etc/${CLANG_INTERPRETER}${CLANG_VERSION}-valgrind.supp" 123 | fi 124 | export IS_VALGRIND=true 125 | valgrind --show-error-list=yes --error-exitcode=1 --track-origins=yes --gen-suppressions=all --suppressions="${SUPPRESSION_FILE}" --suppressions=../etc/valgrind-cppyy-cling.supp python -m pytest -m "not xfail" -sv -ra --ignore=test_leakcheck.py 126 | unset IS_VALGRIND 127 | fi 128 | export RETCODE=+$? 129 | echo ::endgroup:: 130 | 131 | RETCODE=+$? 132 | 133 | echo "Complete Test Suite Summary: \n" 134 | tail -n1 complete_testrun.log 135 | echo "Crashing Summary: \n" 136 | tail -n1 test_crashed.log 137 | echo "XFAIL Summary:" 138 | tail -n1 test_xfailed.log 139 | echo "Return Code: ${RETCODE}" 140 | exit $RETCODE 141 | -------------------------------------------------------------------------------- /unittests/CppInterOp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (EMSCRIPTEN) 2 | # So we create a html file, as well as the javascript file 3 | set(CMAKE_EXECUTABLE_SUFFIX ".html") 4 | # Omitting CUDATest.cpp since Emscripten build currently has no GPU support 5 | # For Emscripten builds linking to gtest_main will not suffice for gtest to run 6 | # the tests and an explicitly main.cpp is needed 7 | set(EXTRA_TEST_SOURCE_FILES main.cpp) 8 | else() 9 | # Do not need main.cpp for native builds, but we do have GPU support for native builds 10 | set(EXTRA_TEST_SOURCE_FILES CUDATest.cpp) 11 | set(EXTRA_PATH_TEST_BINARIES /CppInterOpTests/unittests/bin/$/) 12 | endif() 13 | 14 | add_cppinterop_unittest(CppInterOpTests 15 | EnumReflectionTest.cpp 16 | FunctionReflectionTest.cpp 17 | InterpreterTest.cpp 18 | JitTest.cpp 19 | ScopeReflectionTest.cpp 20 | TypeReflectionTest.cpp 21 | Utils.cpp 22 | VariableReflectionTest.cpp 23 | ${EXTRA_TEST_SOURCE_FILES} 24 | ) 25 | 26 | if(EMSCRIPTEN) 27 | string(REPLACE "@" "@@" ESCAPED_SYSROOT_PATH "${SYSROOT_PATH}") 28 | # Explanation of Emscripten-specific link flags for CppInterOpTests: 29 | # 30 | # MAIN_MODULE=1: 31 | # Enables building CppInterOpTests.js as the main WebAssembly module, allowing dynamic linking of side modules. 32 | # 33 | # WASM_BIGINT: 34 | # Ensures support for 64-bit integer types by enabling JavaScript BigInt integration in WASM. 35 | # 36 | # ALLOW_MEMORY_GROWTH=1: 37 | # Allows the WebAssembly memory to grow dynamically at runtime to accommodate increasing memory needs. 38 | # Would lead to an abortOnCannotGrowMemory error if memory cannot be grown while running the tests. 39 | # 40 | # STACK_SIZE=32mb: Allocates 32MB of stack space to handle deep recursion or large stack-allocated objects safely. 41 | # INITIAL_MEMORY=128mb: Sets the initial linear memory size to 128MB to reduce the likelihood of early memory expansion and improve performance. 42 | # The STACK_SIZE and INITIAL_MEMORY values are chosen based on what has been put to use for running xeus-cpp-lite. 43 | # Check https://github.com/jupyter-xeus/xeus/blob/main/cmake/WasmBuildOptions.cmake#L35-L36 for more details. 44 | # Not setting these flags would lead to a memory access out of bounds error while running the tests. 45 | # 46 | # --preload-file ${SYSROOT_PATH}/include@/include: 47 | # Preloads the system include directory into the Emscripten virtual filesystem to make headers accessible at runtime. 48 | # 49 | # --emrun 50 | # Makes it so that we run the html file created by this target, that we can capture the standard output 51 | # and output to the terminal 52 | target_link_options(CppInterOpTests 53 | PUBLIC "SHELL: -fexceptions" 54 | PUBLIC "SHELL: -s MAIN_MODULE=1" 55 | PUBLIC "SHELL: -s WASM_BIGINT" 56 | PUBLIC "SHELL: -s ALLOW_MEMORY_GROWTH=1" 57 | PUBLIC "SHELL: -s STACK_SIZE=32mb" 58 | PUBLIC "SHELL: -s INITIAL_MEMORY=128mb" 59 | PUBLIC "SHELL: --preload-file ${ESCAPED_SYSROOT_PATH}/include@/include" 60 | PUBLIC "SHELL: --emrun" 61 | ) 62 | endif() 63 | 64 | target_link_libraries(CppInterOpTests 65 | PRIVATE 66 | clangCppInterOp 67 | ) 68 | 69 | set_output_directory(CppInterOpTests 70 | BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${EXTRA_PATH_TEST_BINARIES} 71 | LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${EXTRA_PATH_TEST_BINARIES} 72 | ) 73 | 74 | if(NOT WIN32) 75 | set_source_files_properties(VariableReflectionTest.cpp PROPERTIES COMPILE_FLAGS 76 | "-Wno-pedantic" 77 | ) 78 | endif() 79 | 80 | set_source_files_properties(InterpreterTest.cpp PROPERTIES COMPILE_DEFINITIONS 81 | "LLVM_BINARY_DIR=\"${LLVM_BINARY_DIR}\"" 82 | ) 83 | export_executable_symbols(CppInterOpTests) 84 | 85 | unset(LLVM_LINK_COMPONENTS) 86 | 87 | if (NOT EMSCRIPTEN) 88 | set(EXTRA_TEST_SOURCE_FILES "") 89 | set(EXTRA_PATH_TEST_BINARIES /TestSharedLib/unittests/bin/$/) 90 | endif() 91 | 92 | add_cppinterop_unittest(DynamicLibraryManagerTests 93 | DynamicLibraryManagerTest.cpp 94 | Utils.cpp 95 | ${EXTRA_TEST_SOURCE_FILES} 96 | ) 97 | 98 | target_link_libraries(DynamicLibraryManagerTests 99 | PRIVATE 100 | clangCppInterOp 101 | ) 102 | 103 | if(EMSCRIPTEN) 104 | set(TEST_SHARED_LIBRARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/TestSharedLib/unittests/bin/$/") 105 | string(REPLACE "@" "@@" ESCAPED_TEST_SHARED_LIBRARY_PATH "${TEST_SHARED_LIBRARY_PATH}") 106 | # Check explanation of Emscripten-specific link flags for CppInterOpTests above for DynamicLibraryManagerTests as well. 107 | target_link_options(DynamicLibraryManagerTests 108 | PUBLIC "SHELL: -s MAIN_MODULE=1" 109 | PUBLIC "SHELL: -s WASM_BIGINT" 110 | PUBLIC "SHELL: -s ALLOW_MEMORY_GROWTH=1" 111 | PUBLIC "SHELL: -s STACK_SIZE=32mb" 112 | PUBLIC "SHELL: -s INITIAL_MEMORY=128mb" 113 | PUBLIC "SHELL: --preload-file ${ESCAPED_SYSROOT_PATH}/include@/include" 114 | PUBLIC "SHELL: --emrun" 115 | PUBLIC "SHELL: --preload-file ${ESCAPED_TEST_SHARED_LIBRARY_PATH}/libTestSharedLib.so@/libTestSharedLib.so" 116 | ) 117 | endif() 118 | 119 | if (EMSCRIPTEN) 120 | if (BUILD_SHARED_LIBS) 121 | target_compile_definitions(CppInterOpTests PRIVATE "EMSCRIPTEN_SHARED_LIBRARY") 122 | target_compile_definitions(DynamicLibraryManagerTests PRIVATE "EMSCRIPTEN_SHARED_LIBRARY") 123 | else() 124 | target_compile_definitions(CppInterOpTests PRIVATE "EMSCRIPTEN_STATIC_LIBRARY") 125 | target_compile_definitions(DynamicLibraryManagerTests PRIVATE "EMSCRIPTEN_STATIC_LIBRARY") 126 | endif() 127 | endif() 128 | 129 | set_output_directory(DynamicLibraryManagerTests 130 | BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${EXTRA_PATH_TEST_BINARIES} 131 | LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${EXTRA_PATH_TEST_BINARIES} 132 | ) 133 | 134 | add_dependencies(DynamicLibraryManagerTests TestSharedLib) 135 | 136 | #export_executable_symbols_for_plugins(TestSharedLib) 137 | add_subdirectory(TestSharedLib) 138 | -------------------------------------------------------------------------------- /docs/DebuggingCppInterOp.rst: -------------------------------------------------------------------------------- 1 | Debugging in JIT Compiled Code 2 | ------------------------------ 3 | 4 | C++ Language Interoperability Layer - Debugging Guide 5 | ====================================================== 6 | 7 | Overview 8 | ======== 9 | 10 | This guide provides comprehensive instructions for debugging CppInterOp applications using LLDB. 11 | 12 | Prerequisites 13 | ============= 14 | 15 | Before proceeding with debugging, ensure you have the following tools installed: 16 | 17 | - **LLDB**: The LLVM debugger 18 | - **LLVM/Clang**: The C++ compiler and toolchain 19 | - **CppInterOp**: The C++ language interoperability library 20 | 21 | The debugging tools should be available in your LLVM toolchain. On most systems, these are installed alongside your LLVM/Clang installation. 22 | 23 | Setting Up Debug Environment 24 | ============================ 25 | 26 | To effectively debug CppInterOp applications, you need to compile your code with debugging symbols and disable optimizations. This ensures that the debugger can accurately map between source code and machine instructions. 27 | 28 | **Compilation Flags** 29 | 30 | When compiling your CppInterOp application, use the following essential flags: 31 | 32 | .. code-block:: bash 33 | 34 | $CXX -I$CPPINTEROP_DIR/include -g -O0 -lclangCppInterOp -Wl,-rpath,$CPPINTEROP_DIR/build/lib 35 | 36 | **Flag Explanation:** 37 | 38 | - ``-g``: Includes debugging information in the executable 39 | - ``-O0``: Disables compiler optimizations for clearer debugging 40 | - ``-I$CPPINTEROP_DIR/include``: Includes CppInterOp headers 41 | - ``-lclangCppInterOp``: Links against the CppInterOp library 42 | - ``-Wl,-rpath,$CPPINTEROP_DIR/build/lib``: Sets runtime library path 43 | 44 | Creating a Debug-Ready Test Program 45 | =================================== 46 | 47 | Here's a comprehensive example that demonstrates common CppInterOp usage patterns suitable for debugging: 48 | 49 | .. code-block:: cpp 50 | 51 | #include 52 | #include 53 | 54 | void run_code(std::string code) { 55 | Cpp::Declare(code.c_str()); 56 | } 57 | 58 | int main(int argc, char *argv[]) { 59 | Cpp::CreateInterpreter({"-gdwarf-4", "-O0"}); 60 | std::vector Decls; 61 | std::string code = R"( 62 | #include 63 | void f1() { 64 | std::cout << "in f1 function" << std::endl; 65 | } 66 | std::cout << "In codeblock 1" << std::endl; 67 | int a = 100; 68 | int b = 1000; 69 | )"; 70 | run_code(code); 71 | code = R"( 72 | f1(); 73 | )"; 74 | run_code(code); 75 | return 0; 76 | } 77 | 78 | 79 | **Program Structure Explanation:** 80 | 81 | This example demonstrates key debugging scenarios: 82 | 83 | 1. **Interpreter Initialization**: The ``Cpp::CreateInterpreter`` call with debug flags 84 | 2. **Code Declaration**: Dynamic C++ code execution through ``Cpp::Declare`` 85 | 3. **Mixed Execution**: Combination of compiled and interpreted code paths 86 | 4. **Variable Scoping**: Local variables in both compiled and interpreted contexts 87 | 88 | Debugging Strategies 89 | ==================== 90 | 91 | **Debugging Compiled Code** 92 | 93 | For debugging the main executable and compiled portions of your CppInterOp application: 94 | 95 | .. code-block:: bash 96 | 97 | lldb /path/to/executable 98 | (lldb) settings set plugin.jit-loader.gdb.enable on 99 | (lldb) breakpoint set --name f1 100 | (lldb) r 101 | 1 location added to breakpoint 1 102 | In codeblock 1 103 | Process 49132 stopped 104 | * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 105 | frame #0: 0x000000010217c008 JIT(0x10215c218) f1() at input_line_1:4:13 106 | 107 | **Note** 108 | 109 | 1. Ensure the JIT loader is enabled to allow LLDB to debug dynamically generated code. 110 | 2. Use ``settings set plugin.jit-loader.gdb.enable on`` to enable JIT debugging. 111 | 3. Set breakpoints in both compiled and interpreted code using ``breakpoint set --name function_name``. 112 | 113 | 114 | **Some Caveats** 115 | 116 | 1. For each block of code, there is a file named ``input_line_`` that contains the code block. This file is in-memory and thus cannot be directly accessed. 117 | 2. However, generating actual input_line_ files on disk will let LLDB pick them up and render the source content correctly during debugging. This can be achieved by modifying run_code as follows: 118 | 119 | .. code-block:: cpp 120 | 121 | void run_code(std::string code) { 122 | static size_t i = 0; 123 | i++; 124 | std::string filename = "input_line_" + std::to_string(i); 125 | std::ofstream file(filename); 126 | file << code; 127 | file.close(); 128 | Cpp::Declare(code.c_str()); 129 | } 130 | 131 | .. note:: 132 | 133 | You'll need to manually delete these files later to avoid cluttering the filesystem. 134 | 135 | 3. If a function is called from different cell, then it may take multiple step-ins to reach the function definition due to the way CppInterOp handles code blocks. 136 | 137 | Advanced Debugging Techniques 138 | ============================= 139 | 140 | **Using LLDB with VS Code** 141 | 142 | For IDE-based debugging: 143 | 144 | 1. Install the LLDB extension in VS Code 145 | 2. Create a ``launch.json`` configuration: 146 | 147 | .. code-block:: json 148 | 149 | { 150 | "version": "0.2.0", 151 | "configurations": [ 152 | { 153 | "type": "lldb-dap", 154 | "request": "launch", 155 | "name": "Debug", 156 | "program": "/path/to/executable", 157 | "sourcePath" : ["${workspaceFolder}"], 158 | "cwd": "${workspaceFolder}", 159 | "initCommands": [ 160 | "settings set plugin.jit-loader.gdb.enable on", // This is crucial 161 | ] 162 | }, 163 | ] 164 | } 165 | 166 | 167 | 168 | Further Reading 169 | =============== 170 | 171 | - **LLDB Documentation**: `LLDB Debugger `_ 172 | - **CppInterOp Source**: `CppInterOp Repository `_ 173 | - **Clang Documentation**: `Clang Compiler `_ 174 | - **LLVM Debugging Guide**: `LLVM Debug Info `_ 175 | 176 | 177 | -------------------------------------------------------------------------------- /docs/manpage.css: -------------------------------------------------------------------------------- 1 | /* Based on http://www.perldoc.com/css/perldoc.css */ 2 | 3 | @import url("../llvm.css"); 4 | 5 | body { font-family: Arial,Helvetica; } 6 | 7 | blockquote { margin: 10pt; } 8 | 9 | h1, a { color: #336699; } 10 | 11 | 12 | /*** Top menu style ****/ 13 | .mmenuon { 14 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 15 | color: #ff6600; font-size: 10pt; 16 | } 17 | .mmenuoff { 18 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 19 | color: #ffffff; font-size: 10pt; 20 | } 21 | .cpyright { 22 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 23 | color: #ffffff; font-size: xx-small; 24 | } 25 | .cpyrightText { 26 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 27 | color: #ffffff; font-size: xx-small; 28 | } 29 | .sections { 30 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 31 | color: #336699; font-size: 11pt; 32 | } 33 | .dsections { 34 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 35 | color: #336699; font-size: 12pt; 36 | } 37 | .slink { 38 | font-family: Arial,Helvetica; font-weight: normal; text-decoration: none; 39 | color: #000000; font-size: 9pt; 40 | } 41 | 42 | .slink2 { font-family: Arial,Helvetica; text-decoration: none; color: #336699; } 43 | 44 | .maintitle { 45 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 46 | color: #336699; font-size: 18pt; 47 | } 48 | .dblArrow { 49 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 50 | color: #336699; font-size: small; 51 | } 52 | .menuSec { 53 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 54 | color: #336699; font-size: small; 55 | } 56 | 57 | .newstext { 58 | font-family: Arial,Helvetica; font-size: small; 59 | } 60 | 61 | .linkmenu { 62 | font-family: Arial,Helvetica; color: #000000; font-weight: bold; 63 | text-decoration: none; 64 | } 65 | 66 | P { 67 | font-family: Arial,Helvetica; 68 | } 69 | 70 | PRE { 71 | font-size: 10pt; 72 | } 73 | .quote { 74 | font-family: Times; text-decoration: none; 75 | color: #000000; font-size: 9pt; font-style: italic; 76 | } 77 | .smstd { font-family: Arial,Helvetica; color: #000000; font-size: x-small; } 78 | .std { font-family: Arial,Helvetica; color: #000000; } 79 | .meerkatTitle { 80 | font-family: sans-serif; font-size: x-small; color: black; } 81 | 82 | .meerkatDescription { font-family: sans-serif; font-size: 10pt; color: black } 83 | .meerkatCategory { 84 | font-family: sans-serif; font-size: 9pt; font-weight: bold; font-style: italic; 85 | color: brown; } 86 | .meerkatChannel { 87 | font-family: sans-serif; font-size: 9pt; font-style: italic; color: brown; } 88 | .meerkatDate { font-family: sans-serif; font-size: xx-small; color: #336699; } 89 | 90 | .tocTitle { 91 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 92 | color: #333333; font-size: 10pt; 93 | } 94 | 95 | .toc-item { 96 | font-family: Arial,Helvetica; font-weight: bold; 97 | color: #336699; font-size: 10pt; text-decoration: underline; 98 | } 99 | 100 | .perlVersion { 101 | font-family: Arial,Helvetica; font-weight: bold; 102 | color: #336699; font-size: 10pt; text-decoration: none; 103 | } 104 | 105 | .podTitle { 106 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 107 | color: #000000; 108 | } 109 | 110 | .docTitle { 111 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 112 | color: #000000; font-size: 10pt; 113 | } 114 | .dotDot { 115 | font-family: Arial,Helvetica; font-weight: bold; 116 | color: #000000; font-size: 9pt; 117 | } 118 | 119 | .docSec { 120 | font-family: Arial,Helvetica; font-weight: normal; 121 | color: #333333; font-size: 9pt; 122 | } 123 | .docVersion { 124 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 125 | color: #336699; font-size: 10pt; 126 | } 127 | 128 | .docSecs-on { 129 | font-family: Arial,Helvetica; font-weight: normal; text-decoration: none; 130 | color: #ff0000; font-size: 10pt; 131 | } 132 | .docSecs-off { 133 | font-family: Arial,Helvetica; font-weight: normal; text-decoration: none; 134 | color: #333333; font-size: 10pt; 135 | } 136 | 137 | h2 { 138 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 139 | color: #336699; font-size: medium; 140 | } 141 | h1 { 142 | font-family: Verdana,Arial,Helvetica; font-weight: bold; text-decoration: none; 143 | color: #336699; font-size: large; 144 | } 145 | 146 | DL { 147 | font-family: Arial,Helvetica; font-weight: normal; text-decoration: none; 148 | color: #333333; font-size: 10pt; 149 | } 150 | 151 | UL > LI > A { 152 | font-family: Arial,Helvetica; font-weight: bold; 153 | color: #336699; font-size: 10pt; 154 | } 155 | 156 | .moduleInfo { 157 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 158 | color: #333333; font-size: 11pt; 159 | } 160 | 161 | .moduleInfoSec { 162 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 163 | color: #336699; font-size: 10pt; 164 | } 165 | 166 | .moduleInfoVal { 167 | font-family: Arial,Helvetica; font-weight: normal; text-decoration: underline; 168 | color: #000000; font-size: 10pt; 169 | } 170 | 171 | .cpanNavTitle { 172 | font-family: Arial,Helvetica; font-weight: bold; 173 | color: #ffffff; font-size: 10pt; 174 | } 175 | .cpanNavLetter { 176 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 177 | color: #333333; font-size: 9pt; 178 | } 179 | .cpanCat { 180 | font-family: Arial,Helvetica; font-weight: bold; text-decoration: none; 181 | color: #336699; font-size: 9pt; 182 | } 183 | 184 | .bttndrkblue-bkgd-top { 185 | background-color: #225688; 186 | background-image: url(/global/mvc_objects/images/bttndrkblue_bgtop.gif); 187 | } 188 | .bttndrkblue-bkgd-left { 189 | background-color: #225688; 190 | background-image: url(/global/mvc_objects/images/bttndrkblue_bgleft.gif); 191 | } 192 | .bttndrkblue-bkgd { 193 | padding-top: 0px; 194 | padding-bottom: 0px; 195 | margin-bottom: 0px; 196 | margin-top: 0px; 197 | background-repeat: no-repeat; 198 | background-color: #225688; 199 | background-image: url(/global/mvc_objects/images/bttndrkblue_bgmiddle.gif); 200 | vertical-align: top; 201 | } 202 | .bttndrkblue-bkgd-right { 203 | background-color: #225688; 204 | background-image: url(/global/mvc_objects/images/bttndrkblue_bgright.gif); 205 | } 206 | .bttndrkblue-bkgd-bottom { 207 | background-color: #225688; 208 | background-image: url(/global/mvc_objects/images/bttndrkblue_bgbottom.gif); 209 | } 210 | .bttndrkblue-text a { 211 | color: #ffffff; 212 | text-decoration: none; 213 | } 214 | a.bttndrkblue-text:hover { 215 | color: #ffDD3C; 216 | text-decoration: none; 217 | } 218 | .bg-ltblue { 219 | background-color: #f0f5fa; 220 | } 221 | 222 | .border-left-b { 223 | background: #f0f5fa url(/i/corner-leftline.gif) repeat-y; 224 | } 225 | 226 | .border-right-b { 227 | background: #f0f5fa url(/i/corner-rightline.gif) repeat-y; 228 | } 229 | 230 | .border-top-b { 231 | background: #f0f5fa url(/i/corner-topline.gif) repeat-x; 232 | } 233 | 234 | .border-bottom-b { 235 | background: #f0f5fa url(/i/corner-botline.gif) repeat-x; 236 | } 237 | 238 | .border-right-w { 239 | background: #ffffff url(/i/corner-rightline.gif) repeat-y; 240 | } 241 | 242 | .border-top-w { 243 | background: #ffffff url(/i/corner-topline.gif) repeat-x; 244 | } 245 | 246 | .border-bottom-w { 247 | background: #ffffff url(/i/corner-botline.gif) repeat-x; 248 | } 249 | 250 | .bg-white { 251 | background-color: #ffffff; 252 | } 253 | 254 | .border-left-w { 255 | background: #ffffff url(/i/corner-leftline.gif) repeat-y; 256 | } 257 | -------------------------------------------------------------------------------- /.github/actions/Build_and_Test_CppInterOp/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Builds and test CppInterOp' 2 | description: 'This action builds and tests CppInterOp for native platforms' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Build and Test/Install CppInterOp 8 | if: runner.os != 'Windows' 9 | shell: bash 10 | run: | 11 | LLVM_DIR="$(pwd)/llvm-project" 12 | LLVM_BUILD_DIR="$(pwd)/llvm-project/build" 13 | cling_on=$(echo "${{ matrix.cling }}" | tr '[:lower:]' '[:upper:]') 14 | if [[ "${cling_on}" == "ON" ]]; then 15 | CLING_DIR="$(pwd)/cling" 16 | CLING_BUILD_DIR="$(pwd)/cling/build" 17 | CPLUS_INCLUDE_PATH="${CLING_DIR}/tools/cling/include:${CLING_BUILD_DIR}/include:${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_BUILD_DIR}/include:${LLVM_BUILD_DIR}/tools/clang/include:$PWD/include" 18 | else 19 | CPLUS_INCLUDE_PATH="${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_BUILD_DIR}/include:${LLVM_BUILD_DIR}/tools/clang/include:$PWD/include" 20 | fi 21 | 22 | export CB_PYTHON_DIR="$PWD/cppyy-backend/python" 23 | export CPPINTEROP_DIR="$CB_PYTHON_DIR/cppyy_backend" 24 | 25 | # Build CppInterOp next to cling and llvm-project. 26 | mkdir build && cd build 27 | export CPPINTEROP_BUILD_DIR=$PWD 28 | cling_on=$(echo "${{ matrix.cling }}" | tr '[:lower:]' '[:upper:]') 29 | if [[ "${cling_on}" == "ON" ]]; then 30 | cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ 31 | -DCPPINTEROP_USE_CLING=ON \ 32 | -DCPPINTEROP_USE_REPL=OFF \ 33 | -DCPPINTEROP_INCLUDE_DOCS=${{ matrix.documentation }} \ 34 | -DCling_DIR=$LLVM_BUILD_DIR/tools/cling \ 35 | -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ 36 | -DClang_DIR=$LLVM_BUILD_DIR/lib/cmake/clang \ 37 | -DBUILD_SHARED_LIBS=ON \ 38 | -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ 39 | -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR \ 40 | -DLLVM_ENABLE_WERROR=On \ 41 | ../ 42 | else 43 | cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ 44 | -DCPPINTEROP_INCLUDE_DOCS=${{ matrix.documentation }} \ 45 | -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ 46 | -DClang_DIR=$LLVM_BUILD_DIR/lib/cmake/clang \ 47 | -DBUILD_SHARED_LIBS=ON \ 48 | -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ 49 | -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR \ 50 | -DLLVM_ENABLE_WERROR=On \ 51 | -DLLVM_BUILT_WITH_OOP_JIT=${{ matrix.oop-jit }} \ 52 | ../ 53 | fi 54 | docs_on=$(echo "${{ matrix.documentation }}" | tr '[:lower:]' '[:upper:]') 55 | if [[ "${docs_on}" == "ON" ]]; then 56 | cmake --build . --target doxygen-cppinterop --parallel ${{ env.ncpus }} 57 | cmake --build . --target sphinx-cppinterop --parallel ${{ env.ncpus }} 58 | else 59 | cmake --build . --target check-cppinterop --parallel ${{ env.ncpus }} 60 | os="${{ matrix.os }}" 61 | if [[ "${os}" != "macos"* && "${{ matrix.Valgrind }}" == "On" ]]; then 62 | CLANG_VERSION="${{ matrix.clang-runtime }}" 63 | if [[ "$CLANG_VERSION" == "20" && "${{ matrix.cling }}" == "Off" ]]; then 64 | SUPPRESSION_FILE="../etc/clang${CLANG_VERSION}-valgrind.supp" 65 | valgrind --show-error-list=yes --track-origins=yes --error-exitcode=1 \ 66 | --show-leak-kinds=definite,possible \ 67 | --suppressions="${SUPPRESSION_FILE}" \ 68 | unittests/CppInterOp/CppInterOpTests/unittests/bin/${{ env.BUILD_TYPE }}/CppInterOpTests 69 | else 70 | valgrind --show-error-list=yes --track-origins=yes --error-exitcode=1 \ 71 | --show-leak-kinds=definite,possible \ 72 | unittests/CppInterOp/CppInterOpTests/unittests/bin/${{ env.BUILD_TYPE }}/CppInterOpTests 73 | fi 74 | fi 75 | fi 76 | echo "CB_PYTHON_DIR=$CB_PYTHON_DIR" >> $GITHUB_ENV 77 | echo "CPPINTEROP_BUILD_DIR=$CPPINTEROP_BUILD_DIR" >> $GITHUB_ENV 78 | echo "CPPINTEROP_DIR=$CPPINTEROP_DIR" >> $GITHUB_ENV 79 | echo "LLVM_BUILD_DIR=$LLVM_BUILD_DIR" >> $GITHUB_ENV 80 | echo "CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH" >> $GITHUB_ENV 81 | 82 | - name: Build and Test/Install CppInterOp on Windows systems 83 | if: runner.os == 'Windows' 84 | shell: powershell 85 | run: | 86 | $env:PWD_DIR= $PWD.Path 87 | 88 | $env:LLVM_DIR="$env:PWD_DIR\llvm-project" 89 | echo "LLVM_DIR=$env:LLVM_DIR" 90 | echo "LLVM_DIR=$env:LLVM_DIR" >> $env:GITHUB_ENV 91 | 92 | $env:LLVM_BUILD_DIR="$env:PWD_DIR\llvm-project\build" 93 | echo "LLVM_BUILD_DIR=$env:LLVM_BUILD_DIR" 94 | echo "LLVM_BUILD_DIR=$env:LLVM_BUILD_DIR" >> $env:GITHUB_ENV 95 | 96 | if ( "${{ matrix.cling }}" -imatch "On" ) 97 | { 98 | $env:CLING_DIR="$env:PWD_DIR\cling" 99 | echo "CLING_DIR=$env:CLING_DIR" 100 | echo "CLING_DIR=$env:CLING_DIR" >> $env:GITHUB_ENV 101 | 102 | $env:CLING_BUILD_DIR="$env:PWD_DIR\cling\build" 103 | echo "CLING_BUILD_DIR=$env:CLING_BUILD_DIR" 104 | echo "CLING_BUILD_DIR=$env:CLING_BUILD_DIR" >> $env:GITHUB_ENV 105 | 106 | $env:CPLUS_INCLUDE_PATH="$env:CLING_DIR\tools\cling\include;$env:CLING_BUILD_DIR\include;$env:LLVM_DIR\llvm\include;$env:LLVM_DIR\clang\include;$env:LLVM_BUILD_DIR\include;$env:LLVM_BUILD_DIR\tools\clang\include;$env:PWD_DIR\include;" 107 | echo "CPLUS_INCLUDE_PATH=$env:CPLUS_INCLUDE_PATH" 108 | echo "CPLUS_INCLUDE_PATH=$env:CPLUS_INCLUDE_PATH" >> $env:GITHUB_ENV 109 | } 110 | else 111 | { 112 | $env:CPLUS_INCLUDE_PATH="$env:LLVM_DIR\llvm\include;$env:LLVM_DIR\clang\include;$env:LLVM_BUILD_DIR\include;$env:LLVM_BUILD_DIR\tools\clang\include;$env:PWD_DIR\include;" 113 | echo "CPLUS_INCLUDE_PATH=$env:CPLUS_INCLUDE_PATH" 114 | echo "CPLUS_INCLUDE_PATH=$env:CPLUS_INCLUDE_PATH" >> $env:GITHUB_ENV 115 | } 116 | 117 | $env:CB_PYTHON_DIR="$env:PWD_DIR\cppyy-backend\python" 118 | echo "CB_PYTHON_DIR=$env:CB_PYTHON_DIR" 119 | echo "CB_PYTHON_DIR=$env:CB_PYTHON_DIR" >> $env:GITHUB_ENV 120 | 121 | $env:CPPINTEROP_DIR="$env:CB_PYTHON_DIR\cppyy-backend" 122 | echo "CPPINTEROP_DIR=$env:CPPINTEROP_DIR" 123 | echo "CPPINTEROP_DIR=$env:CPPINTEROP_DIR" >> $env:GITHUB_ENV 124 | 125 | # Build CppInterOp next to cling and llvm-project. 126 | mkdir build 127 | cd build 128 | $env:CPPINTEROP_BUILD_DIR="$env:PWD_DIR" 129 | echo "CPPINTEROP_BUILD_DIR=$env:CPPINTEROP_BUILD_DIR" 130 | echo "CPPINTEROP_BUILD_DIR=$env:CPPINTEROP_BUILD_DIR" >> $env:GITHUB_ENV 131 | if ( "${{ matrix.cling }}" -imatch "On" ) 132 | { 133 | cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} ` 134 | -DCPPINTEROP_USE_CLING=ON ` 135 | -DCPPINTEROP_USE_REPL=OFF ` 136 | -DCling_DIR="$env:LLVM_BUILD_DIR\tools\cling" ` 137 | -DLLVM_DIR="$env:LLVM_BUILD_DIR" ` 138 | -DLLVM_ENABLE_WERROR=On ` 139 | -DClang_DIR="$env:LLVM_BUILD_DIR" -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} -DCMAKE_INSTALL_PREFIX="$env:CPPINTEROP_DIR" ..\ 140 | } 141 | else 142 | { 143 | cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} ` 144 | -DLLVM_DIR="$env:LLVM_BUILD_DIR\lib\cmake\llvm" ` 145 | -DLLVM_ENABLE_WERROR=On ` 146 | -DClang_DIR="$env:LLVM_BUILD_DIR\lib\cmake\clang" -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} -DCMAKE_INSTALL_PREFIX="$env:CPPINTEROP_DIR" ..\ 147 | } 148 | cmake --build . --config ${{ env.BUILD_TYPE }} --target check-cppinterop --parallel ${{ env.ncpus }} 149 | -------------------------------------------------------------------------------- /.github/actions/Build_LLVM/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Builds LLVM' 2 | description: 'This action builds LLVM for native platforms' 3 | inputs: 4 | cache-hit: 5 | required: true 6 | 7 | runs: 8 | using: composite 9 | steps: 10 | - name: Build LLVM/Cling if the cache is invalid 11 | if: ${{ inputs.cache-hit != 'true' && runner.os != 'Windows' }} 12 | shell: bash 13 | run: | 14 | cling_on=$(echo "${{ matrix.cling }}" | tr '[:lower:]' '[:upper:]') 15 | if [[ "${cling_on}" == "ON" ]]; then 16 | git clone https://github.com/root-project/cling.git 17 | cd ./cling 18 | git checkout tags/v${{ matrix.cling-version }} 19 | git apply -v ../patches/llvm/cling1.2-LookupHelper.patch 20 | cd .. 21 | git clone --depth=1 -b cling-llvm${{ matrix.clang-runtime }} https://github.com/root-project/llvm-project.git 22 | else # repl 23 | git clone --depth=1 -b release/${{ matrix.clang-runtime }}.x https://github.com/llvm/llvm-project.git 24 | fi 25 | cd llvm-project 26 | # Build 27 | mkdir build 28 | if [[ "${cling_on}" == "ON" ]]; then 29 | cd build 30 | cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects }}" \ 31 | -DLLVM_EXTERNAL_PROJECTS=cling \ 32 | -DLLVM_EXTERNAL_CLING_SOURCE_DIR=../../cling \ 33 | -DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" \ 34 | -DCMAKE_BUILD_TYPE=Release \ 35 | -DLLVM_ENABLE_ASSERTIONS=ON \ 36 | -DCLANG_ENABLE_STATIC_ANALYZER=OFF \ 37 | -DCLANG_ENABLE_ARCMT=OFF \ 38 | -DCLANG_ENABLE_FORMAT=OFF \ 39 | -DCLANG_ENABLE_BOOTSTRAP=OFF \ 40 | -G Ninja \ 41 | ../llvm 42 | ninja clang -j ${{ env.ncpus }} 43 | ninja LLVMOrcDebugging -j ${{ env.ncpus }} 44 | ninja clingInterpreter -j ${{ env.ncpus }} 45 | else 46 | if [[ "${{ matrix.oop-jit }}" == "On" ]]; then 47 | git apply -v ../patches/llvm/clang20-1-out-of-process.patch 48 | echo "Apply clang20-1-out-of-process.patch:" 49 | fi 50 | cd build 51 | cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects}}" \ 52 | -DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" \ 53 | -DCMAKE_BUILD_TYPE=Release \ 54 | -DLLVM_ENABLE_ASSERTIONS=ON \ 55 | -DCLANG_ENABLE_STATIC_ANALYZER=OFF \ 56 | -DCLANG_ENABLE_ARCMT=OFF \ 57 | -DCLANG_ENABLE_FORMAT=OFF \ 58 | -DCLANG_ENABLE_BOOTSTRAP=OFF \ 59 | -G Ninja \ 60 | -DLLVM_INCLUDE_BENCHMARKS=OFF \ 61 | -DLLVM_INCLUDE_EXAMPLES=OFF \ 62 | -DLLVM_INCLUDE_TESTS=OFF \ 63 | ../llvm 64 | ninja clang clangInterpreter clangStaticAnalyzerCore 65 | if [[ "${{ matrix.oop-jit }}" == "On" ]]; then 66 | if [[ "${{ matrix.os }}" == macos* ]]; then 67 | SUFFIX="_osx" 68 | elif [[ "${{ matrix.os }}" == ubuntu* ]]; then 69 | SUFFIX="-x86_64" 70 | fi 71 | ninja llvm-jitlink-executor orc_rt${SUFFIX} -j ${{ env.ncpus }} 72 | fi 73 | cd ./tools/ 74 | rm -rf $(find . -maxdepth 1 ! -name "clang" ! -name ".") 75 | cd .. 76 | rm compile_commands.json build.ninja 77 | fi 78 | cd ../ 79 | rm -rf $(find . -maxdepth 1 ! -name "build" ! -name "llvm" ! -name "clang" ! -name ".") 80 | if [[ "${cling_on}" == "ON" ]]; then 81 | cd ./llvm/ 82 | rm -rf $(find . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name "utils" ! -name ".") 83 | cd ../clang/ 84 | rm -rf $(find . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name "utils" ! -name ".") 85 | cd ../../cling/ 86 | rm -rf $(find . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name ".") 87 | else # repl 88 | cd ./llvm/ 89 | rm -rf $(find . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name ".") 90 | cd ../clang/ 91 | rm -rf $(find . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name ".") 92 | cd ../.. 93 | fi 94 | 95 | 96 | - name: Build LLVM/Cling on Windows systems if the cache is invalid 97 | if: ${{ inputs.cache-hit != 'true' && runner.os == 'Windows' }} 98 | shell: powershell 99 | run: | 100 | 101 | if ( "${{ matrix.cling }}" -imatch "On" ) 102 | { 103 | git clone https://github.com/root-project/cling.git 104 | cd ./cling 105 | git checkout tags/v${{ matrix.cling-version }} 106 | git apply -v ../patches/llvm/cling1.2-LookupHelper.patch 107 | cd .. 108 | git clone --depth=1 -b cling-llvm${{ matrix.clang-runtime }} https://github.com/root-project/llvm-project.git 109 | $env:PWD_DIR= $PWD.Path 110 | $env:CLING_DIR="$env:PWD_DIR\cling" 111 | echo "CLING_DIR=$env:CLING_DIR" 112 | } 113 | else 114 | { 115 | git clone --depth=1 -b release/${{ matrix.clang-runtime }}.x https://github.com/llvm/llvm-project.git 116 | } 117 | 118 | cd llvm-project 119 | # Build 120 | mkdir build 121 | if ( "${{ matrix.cling }}" -imatch "On" ) 122 | { 123 | cd build 124 | cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects}}" ` 125 | -DLLVM_EXTERNAL_PROJECTS=cling ` 126 | -DLLVM_EXTERNAL_CLING_SOURCE_DIR="$env:CLING_DIR" ` 127 | -DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" ` 128 | -DCMAKE_BUILD_TYPE=Release ` 129 | -DLLVM_ENABLE_ASSERTIONS=ON ` 130 | -DCLANG_ENABLE_STATIC_ANALYZER=OFF ` 131 | -DCLANG_ENABLE_ARCMT=OFF ` 132 | -DCLANG_ENABLE_FORMAT=OFF ` 133 | -DCLANG_ENABLE_BOOTSTRAP=OFF ` 134 | ..\llvm 135 | cmake --build . --config Release --target clang --parallel ${{ env.ncpus }} 136 | cmake --build . --config Release --target LLVMOrcDebugging --parallel ${{ env.ncpus }} 137 | cmake --build . --config Release --target clingInterpreter --parallel ${{ env.ncpus }} 138 | } 139 | else 140 | { 141 | cd build 142 | echo "Apply clang${{ matrix.clang-runtime }}-*.patch patches:" 143 | cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects}}" ` 144 | -DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" ` 145 | -DCMAKE_BUILD_TYPE=Release ` 146 | -DLLVM_ENABLE_ASSERTIONS=ON ` 147 | -DCLANG_ENABLE_STATIC_ANALYZER=OFF ` 148 | -DCLANG_ENABLE_ARCMT=OFF ` 149 | -DCLANG_ENABLE_FORMAT=OFF ` 150 | -DCLANG_ENABLE_BOOTSTRAP=OFF ` 151 | ..\llvm 152 | cmake --build . --config Release --target clang clangInterpreter clangStaticAnalyzerCore --parallel ${{ env.ncpus }} 153 | } 154 | cd ..\ 155 | rm -r -force $(find.exe . -maxdepth 1 ! -name "build" ! -name "llvm" ! -name "clang" ! -name ".") 156 | if ( "${{ matrix.cling }}" -imatch "On" ) 157 | { 158 | cd .\llvm\ 159 | rm -r -force $(find.exe . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name "utils" ! -name ".") 160 | cd ..\clang\ 161 | rm -r -force $(find.exe . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name "utils" ! -name ".") 162 | cd ..\..\cling\ 163 | rm -r -force $(find.exe . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name ".") 164 | } 165 | else 166 | { 167 | cd .\llvm\ 168 | rm -r -force $(find.exe . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name ".") 169 | cd ..\clang\ 170 | rm -r -force $(find.exe . -maxdepth 1 ! -name "include" ! -name "lib" ! -name "cmake" ! -name ".") 171 | cd ..\.. 172 | } 173 | -------------------------------------------------------------------------------- /unittests/CppInterOp/EnumReflectionTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include "CppInterOp/CppInterOp.h" 4 | 5 | #include "clang/AST/ASTContext.h" 6 | #include "clang/Frontend/CompilerInstance.h" 7 | #include "clang/Sema/Sema.h" 8 | 9 | #include "gtest/gtest.h" 10 | 11 | using namespace TestUtils; 12 | using namespace llvm; 13 | using namespace clang; 14 | 15 | TYPED_TEST(CppInterOpTest, EnumReflectionTestIsEnumType) { 16 | std::vector Decls; 17 | std::string code = R"( 18 | enum class E { 19 | a, 20 | b 21 | }; 22 | 23 | enum F { 24 | c, 25 | d 26 | }; 27 | 28 | E e; 29 | F f; 30 | 31 | auto g = E::a; 32 | auto h = c; 33 | )"; 34 | 35 | GetAllTopLevelDecls(code, Decls); 36 | 37 | EXPECT_TRUE(Cpp::IsEnumType(Cpp::GetVariableType(Decls[2]))); 38 | EXPECT_TRUE(Cpp::IsEnumType(Cpp::GetVariableType(Decls[3]))); 39 | EXPECT_TRUE(Cpp::IsEnumType(Cpp::GetVariableType(Decls[4]))); 40 | EXPECT_TRUE(Cpp::IsEnumType(Cpp::GetVariableType(Decls[5]))); 41 | } 42 | 43 | TYPED_TEST(CppInterOpTest, EnumReflectionTestGetIntegerTypeFromEnumScope) { 44 | std::vector Decls; 45 | std::string code = R"( 46 | enum Switch : bool { 47 | OFF, 48 | ON 49 | }; 50 | 51 | enum CharEnum : char { 52 | OneChar, 53 | TwoChar 54 | }; 55 | 56 | enum IntEnum : int { 57 | OneInt, 58 | TwoInt 59 | }; 60 | 61 | enum LongEnum : long long { 62 | OneLong, 63 | TwoLong 64 | }; 65 | 66 | enum DefaultEnum { 67 | OneDefault, 68 | TwoDefault 69 | }; 70 | 71 | // Non enum type 72 | struct Employee { 73 | int id; 74 | int salary; 75 | }; 76 | )"; 77 | 78 | GetAllTopLevelDecls(code, Decls); 79 | 80 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[0])), "bool"); 81 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[1])), "char"); 82 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[2])), "int"); 83 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[3])), "long long"); 84 | #ifdef _WIN32 85 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[4])), 86 | "int"); 87 | #else 88 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[4])), "unsigned int"); 89 | #endif 90 | EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumScope(Decls[5])),"NULL TYPE"); 91 | } 92 | 93 | TYPED_TEST(CppInterOpTest, EnumReflectionTestGetIntegerTypeFromEnumType) { 94 | std::vector Decls; 95 | std::string code = R"( 96 | enum Switch : bool { 97 | OFF, 98 | ON 99 | }; 100 | 101 | enum CharEnum : char { 102 | OneChar, 103 | TwoChar 104 | }; 105 | 106 | enum IntEnum : int { 107 | OneInt, 108 | TwoInt 109 | }; 110 | 111 | enum LongEnum : long long { 112 | OneLong, 113 | TwoLong 114 | }; 115 | 116 | enum DefaultEnum { 117 | OneDefault, 118 | TwoDefault 119 | }; 120 | 121 | struct Employee { 122 | int id; 123 | int salary; 124 | }; 125 | 126 | Switch s; 127 | CharEnum ch; 128 | IntEnum in; 129 | LongEnum lng; 130 | DefaultEnum def; 131 | Employee emp; 132 | )"; 133 | 134 | GetAllTopLevelDecls(code, Decls); 135 | 136 | auto get_int_type_from_enum_var = [](Decl *D) { 137 | return Cpp::GetTypeAsString(Cpp::GetIntegerTypeFromEnumType(Cpp::GetVariableType(D))); 138 | }; 139 | 140 | EXPECT_EQ(get_int_type_from_enum_var(Decls[5]), "NULL TYPE"); // When a nullptr is returned by GetVariableType() 141 | EXPECT_EQ(get_int_type_from_enum_var(Decls[6]), "bool"); 142 | EXPECT_EQ(get_int_type_from_enum_var(Decls[7]), "char"); 143 | EXPECT_EQ(get_int_type_from_enum_var(Decls[8]), "int"); 144 | EXPECT_EQ(get_int_type_from_enum_var(Decls[9]), "long long"); 145 | #ifdef _WIN32 146 | EXPECT_EQ(get_int_type_from_enum_var(Decls[10]), "int"); 147 | #else 148 | EXPECT_EQ(get_int_type_from_enum_var(Decls[10]), "unsigned int"); 149 | #endif 150 | EXPECT_EQ(get_int_type_from_enum_var(Decls[11]), "NULL TYPE"); // When a non Enum Type variable is used 151 | } 152 | 153 | TYPED_TEST(CppInterOpTest, EnumReflectionTestGetEnumConstants) { 154 | std::vector Decls; 155 | std::string code = R"( 156 | enum ZeroEnum { 157 | }; 158 | 159 | enum OneEnum { 160 | One_OneEnum, 161 | }; 162 | 163 | enum TwoEnum { 164 | One_TwoEnum, 165 | Two_TwoEnum, 166 | }; 167 | 168 | enum ThreeEnum { 169 | One_ThreeEnum, 170 | Two_ThreeEnum, 171 | Three_ThreeEnum, 172 | }; 173 | 174 | enum FourEnum { 175 | One_FourEnum, 176 | Two_FourEnum, 177 | Three_FourEnum, 178 | Four_FourEnum, 179 | }; 180 | 181 | struct Employee { 182 | int id; 183 | int salary; 184 | }; 185 | )"; 186 | 187 | GetAllTopLevelDecls(code, Decls); 188 | 189 | EXPECT_EQ(Cpp::GetEnumConstants(Decls[0]).size(), 0); 190 | EXPECT_EQ(Cpp::GetEnumConstants(Decls[1]).size(), 1); 191 | EXPECT_EQ(Cpp::GetEnumConstants(Decls[2]).size(), 2); 192 | EXPECT_EQ(Cpp::GetEnumConstants(Decls[3]).size(), 3); 193 | EXPECT_EQ(Cpp::GetEnumConstants(Decls[4]).size(), 4); 194 | EXPECT_EQ(Cpp::GetEnumConstants(Decls[5]).size(), 0); 195 | } 196 | 197 | TYPED_TEST(CppInterOpTest, EnumReflectionTestGetEnumConstantType) { 198 | std::vector Decls; 199 | std::string code = R"( 200 | enum Enum0 { 201 | Constant0 = 0 202 | }; 203 | 204 | enum class Enum1 { 205 | Constant1 = 1 206 | }; 207 | )"; 208 | 209 | GetAllTopLevelDecls(code, Decls); 210 | 211 | auto get_enum_constant_type_as_str = [](Cpp::TCppScope_t enum_constant) { 212 | return Cpp::GetTypeAsString(Cpp::GetEnumConstantType(enum_constant)); 213 | }; 214 | 215 | auto EnumConstants0 = Cpp::GetEnumConstants(Decls[0]); 216 | 217 | EXPECT_EQ(get_enum_constant_type_as_str(EnumConstants0[0]), "Enum0"); 218 | 219 | auto EnumConstants1 = Cpp::GetEnumConstants(Decls[1]); 220 | 221 | EXPECT_EQ(get_enum_constant_type_as_str(EnumConstants1[0]), "Enum1"); 222 | 223 | EXPECT_EQ(get_enum_constant_type_as_str(Decls[1]), "NULL TYPE"); 224 | 225 | EXPECT_EQ(get_enum_constant_type_as_str(nullptr), "NULL TYPE"); 226 | } 227 | 228 | TYPED_TEST(CppInterOpTest, EnumReflectionTestGetEnumConstantValue) { 229 | std::vector Decls; 230 | std::string code = R"( 231 | enum Counter { 232 | Zero = 0, 233 | One, 234 | FiftyTwo = 52, 235 | FiftyThree, 236 | FiftyFour, 237 | MinusTen = -10, 238 | MinusNine 239 | }; 240 | int a = 10; 241 | )"; 242 | 243 | GetAllTopLevelDecls(code, Decls); 244 | auto EnumConstants = Cpp::GetEnumConstants(Decls[0]); 245 | 246 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[0]), 0); 247 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[1]), 1); 248 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[2]), 52); 249 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[3]), 53); 250 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[4]), 54); 251 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[5]), -10); 252 | EXPECT_EQ(Cpp::GetEnumConstantValue(EnumConstants[6]), -9); 253 | EXPECT_EQ(Cpp::GetEnumConstantValue(Decls[1]), 0); // Checking value of non enum constant 254 | } 255 | 256 | TYPED_TEST(CppInterOpTest, EnumReflectionTestGetEnums) { 257 | std::string code = R"( 258 | enum Color { 259 | Red, 260 | Green, 261 | Blue 262 | }; 263 | 264 | enum Days { 265 | Monday, 266 | Tuesday, 267 | Wednesday, 268 | Thursday, 269 | }; 270 | 271 | namespace Animals { 272 | enum AnimalType { 273 | Dog, 274 | Cat, 275 | Bird 276 | }; 277 | 278 | enum Months { 279 | January, 280 | February, 281 | March 282 | }; 283 | } 284 | 285 | class myClass { 286 | public: 287 | enum Color { 288 | Red, 289 | Green, 290 | Blue 291 | }; 292 | }; 293 | 294 | int myVariable; 295 | )"; 296 | 297 | TestFixture::CreateInterpreter(); 298 | Interp->declare(code); 299 | std::vector enumNames1, enumNames2, enumNames3, enumNames4; 300 | Cpp::TCppScope_t globalscope = Cpp::GetScope("", 0); 301 | Cpp::TCppScope_t Animals_scope = Cpp::GetScope("Animals", 0); 302 | Cpp::TCppScope_t myClass_scope = Cpp::GetScope("myClass", 0); 303 | Cpp::TCppScope_t unsupported_scope = Cpp::GetScope("myVariable", 0); 304 | 305 | Cpp::GetEnums(globalscope,enumNames1); 306 | Cpp::GetEnums(Animals_scope,enumNames2); 307 | Cpp::GetEnums(myClass_scope, enumNames3); 308 | Cpp::GetEnums(unsupported_scope, enumNames4); 309 | 310 | // Check if the enum names are correctly retrieved 311 | EXPECT_TRUE(std::find(enumNames1.begin(), enumNames1.end(), "Color") != enumNames1.end()); 312 | EXPECT_TRUE(std::find(enumNames1.begin(), enumNames1.end(), "Days") != enumNames1.end()); 313 | EXPECT_TRUE(std::find(enumNames2.begin(), enumNames2.end(), "AnimalType") != enumNames2.end()); 314 | EXPECT_TRUE(std::find(enumNames2.begin(), enumNames2.end(), "Months") != enumNames2.end()); 315 | EXPECT_TRUE(std::find(enumNames3.begin(), enumNames3.end(), "Color") != enumNames3.end()); 316 | EXPECT_TRUE(enumNames4.empty()); 317 | } 318 | -------------------------------------------------------------------------------- /lib/CppInterOp/DynamicLibraryManager.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------*- C++ -*- 2 | // CLING - the C++ LLVM-based InterpreterG :) 3 | // author: Vassil Vassilev 4 | // 5 | // This file is dual-licensed: you can choose to license it under the University 6 | // of Illinois Open Source License or the GNU Lesser General Public License. See 7 | // LICENSE.TXT for details. 8 | //------------------------------------------------------------------------------ 9 | 10 | #ifndef CPPINTEROP_DYNAMIC_LIBRARY_MANAGER_H 11 | #define CPPINTEROP_DYNAMIC_LIBRARY_MANAGER_H 12 | 13 | #include "llvm/ADT/ArrayRef.h" 14 | #include "llvm/ADT/DenseMap.h" 15 | #include "llvm/ADT/StringRef.h" 16 | #include "llvm/ADT/StringSet.h" 17 | #include "llvm/Support/Debug.h" 18 | #include "llvm/Support/Path.h" 19 | 20 | namespace Cpp { 21 | class Dyld; 22 | class InterpreterCallbacks; 23 | 24 | ///\brief A helper class managing dynamic shared objects. 25 | /// 26 | class DynamicLibraryManager { 27 | public: 28 | ///\brief Describes the result of loading a library. 29 | /// 30 | enum LoadLibResult { 31 | kLoadLibSuccess, ///< library loaded successfully 32 | kLoadLibAlreadyLoaded, ///< library was already loaded 33 | kLoadLibNotFound, ///< library was not found 34 | kLoadLibLoadError, ///< loading the library failed 35 | kLoadLibNumResults 36 | }; 37 | 38 | /// Describes the library search paths. 39 | struct SearchPathInfo { 40 | /// The search path. 41 | /// 42 | std::string Path; 43 | 44 | /// True if the Path is on the LD_LIBRARY_PATH. 45 | /// 46 | bool IsUser; 47 | 48 | bool operator==(const SearchPathInfo& Other) const { 49 | return IsUser == Other.IsUser && Path == Other.Path; 50 | } 51 | }; 52 | using SearchPathInfos = llvm::SmallVector; 53 | 54 | private: 55 | typedef void* DyLibHandle; 56 | typedef llvm::DenseMap DyLibs; 57 | ///\brief DynamicLibraries loaded by this Interpreter. 58 | /// 59 | DyLibs m_DyLibs; 60 | llvm::StringSet<> m_LoadedLibraries; 61 | 62 | ///\brief System's include path, get initialized at construction time. 63 | /// 64 | SearchPathInfos m_SearchPaths; 65 | 66 | InterpreterCallbacks* m_Callbacks = nullptr; 67 | 68 | Dyld* m_Dyld = nullptr; 69 | 70 | ///\brief Concatenates current include paths and the system include paths 71 | /// and performs a lookup for the filename. 72 | /// See more information for RPATH and RUNPATH: 73 | /// https://en.wikipedia.org/wiki/Rpath 74 | ///\param[in] libStem - The filename being looked up 75 | ///\param[in] RPath - RPATH as provided by loader library, searching for 76 | /// libStem \param[in] RunPath - RUNPATH as provided by loader library, 77 | /// searching for libStem \param[in] libLoader - The library that loads 78 | /// libStem. Use "" for main program. 79 | /// 80 | ///\returns the canonical path to the file or empty string if not found 81 | /// 82 | std::string 83 | lookupLibInPaths(llvm::StringRef libStem, 84 | llvm::SmallVector RPath = {}, 85 | llvm::SmallVector RunPath = {}, 86 | llvm::StringRef libLoader = "") const; 87 | 88 | ///\brief Concatenates current include paths and the system include paths 89 | /// and performs a lookup for the filename. If still not found it tries to 90 | /// add the platform-specific extensions (such as so, dll, dylib) and 91 | /// retries the lookup (from lookupLibInPaths) 92 | /// See more information for RPATH and RUNPATH: 93 | /// https://en.wikipedia.org/wiki/Rpath 94 | ///\param[in] filename - The filename being looked up 95 | ///\param[in] RPath - RPATH as provided by loader library, searching for 96 | /// libStem \param[in] RunPath - RUNPATH as provided by loader library, 97 | /// searching for libStem \param[in] libLoader - The library that loads 98 | /// libStem. Use "" for main program. 99 | /// 100 | ///\returns the canonical path to the file or empty string if not found 101 | /// 102 | std::string 103 | lookupLibMaybeAddExt(llvm::StringRef filename, 104 | llvm::SmallVector RPath = {}, 105 | llvm::SmallVector RunPath = {}, 106 | llvm::StringRef libLoader = "") const; 107 | 108 | /// On a success returns to full path to a shared object that holds the 109 | /// symbol pointed by func. 110 | /// 111 | static std::string getSymbolLocation(void* func); 112 | 113 | public: 114 | DynamicLibraryManager(); 115 | ~DynamicLibraryManager(); 116 | DynamicLibraryManager(const DynamicLibraryManager&) = delete; 117 | DynamicLibraryManager& operator=(const DynamicLibraryManager&) = delete; 118 | 119 | InterpreterCallbacks* getCallbacks() { return m_Callbacks; } 120 | const InterpreterCallbacks* getCallbacks() const { return m_Callbacks; } 121 | void setCallbacks(InterpreterCallbacks* C) { m_Callbacks = C; } 122 | 123 | ///\brief Returns the system include paths. 124 | /// 125 | ///\returns System include paths. 126 | /// 127 | const SearchPathInfos& getSearchPaths() const { return m_SearchPaths; } 128 | 129 | void addSearchPath(llvm::StringRef dir, bool isUser = true, 130 | bool prepend = false) { 131 | if (!dir.empty()) { 132 | for (auto& item : m_SearchPaths) 133 | if (dir == item.Path) 134 | return; 135 | auto pos = prepend ? m_SearchPaths.begin() : m_SearchPaths.end(); 136 | m_SearchPaths.insert(pos, SearchPathInfo{dir.str(), isUser}); 137 | } 138 | } 139 | 140 | ///\brief Looks up a library taking into account the current include paths 141 | /// and the system include paths. 142 | /// See more information for RPATH and RUNPATH: 143 | /// https://en.wikipedia.org/wiki/Rpath 144 | ///\param[in] libStem - The filename being looked up 145 | ///\param[in] RPath - RPATH as provided by loader library, searching for 146 | /// libStem \param[in] RunPath - RUNPATH as provided by loader library, 147 | /// searching for libStem \param[in] libLoader - The library that loads 148 | /// libStem. Use "" for main program. \param[in] variateLibStem - If this 149 | /// param is true, and libStem is "L", then 150 | /// we search for "L", "libL", "L.so", "libL.so"", etc. 151 | /// 152 | ///\returns the canonical path to the file or empty string if not found 153 | /// 154 | std::string lookupLibrary(llvm::StringRef libStem, 155 | llvm::SmallVector RPath = {}, 156 | llvm::SmallVector RunPath = {}, 157 | llvm::StringRef libLoader = "", 158 | bool variateLibStem = true) const; 159 | 160 | ///\brief Loads a shared library. 161 | /// 162 | ///\param [in] libStem - The file to load. 163 | ///\param [in] permanent - If false, the file can be unloaded later. 164 | ///\param [in] resolved - Whether libStem is an absolute path or resolved 165 | /// from a previous call to DynamicLibraryManager::lookupLibrary 166 | /// 167 | ///\returns kLoadLibSuccess on success, kLoadLibAlreadyLoaded if the library 168 | /// was already loaded, kLoadLibError if the library cannot be found or any 169 | /// other error was encountered. 170 | /// 171 | LoadLibResult loadLibrary(llvm::StringRef, bool permanent, 172 | bool resolved = false); 173 | 174 | void unloadLibrary(llvm::StringRef libStem); 175 | 176 | ///\brief Returns true if the file was a dynamic library and it was already 177 | /// loaded. 178 | /// 179 | bool isLibraryLoaded(llvm::StringRef fullPath) const; 180 | 181 | /// Initialize the dyld. 182 | /// 183 | ///\param [in] shouldPermanentlyIgnore - a callback deciding if a library 184 | /// should be ignored from the result set. Useful for ignoring 185 | /// dangerous libraries such as the ones overriding malloc. 186 | /// 187 | void 188 | initializeDyld(std::function shouldPermanentlyIgnore); 189 | 190 | /// Find the first not-yet-loaded shared object that contains the symbol 191 | /// 192 | ///\param[in] mangledName - the mangled name to look for. 193 | ///\param[in] searchSystem - whether to descend into system libraries. 194 | /// 195 | ///\returns the library name if found, and empty string otherwise. 196 | /// 197 | std::string searchLibrariesForSymbol(llvm::StringRef mangledName, 198 | bool searchSystem = true) const; 199 | 200 | void dump(llvm::raw_ostream* S = nullptr) const; 201 | 202 | /// On a success returns to full path to a shared object that holds the 203 | /// symbol pointed by func. 204 | /// 205 | template static std::string getSymbolLocation(T func) { 206 | static_assert(std::is_pointer::value, "Must be a function pointer!"); 207 | return getSymbolLocation(reinterpret_cast(func)); 208 | } 209 | 210 | static std::string normalizePath(llvm::StringRef path); 211 | 212 | /// Returns true if file is a shared library. 213 | /// 214 | ///\param[in] libFullPath - the full path to file. 215 | /// 216 | ///\param[out] exists - sets if the file exists. Useful to distinguish if it 217 | /// is a library but of incompatible file format. 218 | /// 219 | static bool isSharedLibrary(llvm::StringRef libFullPath, 220 | bool* exists = nullptr); 221 | }; 222 | } // end namespace Cpp 223 | 224 | #endif // CPPINTEROP_DYNAMIC_LIBRARY_MANAGER_H 225 | -------------------------------------------------------------------------------- /docs/doxygen.css: -------------------------------------------------------------------------------- 1 | BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { 2 | font-family: Verdana,Geneva,Arial,Helvetica,sans-serif; 3 | } 4 | BODY,TD { 5 | font-size: 90%; 6 | } 7 | H1 { 8 | text-align: center; 9 | font-size: 140%; 10 | font-weight: bold; 11 | } 12 | H2 { 13 | font-size: 120%; 14 | font-style: italic; 15 | } 16 | H3 { 17 | font-size: 100%; 18 | } 19 | CAPTION { font-weight: bold } 20 | DIV.qindex { 21 | width: 100%; 22 | background-color: #eeeeff; 23 | border: 1px solid #b0b0b0; 24 | text-align: center; 25 | margin: 2px; 26 | padding: 2px; 27 | line-height: 140%; 28 | } 29 | DIV.nav { 30 | width: 100%; 31 | background-color: #eeeeff; 32 | border: 1px solid #b0b0b0; 33 | text-align: center; 34 | margin: 2px; 35 | padding: 2px; 36 | line-height: 140%; 37 | } 38 | DIV.navtab { 39 | background-color: #eeeeff; 40 | border: 1px solid #b0b0b0; 41 | text-align: center; 42 | margin: 2px; 43 | margin-right: 15px; 44 | padding: 2px; 45 | } 46 | TD.navtab { 47 | font-size: 70%; 48 | } 49 | A.qindex { 50 | text-decoration: none; 51 | font-weight: bold; 52 | color: #1A419D; 53 | } 54 | A.qindex:visited { 55 | text-decoration: none; 56 | font-weight: bold; 57 | color: #1A419D 58 | } 59 | A.qindex:hover { 60 | text-decoration: none; 61 | background-color: #ddddff; 62 | } 63 | A.qindexHL { 64 | text-decoration: none; 65 | font-weight: bold; 66 | background-color: #6666cc; 67 | color: #ffffff; 68 | border: 1px double #9295C2; 69 | } 70 | A.qindexHL:hover { 71 | text-decoration: none; 72 | background-color: #6666cc; 73 | color: #ffffff; 74 | } 75 | A.qindexHL:visited { 76 | text-decoration: none; background-color: #6666cc; color: #ffffff } 77 | A.el { text-decoration: none; font-weight: bold } 78 | A.elRef { font-weight: bold } 79 | A.code:link { text-decoration: none; font-weight: normal; color: #0000FF} 80 | A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} 81 | A.codeRef:link { font-weight: normal; color: #0000FF} 82 | A.codeRef:visited { font-weight: normal; color: #0000FF} 83 | A:hover { text-decoration: none; background-color: #f2f2ff } 84 | DL.el { margin-left: -1cm } 85 | .fragment { 86 | font-family: Fixed, monospace; 87 | font-size: 95%; 88 | } 89 | PRE.fragment { 90 | border: 1px solid #CCCCCC; 91 | background-color: #f5f5f5; 92 | margin-top: 4px; 93 | margin-bottom: 4px; 94 | margin-left: 2px; 95 | margin-right: 8px; 96 | padding-left: 6px; 97 | padding-right: 6px; 98 | padding-top: 4px; 99 | padding-bottom: 4px; 100 | } 101 | DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } 102 | TD.md { background-color: #F4F4FB; font-weight: bold; } 103 | TD.mdPrefix { 104 | background-color: #F4F4FB; 105 | color: #606060; 106 | font-size: 80%; 107 | } 108 | TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } 109 | TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } 110 | DIV.groupHeader { 111 | margin-left: 16px; 112 | margin-top: 12px; 113 | margin-bottom: 6px; 114 | font-weight: bold; 115 | } 116 | DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } 117 | BODY { 118 | background: white; 119 | color: black; 120 | margin-right: 20px; 121 | margin-left: 20px; 122 | } 123 | TD.indexkey { 124 | background-color: #eeeeff; 125 | font-weight: bold; 126 | padding-right : 10px; 127 | padding-top : 2px; 128 | padding-left : 10px; 129 | padding-bottom : 2px; 130 | margin-left : 0px; 131 | margin-right : 0px; 132 | margin-top : 2px; 133 | margin-bottom : 2px; 134 | border: 1px solid #CCCCCC; 135 | } 136 | TD.indexvalue { 137 | background-color: #eeeeff; 138 | font-style: italic; 139 | padding-right : 10px; 140 | padding-top : 2px; 141 | padding-left : 10px; 142 | padding-bottom : 2px; 143 | margin-left : 0px; 144 | margin-right : 0px; 145 | margin-top : 2px; 146 | margin-bottom : 2px; 147 | border: 1px solid #CCCCCC; 148 | } 149 | TR.memlist { 150 | background-color: #f0f0f0; 151 | } 152 | P.formulaDsp { text-align: center; } 153 | IMG.formulaDsp { } 154 | IMG.formulaInl { vertical-align: middle; } 155 | SPAN.keyword { color: #008000 } 156 | SPAN.keywordtype { color: #604020 } 157 | SPAN.keywordflow { color: #e08000 } 158 | SPAN.comment { color: #800000 } 159 | SPAN.preprocessor { color: #806020 } 160 | SPAN.stringliteral { color: #002080 } 161 | SPAN.charliteral { color: #008080 } 162 | .mdTable { 163 | border: 1px solid #868686; 164 | background-color: #F4F4FB; 165 | } 166 | .mdRow { 167 | padding: 8px 10px; 168 | } 169 | .mdescLeft { 170 | padding: 0px 8px 4px 8px; 171 | font-size: 80%; 172 | font-style: italic; 173 | background-color: #FAFAFA; 174 | border-top: 1px none #E0E0E0; 175 | border-right: 1px none #E0E0E0; 176 | border-bottom: 1px none #E0E0E0; 177 | border-left: 1px none #E0E0E0; 178 | margin: 0px; 179 | } 180 | .mdescRight { 181 | padding: 0px 8px 4px 8px; 182 | font-size: 80%; 183 | font-style: italic; 184 | background-color: #FAFAFA; 185 | border-top: 1px none #E0E0E0; 186 | border-right: 1px none #E0E0E0; 187 | border-bottom: 1px none #E0E0E0; 188 | border-left: 1px none #E0E0E0; 189 | margin: 0px; 190 | } 191 | .memItemLeft { 192 | padding: 1px 0px 0px 8px; 193 | margin: 4px; 194 | border-top-width: 1px; 195 | border-right-width: 1px; 196 | border-bottom-width: 1px; 197 | border-left-width: 1px; 198 | border-top-color: #E0E0E0; 199 | border-right-color: #E0E0E0; 200 | border-bottom-color: #E0E0E0; 201 | border-left-color: #E0E0E0; 202 | border-top-style: solid; 203 | border-right-style: none; 204 | border-bottom-style: none; 205 | border-left-style: none; 206 | background-color: #FAFAFA; 207 | font-size: 80%; 208 | } 209 | .memItemRight { 210 | padding: 1px 8px 0px 8px; 211 | margin: 4px; 212 | border-top-width: 1px; 213 | border-right-width: 1px; 214 | border-bottom-width: 1px; 215 | border-left-width: 1px; 216 | border-top-color: #E0E0E0; 217 | border-right-color: #E0E0E0; 218 | border-bottom-color: #E0E0E0; 219 | border-left-color: #E0E0E0; 220 | border-top-style: solid; 221 | border-right-style: none; 222 | border-bottom-style: none; 223 | border-left-style: none; 224 | background-color: #FAFAFA; 225 | font-size: 80%; 226 | } 227 | .memTemplItemLeft { 228 | padding: 1px 0px 0px 8px; 229 | margin: 4px; 230 | border-top-width: 1px; 231 | border-right-width: 1px; 232 | border-bottom-width: 1px; 233 | border-left-width: 1px; 234 | border-top-color: #E0E0E0; 235 | border-right-color: #E0E0E0; 236 | border-bottom-color: #E0E0E0; 237 | border-left-color: #E0E0E0; 238 | border-top-style: none; 239 | border-right-style: none; 240 | border-bottom-style: none; 241 | border-left-style: none; 242 | background-color: #FAFAFA; 243 | font-size: 80%; 244 | } 245 | .memTemplItemRight { 246 | padding: 1px 8px 0px 8px; 247 | margin: 4px; 248 | border-top-width: 1px; 249 | border-right-width: 1px; 250 | border-bottom-width: 1px; 251 | border-left-width: 1px; 252 | border-top-color: #E0E0E0; 253 | border-right-color: #E0E0E0; 254 | border-bottom-color: #E0E0E0; 255 | border-left-color: #E0E0E0; 256 | border-top-style: none; 257 | border-right-style: none; 258 | border-bottom-style: none; 259 | border-left-style: none; 260 | background-color: #FAFAFA; 261 | font-size: 80%; 262 | } 263 | .memTemplParams { 264 | padding: 1px 0px 0px 8px; 265 | margin: 4px; 266 | border-top-width: 1px; 267 | border-right-width: 1px; 268 | border-bottom-width: 1px; 269 | border-left-width: 1px; 270 | border-top-color: #E0E0E0; 271 | border-right-color: #E0E0E0; 272 | border-bottom-color: #E0E0E0; 273 | border-left-color: #E0E0E0; 274 | border-top-style: solid; 275 | border-right-style: none; 276 | border-bottom-style: none; 277 | border-left-style: none; 278 | color: #606060; 279 | background-color: #FAFAFA; 280 | font-size: 80%; 281 | } 282 | .search { color: #003399; 283 | font-weight: bold; 284 | } 285 | FORM.search { 286 | margin-bottom: 0px; 287 | margin-top: 0px; 288 | } 289 | INPUT.search { font-size: 75%; 290 | color: #000080; 291 | font-weight: normal; 292 | background-color: #eeeeff; 293 | } 294 | TD.tiny { font-size: 75%; 295 | } 296 | a { 297 | color: #252E78; 298 | } 299 | a:visited { 300 | color: #3D2185; 301 | } 302 | .dirtab { padding: 4px; 303 | border-collapse: collapse; 304 | border: 1px solid #b0b0b0; 305 | } 306 | TH.dirtab { background: #eeeeff; 307 | font-weight: bold; 308 | } 309 | HR { height: 1px; 310 | border: none; 311 | border-top: 1px solid black; 312 | } 313 | 314 | /* 315 | * LLVM Modifications. 316 | * Note: Everything above here is generated with "doxygen -w html" command. See 317 | * "doxygen --help" for details. What follows are CSS overrides for LLVM 318 | * specific formatting. We want to keep the above so it can be replaced with 319 | * subsequent doxygen upgrades. 320 | */ 321 | 322 | .footer { 323 | font-size: 80%; 324 | font-weight: bold; 325 | text-align: center; 326 | vertical-align: middle; 327 | } 328 | .title { 329 | font-size: 25pt; 330 | color: black; background: url("../img/lines.gif"); 331 | font-weight: bold; 332 | border-width: 1px; 333 | border-style: solid none solid none; 334 | text-align: center; 335 | vertical-align: middle; 336 | padding-left: 8pt; 337 | padding-top: 1px; 338 | padding-bottom: 2px 339 | } 340 | A:link { 341 | cursor: pointer; 342 | text-decoration: none; 343 | font-weight: bolder; 344 | } 345 | A:visited { 346 | cursor: pointer; 347 | text-decoration: underline; 348 | font-weight: bolder; 349 | } 350 | A:hover { 351 | cursor: pointer; 352 | text-decoration: underline; 353 | font-weight: bolder; 354 | } 355 | A:active { 356 | cursor: pointer; 357 | text-decoration: underline; 358 | font-weight: bolder; 359 | font-style: italic; 360 | } 361 | H1 { 362 | text-align: center; 363 | font-size: 140%; 364 | font-weight: bold; 365 | } 366 | H2 { 367 | font-size: 120%; 368 | font-style: italic; 369 | } 370 | H3 { 371 | font-size: 100%; 372 | } 373 | 374 | H2, H3 { 375 | border-bottom: 2px solid; 376 | margin-top: 2em; 377 | } 378 | 379 | A.qindex {} 380 | A.qindexRef {} 381 | A.el { text-decoration: none; font-weight: bold } 382 | A.elRef { font-weight: bold } 383 | A.code { text-decoration: none; font-weight: normal; color: #4444ee } 384 | A.codeRef { font-weight: normal; color: #4444ee } 385 | 386 | div.memitem { 387 | border: 1px solid #999999; 388 | margin-top: 1.0em; 389 | margin-bottom: 1.0em; 390 | -webkit-border-radius: 0.5em; 391 | -webkit-box-shadow: 3px 3px 6px #777777; 392 | -moz-border-radius: 0.5em; 393 | -moz-box-shadow: black 3px 3px 3px; 394 | } 395 | 396 | div.memproto { 397 | background-color: #E3E4E5; 398 | padding: 0.25em 0.5em; 399 | -webkit-border-top-left-radius: 0.5em; 400 | -webkit-border-top-right-radius: 0.5em; 401 | -moz-border-radius-topleft: 0.5em; 402 | -moz-border-radius-topright: 0.5em; 403 | } 404 | 405 | div.memdoc { 406 | padding-left: 1em; 407 | padding-right: 1em; 408 | } 409 | -------------------------------------------------------------------------------- /include/clang-c/CXCppInterOp.h: -------------------------------------------------------------------------------- 1 | // NOLINTBEGIN() 2 | #ifndef LLVM_CLANG_C_CXCPPINTEROP_H 3 | #define LLVM_CLANG_C_CXCPPINTEROP_H 4 | 5 | #include "clang-c/CXErrorCode.h" 6 | #include "clang-c/CXString.h" 7 | #include "clang-c/ExternC.h" 8 | #include "clang-c/Index.h" 9 | #include "clang-c/Platform.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | LLVM_CLANG_C_EXTERN_C_BEGIN 16 | 17 | /** 18 | * \defgroup CPPINTEROP_INTERPRETER_MANIP Interpreter manipulations 19 | * 20 | * @{ 21 | */ 22 | 23 | /** 24 | * An opaque pointer representing an interpreter context. 25 | */ 26 | typedef struct CXInterpreterImpl* CXInterpreter; 27 | 28 | /** 29 | * Create a Clang interpreter instance from the given arguments. 30 | * 31 | * \param argv The arguments that would be passed to the interpreter. 32 | * 33 | * \param argc The number of arguments in \c argv. 34 | * 35 | * \returns a \c CXInterpreter. 36 | */ 37 | CINDEX_LINKAGE CXInterpreter clang_createInterpreter(const char* const* argv, 38 | int argc); 39 | 40 | typedef void* TInterp_t; 41 | 42 | /** 43 | * Bridge between C API and C++ API. 44 | * 45 | * \returns a \c CXInterpreter. 46 | */ 47 | CINDEX_LINKAGE CXInterpreter clang_createInterpreterFromRawPtr(TInterp_t I); 48 | 49 | /** 50 | * Returns a pointer to the underlying interpreter. 51 | */ 52 | CINDEX_LINKAGE void* clang_Interpreter_getClangInterpreter(CXInterpreter I); 53 | 54 | /** 55 | * Returns a \c TInterp_t and takes the ownership. 56 | */ 57 | CINDEX_LINKAGE TInterp_t 58 | clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I); 59 | 60 | /** 61 | * Undo N previous incremental inputs. 62 | */ 63 | CINDEX_LINKAGE enum CXErrorCode clang_Interpreter_undo(CXInterpreter I, 64 | unsigned int N); 65 | 66 | /** 67 | * Dispose of the given interpreter context. 68 | */ 69 | CINDEX_LINKAGE void clang_Interpreter_dispose(CXInterpreter I); 70 | 71 | /** 72 | * Describes the return result of the different routines that do the incremental 73 | * compilation. 74 | */ 75 | typedef enum { 76 | /** 77 | * The compilation was successful. 78 | */ 79 | CXInterpreter_Success = 0, 80 | /** 81 | * The compilation failed. 82 | */ 83 | CXInterpreter_Failure = 1, 84 | /** 85 | * More more input is expected. 86 | */ 87 | CXInterpreter_MoreInputExpected = 2, 88 | } CXInterpreter_CompilationResult; 89 | 90 | /** 91 | * Add a search path to the interpreter. 92 | * 93 | * \param I The interpreter. 94 | * 95 | * \param dir The directory to add. 96 | * 97 | * \param isUser Whether the directory is a user directory. 98 | * 99 | * \param prepend Whether to prepend the directory to the search path. 100 | */ 101 | CINDEX_LINKAGE void clang_Interpreter_addSearchPath(CXInterpreter I, 102 | const char* dir, 103 | bool isUser, bool prepend); 104 | 105 | /** 106 | * Add an include path. 107 | * 108 | * \param I The interpreter. 109 | * 110 | * \param dir The directory to add. 111 | */ 112 | CINDEX_LINKAGE void clang_Interpreter_addIncludePath(CXInterpreter I, 113 | const char* dir); 114 | 115 | /** 116 | * Declares a code snippet in \c code and does not execute it. 117 | * 118 | * \param I The interpreter. 119 | * 120 | * \param code The code snippet to declare. 121 | * 122 | * \param silent Whether to suppress the diagnostics or not 123 | * 124 | * \returns a \c CXErrorCode. 125 | */ 126 | CINDEX_LINKAGE enum CXErrorCode 127 | clang_Interpreter_declare(CXInterpreter I, const char* code, bool silent); 128 | 129 | /** 130 | * Declares and executes a code snippet in \c code. 131 | * 132 | * \param I The interpreter. 133 | * 134 | * \param code The code snippet to execute. 135 | * 136 | * \returns a \c CXErrorCode. 137 | */ 138 | CINDEX_LINKAGE enum CXErrorCode clang_Interpreter_process(CXInterpreter I, 139 | const char* code); 140 | 141 | /** 142 | * An opaque pointer representing a lightweight struct that is used for carrying 143 | * execution results. 144 | */ 145 | typedef void* CXValue; 146 | 147 | /** 148 | * Create a CXValue. 149 | * 150 | * \returns a \c CXValue. 151 | */ 152 | CINDEX_LINKAGE CXValue clang_createValue(void); 153 | 154 | /** 155 | * Dispose of the given CXValue. 156 | * 157 | * \param V The CXValue to dispose. 158 | */ 159 | CINDEX_LINKAGE void clang_Value_dispose(CXValue V); 160 | 161 | /** 162 | * Declares, executes and stores the execution result to \c V. 163 | * 164 | * \param[in] I The interpreter. 165 | * 166 | * \param[in] code The code snippet to evaluate. 167 | * 168 | * \param[out] V The value to store the execution result. 169 | * 170 | * \returns a \c CXErrorCode. 171 | */ 172 | CINDEX_LINKAGE enum CXErrorCode 173 | clang_Interpreter_evaluate(CXInterpreter I, const char* code, CXValue V); 174 | 175 | /** 176 | * Looks up the library if access is enabled. 177 | * 178 | * \param I The interpreter. 179 | * 180 | * \param lib_name The name of the library to lookup. 181 | * 182 | * \returns the path to the library. 183 | */ 184 | CINDEX_LINKAGE CXString clang_Interpreter_lookupLibrary(CXInterpreter I, 185 | const char* lib_name); 186 | 187 | /** 188 | * Finds \c lib_stem considering the list of search paths and loads it by 189 | * calling dlopen. 190 | * 191 | * \param I The interpreter. 192 | * 193 | * \param lib_stem The stem of the library to load. 194 | * 195 | * \param lookup Whether to lookup the library or not. 196 | * 197 | * \returns a \c CXInterpreter_CompilationResult. 198 | */ 199 | CINDEX_LINKAGE CXInterpreter_CompilationResult clang_Interpreter_loadLibrary( 200 | CXInterpreter I, const char* lib_stem, bool lookup); 201 | 202 | /** 203 | * Finds \c lib_stem considering the list of search paths and unloads it by 204 | * calling dlclose. 205 | * 206 | * \param I The interpreter. 207 | * 208 | * \param lib_stem The stem of the library to unload. 209 | */ 210 | CINDEX_LINKAGE void clang_Interpreter_unloadLibrary(CXInterpreter I, 211 | const char* lib_stem); 212 | 213 | /** 214 | * @} 215 | */ 216 | 217 | /** 218 | * \defgroup CPPINTEROP_SCOPE_MANIP Scope manipulations 219 | * 220 | * @{ 221 | */ 222 | 223 | /** 224 | * A fake CXCursor for working with the interpreter. 225 | * It has the same structure as CXCursor, but unlike CXCursor, it stores a 226 | * handle to the interpreter in the third slot of the data field. 227 | * This pave the way for upstreaming features to the LLVM project. 228 | */ 229 | typedef struct { 230 | enum CXCursorKind kind; 231 | int xdata; 232 | const void* data[3]; 233 | } CXScope; 234 | 235 | // for debugging purposes 236 | CINDEX_LINKAGE void clang_scope_dump(CXScope S); 237 | 238 | /** 239 | * Checks if a class has a default constructor. 240 | */ 241 | CINDEX_LINKAGE bool clang_hasDefaultConstructor(CXScope S); 242 | 243 | /** 244 | * Returns the default constructor of a class, if any. 245 | */ 246 | CINDEX_LINKAGE CXScope clang_getDefaultConstructor(CXScope S); 247 | 248 | /** 249 | * Returns the class destructor, if any. 250 | */ 251 | CINDEX_LINKAGE CXScope clang_getDestructor(CXScope S); 252 | 253 | /** 254 | * Returns a stringified version of a given function signature in the form: 255 | * void N::f(int i, double d, long l = 0, char ch = 'a'). 256 | */ 257 | CINDEX_LINKAGE CXString clang_getFunctionSignature(CXScope func); 258 | 259 | /** 260 | * Checks if a function is a templated function. 261 | */ 262 | CINDEX_LINKAGE bool clang_isTemplatedFunction(CXScope func); 263 | 264 | /** 265 | * This function performs a lookup to check if there is a templated function of 266 | * that type. \c parent is mandatory, the global scope should be used as the 267 | * default value. 268 | */ 269 | CINDEX_LINKAGE bool clang_existsFunctionTemplate(const char* name, 270 | CXScope parent); 271 | 272 | typedef struct { 273 | void* Type; 274 | const char* IntegralValue; 275 | } CXTemplateArgInfo; 276 | 277 | /** 278 | * Builds a template instantiation for a given templated declaration. 279 | * Offers a single interface for instantiation of class, function and variable 280 | * templates. 281 | * 282 | * \param[in] tmpl The uninstantiated template class/function. 283 | * 284 | * \param[in] template_args The pointer to vector of template arguments stored 285 | * in the \c TemplateArgInfo struct 286 | * 287 | * \param[in] template_args_size The size of the vector of template arguments 288 | * passed as \c template_args 289 | * 290 | * \returns a \c CXScope representing the instantiated templated 291 | * class/function/variable. 292 | */ 293 | CINDEX_LINKAGE CXScope clang_instantiateTemplate( 294 | CXScope tmpl, CXTemplateArgInfo* template_args, size_t template_args_size); 295 | 296 | /** 297 | * A fake CXType for working with the interpreter. 298 | * It has the same structure as CXType, but unlike CXType, it stores a 299 | * handle to the interpreter in the second slot of the data field. 300 | */ 301 | typedef struct { 302 | enum CXTypeKind kind; 303 | void* data[2]; 304 | } CXQualType; 305 | 306 | /** 307 | * Gets the string of the type that is passed as a parameter. 308 | */ 309 | CINDEX_LINKAGE CXString clang_getTypeAsString(CXQualType type); 310 | 311 | /** 312 | * Returns the complex of the provided type. 313 | */ 314 | CINDEX_LINKAGE CXQualType clang_getComplexType(CXQualType eltype); 315 | 316 | /** 317 | * An opaque pointer representing the object of a given type (\c CXScope). 318 | */ 319 | typedef void* CXObject; 320 | 321 | /** 322 | * Allocates memory for the given type. 323 | */ 324 | CINDEX_LINKAGE CXObject clang_allocate(unsigned int n); 325 | 326 | /** 327 | * Deallocates memory for a given class. 328 | */ 329 | CINDEX_LINKAGE void clang_deallocate(CXObject address); 330 | 331 | /** 332 | * Creates an object of class \c scope and calls its default constructor. If \c 333 | * arena is set it uses placement new. 334 | */ 335 | CINDEX_LINKAGE CXObject clang_construct(CXScope scope, void* arena, 336 | size_t count = 1UL); 337 | 338 | /** 339 | * Creates a trampoline function and makes a call to a generic function or 340 | * method. 341 | * 342 | * \param func The function or method to call. 343 | * 344 | * \param result The location where the return result will be placed. 345 | * 346 | * \param args The arguments to pass to the invocation. 347 | * 348 | * \param n The number of arguments. 349 | * 350 | * \param self The 'this pointer' of the object. 351 | */ 352 | CINDEX_LINKAGE void clang_invoke(CXScope func, void* result, void** args, 353 | size_t n, void* self); 354 | 355 | /** 356 | * Calls the destructor of object of type \c type. When withFree is true it 357 | * calls operator delete/free. 358 | * 359 | * \param This The object to destruct. 360 | * 361 | * \param type The type of the object. 362 | * 363 | * \param withFree Whether to call operator delete/free or not. 364 | * 365 | * \returns true if wrapper generation and invocation succeeded. 366 | */ 367 | CINDEX_LINKAGE bool clang_destruct(CXObject This, CXScope S, 368 | bool withFree = true, size_t nary = 0UL); 369 | 370 | /** 371 | * @} 372 | */ 373 | 374 | LLVM_CLANG_C_EXTERN_C_END 375 | 376 | #endif // LLVM_CLANG_C_CXCPPINTEROP_H 377 | // NOLINTEND() -------------------------------------------------------------------------------- /docs/tutorials.rst: -------------------------------------------------------------------------------- 1 | Tutorials 2 | ---------- 3 | This tutorial emphasises the abilities and usage of CppInterOp. Let's get 4 | started! The tutorial demonstrates two examples, one in C and one in Python, 5 | for interoperability. 6 | 7 | **Note:This example library shown below is to illustrate the concept on which 8 | CppInterOp is based.** 9 | 10 | Python: 11 | ======= 12 | 13 | .. code-block:: python 14 | 15 | libInterop = ctypes.CDLL(libpath, mode = ctypes.RTLD_GLOBAL) 16 | _cpp_compile = libInterop.Clang_Parse 17 | _cpp_compile.argtypes = [ctypes.c_char_p] 18 | 19 | # We are using ctypes for inducting our library, and *Clang_Parse*, which is 20 | # part of the library, for parsing the C++ code. 21 | 22 | .. code-block:: cpp 23 | 24 | Giving a glance at how the header file looks for our library : 25 | The header keeps our function declarations for the functions used in our 26 | library. 27 | 28 | # This basically parses our C++ code. 29 | void Clang_Parse(const char* Code); 30 | 31 | # Looks up an entity with the given name, possibly in the given Context. 32 | Decl_t Clang_LookupName(const char* Name, Decl_t Context); 33 | 34 | # Returns the address of a JIT'd function of the corresponding declaration. 35 | FnAddr_t Clang_GetFunctionAddress(Decl_t D); 36 | 37 | # Returns the name of any named decl (class, namespace) & template arguments 38 | std::string GetCompleteName(Decl_t A); 39 | 40 | # Allocates memory of underlying size of the passed declaration. 41 | void * Clang_CreateObject(Decl_t RecordDecl); 42 | 43 | # Instantiates a given templated declaration. 44 | Decl_t Clang_InstantiateTemplate(Decl_t D, const char* Name, const char* Args); 45 | 46 | The C++ code that is to be used in Python comes under this below section. This 47 | code is parsed by the CppInterOp library in the previous snippet and further 48 | compilation goes on. 49 | 50 | .. code-block:: cpp 51 | 52 | def cpp_compile(arg): 53 | return _cpp_compile(arg.encode("ascii")) 54 | 55 | cpp_compile(r"""\ 56 | void* operator new(__SIZE_TYPE__, void* __p) noexcept; 57 | extern "C" int printf(const char*,...); 58 | class A {}; 59 | class C {}; 60 | struct B : public A { 61 | template 62 | void callme(T, S, U*) { printf(" call me may B! \n"); } 63 | }; 64 | """) 65 | 66 | .. code-block:: python 67 | 68 | class CppInterOpLayerWrapper: 69 | _get_scope = libInterop.Clang_LookupName 70 | _get_scope.restype = ctypes.c_size_t 71 | _get_scope.argtypes = [ctypes.c_char_p] 72 | 73 | _construct = libInterop.Clang_CreateObject 74 | _construct.restype = ctypes.c_void_p 75 | _construct.argtypes = [ctypes.c_size_t] 76 | 77 | _get_template_ct = libInterop.Clang_InstantiateTemplate 78 | _get_template_ct.restype = ctypes.c_size_t 79 | _get_template_ct.argtypes = [ctypes.c_size_t, ctypes.c_char_p, ctypes.c_char_p] 80 | 81 | def _get_template(self, scope, name, args): 82 | return self._get_template_ct(scope, name.encode("ascii"), args.encode("ascii")) 83 | 84 | def get_scope(self, name): 85 | return self._get_scope(name.encode("ascii")) 86 | 87 | def get_template(self, scope, name, tmpl_args = [], tpargs = []): 88 | if tmpl_args: 89 | # Instantiation is explicit from full name 90 | full_name = name + '<' + ', '.join([a for a in tmpl_args]) + '>' 91 | meth = self._get_template(scope, full_name, '') 92 | elif tpargs: 93 | # Instantiation is implicit from argument types 94 | meth = self._get_template(scope, name, ', '.join([a.__name__ for a in tpargs])) 95 | return CallCPPFunc(meth) 96 | 97 | def construct(self, cpptype): 98 | return self._construct(cpptype) 99 | 100 | The class CppInterOpLayerWrapper is supposed to provide a Python wrapper over 101 | the cppinterop layer. Here, we have the functions *Clang_LookupName, 102 | Clang_CreateObject and Clang_InstantiateTemplate* are being used, so these 103 | are being wrapped to be used in the Python language. 104 | 105 | We get to know the scope of the attribute of class by using get_scope. In a 106 | similar manner, we can use get_namespace. 107 | 108 | .. code-block::python 109 | 110 | class TemplateWrapper: 111 | def __init__(self, scope, name): 112 | self._scope = scope 113 | self._name = name 114 | 115 | def __getitem__(self, *args, **kwds): 116 | # Look up the template and return the overload. 117 | return gIL.get_template( 118 | self._scope, self._name, tmpl_args = args) 119 | 120 | def __call__(self, *args, **kwds): 121 | # Keyword arguments are not supported for this demo. 122 | assert not kwds 123 | 124 | # Construct the template arguments from the types and find the overload. 125 | ol = gIL.get_template( 126 | self._scope, self._name, tpargs = [type(a) for a in args]) 127 | 128 | # Call actual method. 129 | ol(*args, **kwds) 130 | 131 | In this example for instantiating templates, we need the wrapper for the 132 | function being used, which is responsible for finding a template that matches 133 | the arguments. 134 | 135 | .. code-block::python 136 | 137 | gIL = CppInterOpLayerWrapper() 138 | 139 | def cpp_allocate(proxy): 140 | pyobj = object.__new__(proxy) 141 | proxy.__init__(pyobj) 142 | pyobj.cppobj = gIL.construct(proxy.handle) 143 | return pyobj 144 | 145 | 146 | .. code-block::python 147 | 148 | if __name__ == '__main__': 149 | # create a couple of types to play with 150 | CppA = type('A', (), { 151 | 'handle' : gIL.get_scope('A'), 152 | '__new__' : cpp_allocate 153 | }) 154 | h = gIL.get_scope('B') 155 | CppB = type('B', (CppA,), { 156 | 'handle' : h, 157 | '__new__' : cpp_allocate, 158 | 'callme' : TemplateWrapper(h, 'callme') 159 | }) 160 | CppC = type('C', (), { 161 | 'handle' : gIL.get_scope('C'), 162 | '__new__' : cpp_allocate 163 | }) 164 | 165 | # call templates 166 | a = CppA() 167 | b = CppB() 168 | c = CppC() 169 | 170 | b.callme(a, 42, c) 171 | 172 | * In the main, we create types to access the class attributes and the wrapper 173 | can be supplied as the parameter in the map of the type given. 174 | 175 | * We are using a python wrapper around functions to be supplied to the map for 176 | the identification and usage of the function. 177 | 178 | * Finally, the objects are created for the respective class and the desired 179 | function is called, which is `callme` function in this case. 180 | 181 | The complete example can found below: 182 | `Example `_. 184 | 185 | C: 186 | === 187 | 188 | Include **p3-ex4-lib.h**, which contains the declarations for the functions used 189 | in this code. The detailed summary of header comes in the latter part. 190 | 191 | 192 | The variable Code is given as a C-style string, it contains the C++ code 193 | to be parsed. It has two classes, class `A` and a templated class `B` with a member 194 | function callme. 195 | 196 | .. code-block:: C 197 | 198 | const char* Code = "void* operator new(__SIZE_TYPE__, void* __p) noexcept;" 199 | "extern \"C\" int printf(const char*,...);" 200 | "class A {};" 201 | "\n #include \n" 202 | "class B {" 203 | "public:" 204 | " template" 205 | " void callme(T) {" 206 | " printf(\" Instantiated with [%s] \\n \", typeid(T).name());" 207 | " }" 208 | "};"; 209 | 210 | The main() begins with the call to **Clang_Parse** from interop library responsible 211 | for parsing the provided C++ code. 212 | 213 | Next there a number of initializations, **Instantiation** is initialized to zero, 214 | it will be used to store the instantiated template. The **InstantiationArgs** 215 | is initialized to "A", it will be used as the argument when instantiating the template. 216 | `T` is initialized to zero, used to store the declaration of the type "T" used for 217 | instantiation. 218 | 219 | .. code-block:: C 220 | 221 | Decl_t Instantiation = 0; 222 | const char * InstantiationArgs = "A"; 223 | Decl_t TemplatedClass = Clang_LookupName("B", /*Context=*/0); 224 | Decl_t T = 0; 225 | 226 | This snippet checks command-line arguments were provided by the argc arguments. 227 | We take the first argument (`argv[1]`), parse it, then take the second argument 228 | (`argv[2]`) using **Clang_LookupName**, and reassigns **InstantiationArgs** to 229 | the third argument (`argv[3]`). In the else case, we decide to go with the "A". 230 | 231 | The code proceeds to instantiate the template `B::callme` with the given 232 | type, using the **Clang_InstantiateTemplate** function from the 233 | library. The instantiated template is stored in the **Instantiation**. 234 | 235 | .. code-block:: C 236 | 237 | Instantiation = Clang_InstantiateTemplate(TemplatedClass, "callme", InstantiationArgs); 238 | 239 | 240 | A function pointer **callme_fn_ptr** is declared with a type `fn_def` that represents 241 | the function taking a `void*` argument and returning void. The result of 242 | **Clang_GetFunctionAddress** is casted by the function pointer. 243 | 244 | .. code-block:: C 245 | 246 | typedef void (*fn_def)(void*); 247 | fn_def callme_fn_ptr = (fn_def) Clang_GetFunctionAddress(Instantiation); 248 | 249 | The code then creates an object of type `A` using **Clang_CreateObject**, and the 250 | pointer to this object is stored in `NewA`. 251 | 252 | .. code-block:: C 253 | 254 | void* NewA = Clang_CreateObject(T); 255 | 256 | Then the function pointer **callme_fn_ptr** is called with the `NewA`, which 257 | calls the member function `B::callme` with the instantiated type. Thus, the instantiation 258 | happens with type `A` and we get the below result. 259 | 260 | You get the **output** as : 261 | .. code-block:: bash 262 | 263 | Instantiated with [1A] 264 | 265 | In conclusion, this C code uses the CppInterOp library to dynamically instantiate 266 | templates and call member functions based on provided types. This example was to 267 | show how we can instantiate templates, in a similar manner we can use the 268 | CppInterOp library to use many other features and attributes of languages to 269 | interoperate. 270 | 271 | **Header File:** 272 | 273 | We wrap the library functions within the extern "C" for its usage in C programs. 274 | All the functions of the library which are to be used in the C program have to 275 | be under the extern C for the compiler to know the C++ code within. 276 | 277 | .. code-block:: C 278 | 279 | extern "C" { 280 | #endif // __cplusplus 281 | /// Process C++ code. 282 | /// 283 | void Clang_Parse(const char* Code); 284 | 285 | /// Looks up an entity with the given name, possibly in the given Context. 286 | /// 287 | Decl_t Clang_LookupName(const char* Name, Decl_t Context); 288 | 289 | /// Returns the address of a JIT'd function of the corresponding declaration. 290 | /// 291 | FnAddr_t Clang_GetFunctionAddress(Decl_t D); 292 | 293 | /// Allocates memory of underlying size of the passed declaration. 294 | /// 295 | void * Clang_CreateObject(Decl_t RecordDecl); 296 | 297 | /// Instantiates a given templated declaration. 298 | Decl_t Clang_InstantiateTemplate(Decl_t D, const char* Name, const char* Args); 299 | #ifdef __cplusplus 300 | } 301 | 302 | 303 | The complete example can found below: 304 | `Example `_. -------------------------------------------------------------------------------- /.github/workflows/deploy-pages.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | schedule: 9 | - cron: '30 20 * * *' # Warning: Timezone dep - 20:00 is 1:00 10 | 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | jobs: 17 | build: 18 | runs-on: ${{ matrix.os }} 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | include: 24 | - name: osx15-arm-clang-repl-20-emscripten_wasm 25 | os: macos-15 26 | clang-runtime: '20' 27 | cling: Off 28 | micromamba_shell_init: bash 29 | emsdk_ver: "4.0.9" 30 | 31 | steps: 32 | - uses: actions/checkout@v5 33 | with: 34 | fetch-depth: 0 35 | 36 | - name: Set up Python 37 | uses: actions/setup-python@v6 38 | with: 39 | python-version: '3.11' 40 | 41 | - name: Save PR Info 42 | uses: ./.github/actions/Miscellaneous/Save_PR_Info 43 | 44 | - name: Setup default Build Type 45 | uses: ./.github/actions/Miscellaneous/Select_Default_Build_Type 46 | 47 | - name: install mamba 48 | uses: mamba-org/setup-micromamba@main 49 | with: 50 | init-shell: >- 51 | ${{ matrix.micromamba_shell_init }} 52 | 53 | - name: Setup emsdk 54 | shell: bash -l {0} 55 | run: | 56 | git clone --depth=1 https://github.com/emscripten-core/emsdk.git 57 | cd emsdk 58 | ./emsdk install ${{ matrix.emsdk_ver }} 59 | 60 | - name: Restore cached LLVM-${{ matrix.clang-runtime }} and ${{ matrix.cling == 'On' && 'Cling' || 'Clang-REPL' }} build 61 | uses: actions/cache/restore@v4 62 | id: cache 63 | with: 64 | path: | 65 | llvm-project 66 | ${{ matrix.cling=='On' && 'cling' || '' }} 67 | key: ${{ env.CLING_HASH }}-${{ runner.os }}-${{ matrix.os }}-clang-${{ matrix.clang-runtime }}.x-emscripten 68 | 69 | - name: Emscripten build of CppInterOp on Unix systems 70 | if: ${{ runner.os != 'windows' }} 71 | shell: bash -l {0} 72 | run: | 73 | set -e 74 | ./emsdk/emsdk activate ${{matrix.emsdk_ver}} 75 | source ./emsdk/emsdk_env.sh 76 | micromamba create -f environment-wasm.yml --platform=emscripten-wasm32 77 | export SYSROOT_PATH=$PWD/emsdk/upstream/emscripten/cache/sysroot 78 | export PREFIX=$MAMBA_ROOT_PREFIX/envs/CppInterOp-wasm 79 | export CMAKE_PREFIX_PATH=$PREFIX 80 | export CMAKE_SYSTEM_PREFIX_PATH=$PREFIX 81 | 82 | LLVM_DIR="$(pwd)/llvm-project" 83 | LLVM_BUILD_DIR="$(pwd)/llvm-project/build" 84 | cling_on=$(echo "${{ matrix.cling }}" | tr '[:lower:]' '[:upper:]') 85 | if [[ "${cling_on}" == "ON" ]]; then 86 | CLING_DIR="$(pwd)/cling" 87 | CLING_BUILD_DIR="$(pwd)/cling/build" 88 | CPLUS_INCLUDE_PATH="${CLING_DIR}/tools/cling/include:${CLING_BUILD_DIR}/include:${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_BUILD_DIR}/include:${LLVM_BUILD_DIR}/tools/clang/include:$PWD/include" 89 | else 90 | CPLUS_INCLUDE_PATH="${LLVM_DIR}/llvm/include:${LLVM_DIR}/clang/include:${LLVM_BUILD_DIR}/include:${LLVM_BUILD_DIR}/tools/clang/include:$PWD/include" 91 | fi 92 | 93 | # Build CppInterOp next to cling and llvm-project. 94 | mkdir build 95 | cd build 96 | 97 | if [[ "${cling_on}" == "ON" ]]; then 98 | emcmake cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ 99 | -DCPPINTEROP_USE_CLING=ON \ 100 | -DCPPINTEROP_USE_REPL=OFF \ 101 | -DCMAKE_PREFIX_PATH=$PREFIX \ 102 | -DCling_DIR=$LLVM_BUILD_DIR/tools/cling \ 103 | -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ 104 | -DLLD_DIR=$LLVM_BUILD_DIR/lib/cmake/lld \ 105 | -DClang_DIR=$LLVM_BUILD_DIR/lib/cmake/clang \ 106 | -DBUILD_SHARED_LIBS=ON \ 107 | -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ 108 | -DCMAKE_INSTALL_PREFIX=$PREFIX \ 109 | -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ 110 | -DSYSROOT_PATH=$SYSROOT_PATH \ 111 | ../ 112 | else 113 | emcmake cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ 114 | -DCMAKE_PREFIX_PATH=$PREFIX \ 115 | -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ 116 | -DLLD_DIR=$LLVM_BUILD_DIR/lib/cmake/lld \ 117 | -DClang_DIR=$LLVM_BUILD_DIR/lib/cmake/clang \ 118 | -DBUILD_SHARED_LIBS=ON \ 119 | -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ 120 | -DCMAKE_INSTALL_PREFIX=$PREFIX \ 121 | -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ 122 | -DSYSROOT_PATH=$SYSROOT_PATH \ 123 | ../ 124 | fi 125 | emmake make -j ${{ env.ncpus }} check-cppinterop 126 | cd ./unittests/CppInterOp/ 127 | 128 | # Fresh install browsers, and run Emscripten tests in them 129 | # This is to match the Emscripten build instructions, where 130 | # we run in a fresh browser, to stop any extra installed 131 | # stuff interferring with the running of the tests 132 | # Explaination of options for emrun 133 | # --browser (name of browser on path) 134 | # --kill_exit makes it so that when emrun finishes, 135 | # that the headless browser we create is killed along with it 136 | # --timeout 60 is such that emrun is killed after 60 seconds if 137 | # still running. emrun should have finished long before then, 138 | # so if it is still running, something went wrong (such as a test 139 | # which crashed the html file). This will cause the ci to fail, 140 | # as a non 0 value of will be returned. 141 | # In the case of Chrome we have the extra --no-sandbox flag, as on 142 | # Ubuntu Chrome will refuse to run otherwise, as it expects to have 143 | # been installed with admin privileges. This flag allows it to run 144 | # in userspace. 145 | 146 | # Install Firefox 147 | wget "https://download.mozilla.org/?product=firefox-latest&os=osx&lang=en-US" -O Firefox-latest.dmg 148 | hdiutil attach Firefox-latest.dmg 149 | cp -r /Volumes/Firefox/Firefox.app $PWD 150 | hdiutil detach /Volumes/Firefox 151 | cd ./Firefox.app/Contents/MacOS/ 152 | export PATH="$PWD:$PATH" 153 | cd - 154 | 155 | # Install Google Chrome 156 | wget https://dl.google.com/chrome/mac/stable/accept_tos%3Dhttps%253A%252F%252Fwww.google.com%252Fintl%252Fen_ph%252Fchrome%252Fterms%252F%26_and_accept_tos%3Dhttps%253A%252F%252Fpolicies.google.com%252Fterms/googlechrome.pkg 157 | pkgutil --expand-full googlechrome.pkg google-chrome 158 | cd ./google-chrome/GoogleChrome.pkg/Payload/Google\ Chrome.app/Contents/MacOS/ 159 | export PATH="$PWD:$PATH" 160 | cd - 161 | 162 | # Run tests in browsers 163 | echo "Running CppInterOpTests in Firefox" 164 | emrun --browser="firefox" --kill_exit --timeout 60 --browser-args="--headless" CppInterOpTests.html 165 | echo "Running DynamicLibraryManagerTests in Firefox" 166 | emrun --browser="firefox" --kill_exit --timeout 60 --browser-args="--headless" DynamicLibraryManagerTests.html 167 | echo "Running CppInterOpTests in Google Chrome" 168 | emrun --browser="Google Chrome" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" CppInterOpTests.html 169 | echo "Running DynamicLibraryManagerTests in Google Chrome" 170 | emrun --browser="Google Chrome" --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html 171 | sudo safaridriver --enable 172 | python -m pip install selenium 173 | echo "Running CppInterOpTests in Safari" 174 | emrun --no_browser --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" CppInterOpTests.html & 175 | python ../../../scripts/browser_tests_safari.py CppInterOpTests.html 176 | echo "Running DynamicLibraryManagerTests in Safari" 177 | emrun --no_browser --kill_exit --timeout 60 --browser-args="--headless --no-sandbox" DynamicLibraryManagerTests.html & 178 | python ../../../scripts/browser_tests_safari.py DynamicLibraryManagerTests.html 179 | 180 | cd ../../ 181 | emmake make -j ${{ env.ncpus }} install 182 | 183 | cd .. 184 | 185 | echo "SYSROOT_PATH=$SYSROOT_PATH" >> $GITHUB_ENV 186 | echo "CB_PYTHON_DIR=$CB_PYTHON_DIR" >> $GITHUB_ENV 187 | echo "CPPINTEROP_DIR=$CPPINTEROP_DIR" >> $GITHUB_ENV 188 | echo "LLVM_BUILD_DIR=$LLVM_BUILD_DIR" >> $GITHUB_ENV 189 | echo "CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH" >> $GITHUB_ENV 190 | echo "PREFIX=$PREFIX" >> $GITHUB_ENV 191 | 192 | - name: Build xeus-cpp 193 | shell: bash -l {0} 194 | run: | 195 | ./emsdk/emsdk activate ${{matrix.emsdk_ver}} 196 | source ./emsdk/emsdk_env.sh 197 | micromamba activate CppInterOp-wasm 198 | git clone --depth=1 https://github.com/compiler-research/xeus-cpp.git 199 | cd ./xeus-cpp 200 | mkdir build 201 | pushd build 202 | export CMAKE_PREFIX_PATH=${{ env.PREFIX }} 203 | export CMAKE_SYSTEM_PREFIX_PATH=${{ env.PREFIX }} 204 | emcmake cmake \ 205 | -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ 206 | -DCMAKE_PREFIX_PATH=${{ env.PREFIX }} \ 207 | -DCMAKE_INSTALL_PREFIX=${{ env.PREFIX }} \ 208 | -DXEUS_CPP_EMSCRIPTEN_WASM_BUILD=ON \ 209 | -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ 210 | -DXEUS_CPP_RESOURCE_DIR=${{ env.LLVM_BUILD_DIR }}/lib/clang/${{ matrix.clang-runtime }} \ 211 | -DSYSROOT_PATH=${{ env.SYSROOT_PATH }} \ 212 | .. 213 | emmake make -j ${{ env.ncpus }} install 214 | 215 | - name: Test xeus-cpp C++ Emscripten 216 | shell: bash -l {0} 217 | run: | 218 | set -e 219 | micromamba activate CppInterOp-wasm 220 | cd ./xeus-cpp/build/test 221 | node test_xeus_cpp.js 222 | 223 | - name: Jupyter Lite integration 224 | shell: bash -l {0} 225 | run: | 226 | cd ./xeus-cpp/ 227 | micromamba create -n xeus-lite-host jupyterlite-core=0.6 jupyterlite-xeus jupyter_server jupyterlab notebook python-libarchive-c -c conda-forge 228 | micromamba activate xeus-lite-host 229 | jupyter lite build --XeusAddon.prefix=${{ env.PREFIX }} \ 230 | --contents notebooks/xeus-cpp-lite-demo.ipynb \ 231 | --contents notebooks/images/marie.png \ 232 | --contents notebooks/audio/audio.wav \ 233 | --XeusAddon.mounts="${{ env.PREFIX }}/share/xeus-cpp/tagfiles:/share/xeus-cpp/tagfiles" \ 234 | --XeusAddon.mounts="${{ env.PREFIX }}/etc/xeus-cpp/tags.d:/etc/xeus-cpp/tags.d" --output-dir dist 235 | 236 | - name: Upload artifact 237 | uses: actions/upload-pages-artifact@v3 238 | with: 239 | path: ./xeus-cpp/dist/ 240 | 241 | deploy: 242 | needs: build 243 | permissions: 244 | pages: write 245 | id-token: write 246 | environment: 247 | name: github-pages 248 | url: ${{ steps.deployment.outputs.page_url }} 249 | runs-on: ubuntu-22.04 250 | steps: 251 | - name: Deploy to GitHub Pages 252 | id: deployment 253 | uses: actions/deploy-pages@v4 254 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Native Builds 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | push: 7 | branches: [main] 8 | release: 9 | types: [published] 10 | schedule: 11 | - cron: '30 20 * * *' # Warning: Timezone dep - 20:00 is 1:00 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build: 19 | name: ${{ matrix.name }} 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | include: 25 | # Ubuntu Arm Jobs 26 | - name: ubu24-arm-gcc12-clang-repl-20 27 | os: ubuntu-24.04-arm 28 | compiler: gcc-12 29 | clang-runtime: '20' 30 | cling: Off 31 | cppyy: Off 32 | llvm_enable_projects: "clang" 33 | llvm_targets_to_build: "host;NVPTX" 34 | Valgrind: On 35 | - name: ubu24-arm-gcc12-clang-repl-19-cppyy 36 | os: ubuntu-24.04-arm 37 | compiler: gcc-12 38 | clang-runtime: '19' 39 | cling: Off 40 | cppyy: On 41 | llvm_enable_projects: "clang" 42 | llvm_targets_to_build: "host;NVPTX" 43 | - name: ubu24-arm-gcc12-clang-repl-18-cppyy 44 | os: ubuntu-24.04-arm 45 | compiler: gcc-12 46 | clang-runtime: '18' 47 | cling: Off 48 | cppyy: On 49 | llvm_enable_projects: "clang" 50 | llvm_targets_to_build: "host;NVPTX" 51 | - name: ubu24-arm-gcc9-clang18-cling-cppyy 52 | os: ubuntu-24.04-arm 53 | compiler: gcc-9 54 | clang-runtime: '18' 55 | cling: On 56 | cppyy: Off 57 | cling-version: '1.2' 58 | llvm_enable_projects: "clang" 59 | llvm_targets_to_build: "host;NVPTX" 60 | # Ubuntu X86 Jobs 61 | - name: ubu24-x86-gcc12-clang-repl-20-coverage 62 | os: ubuntu-24.04 63 | compiler: gcc-12 64 | clang-runtime: '20' 65 | cling: Off 66 | cppyy: Off 67 | llvm_enable_projects: "clang;compiler-rt" 68 | llvm_targets_to_build: "host;NVPTX" 69 | coverage: true 70 | oop-jit: On 71 | Valgrind: On 72 | - name: ubu24-x86-gcc12-clang-repl-20 73 | os: ubuntu-24.04 74 | compiler: gcc-12 75 | clang-runtime: '20' 76 | cling: Off 77 | cppyy: Off 78 | llvm_enable_projects: "clang" 79 | llvm_targets_to_build: "host;NVPTX" 80 | - name: ubu24-x86-gcc12-clang-repl-20-out-of-process 81 | os: ubuntu-24.04 82 | compiler: gcc-12 83 | clang-runtime: '20' 84 | cling: Off 85 | cppyy: Off 86 | llvm_enable_projects: "clang;compiler-rt" 87 | llvm_targets_to_build: "host;NVPTX" 88 | oop-jit: On 89 | - name: ubu24-x86-gcc12-clang-repl-19-cppyy 90 | os: ubuntu-24.04 91 | compiler: gcc-12 92 | clang-runtime: '19' 93 | cling: Off 94 | cppyy: On 95 | llvm_enable_projects: "clang" 96 | llvm_targets_to_build: "host;NVPTX" 97 | - name: ubu24-x86-gcc12-clang-repl-18-cppyy 98 | os: ubuntu-24.04 99 | compiler: gcc-12 100 | clang-runtime: '18' 101 | cling: Off 102 | cppyy: On 103 | llvm_enable_projects: "clang" 104 | llvm_targets_to_build: "host;NVPTX" 105 | - name: ubu24-x86-gcc9-clang18-cling-cppyy 106 | os: ubuntu-24.04 107 | compiler: gcc-9 108 | clang-runtime: '18' 109 | cling: On 110 | cppyy: Off 111 | cling-version: '1.2' 112 | llvm_enable_projects: "clang" 113 | llvm_targets_to_build: "host;NVPTX" 114 | # MacOS Arm Jobs 115 | - name: osx15-arm-clang-clang-repl-20-out-of-process 116 | os: macos-15 117 | compiler: clang 118 | clang-runtime: '20' 119 | cling: Off 120 | cppyy: Off 121 | llvm_enable_projects: "clang;compiler-rt" 122 | llvm_targets_to_build: "host" 123 | oop-jit: On 124 | - name: osx15-arm-clang-clang-repl-20 125 | os: macos-15 126 | compiler: clang 127 | clang-runtime: '20' 128 | cling: Off 129 | cppyy: Off 130 | llvm_enable_projects: "clang" 131 | llvm_targets_to_build: "host" 132 | - name: osx15-arm-clang-clang-repl-19-cppyy 133 | os: macos-15 134 | compiler: clang 135 | clang-runtime: '19' 136 | cling: Off 137 | cppyy: On 138 | llvm_enable_projects: "clang" 139 | llvm_targets_to_build: "host" 140 | - name: osx15-arm-clang-clang-repl-18-cppyy 141 | os: macos-15 142 | compiler: clang 143 | clang-runtime: '18' 144 | cling: Off 145 | cppyy: On 146 | llvm_enable_projects: "clang" 147 | llvm_targets_to_build: "host" 148 | - name: osx15-arm-clang-clang18-cling-cppyy 149 | os: macos-15 150 | compiler: clang 151 | clang-runtime: '18' 152 | cling: On 153 | cppyy: On 154 | cling-version: '1.2' 155 | llvm_enable_projects: "clang" 156 | llvm_targets_to_build: "host;NVPTX" 157 | # MacOS X86 Jobs 158 | - name: osx15-x86-clang-clang-repl-20 159 | os: macos-15-intel 160 | compiler: clang 161 | clang-runtime: '20' 162 | cling: Off 163 | cppyy: Off 164 | llvm_enable_projects: "clang" 165 | llvm_targets_to_build: "host" 166 | - name: osx15-x86-clang-clang-repl-19-cppyy 167 | os: macos-15-intel 168 | compiler: clang 169 | clang-runtime: '19' 170 | cling: Off 171 | cppyy: On 172 | llvm_enable_projects: "clang" 173 | llvm_targets_to_build: "host" 174 | - name: osx15-x86-clang-clang-repl-18-cppyy 175 | os: macos-15-intel 176 | compiler: clang 177 | clang-runtime: '18' 178 | cling: Off 179 | cppyy: On 180 | llvm_enable_projects: "clang" 181 | llvm_targets_to_build: "host" 182 | - name: osx15-x86-clang-clang18-cling-cppyy 183 | os: macos-15-intel 184 | compiler: clang 185 | clang-runtime: '18' 186 | cling: On 187 | cppyy: On 188 | cling-version: '1.2' 189 | llvm_enable_projects: "clang" 190 | llvm_targets_to_build: "host;NVPTX" 191 | # Windows Arm Jobs 192 | - name: win11-msvc-clang-repl-20 193 | os: windows-11-arm 194 | compiler: msvc 195 | clang-runtime: '20' 196 | cling: Off 197 | llvm_enable_projects: "clang" 198 | llvm_targets_to_build: "host;NVPTX" 199 | - name: win11-msvc-clang18-cling 200 | os: windows-11-arm 201 | compiler: msvc 202 | clang-runtime: '18' 203 | cling: On 204 | cling-version: '1.2' 205 | llvm_enable_projects: "clang" 206 | llvm_targets_to_build: "host;NVPTX" 207 | # Windows X86 Jobs 208 | - name: win2025-msvc-clang-repl-20 209 | os: windows-2025 210 | compiler: msvc 211 | clang-runtime: '20' 212 | cling: Off 213 | llvm_enable_projects: "clang" 214 | llvm_targets_to_build: "host;NVPTX" 215 | - name: win2025-msvc-clang18-cling 216 | os: windows-2025 217 | compiler: msvc 218 | clang-runtime: '18' 219 | cling: On 220 | cling-version: '1.2' 221 | llvm_enable_projects: "clang" 222 | llvm_targets_to_build: "host;NVPTX" 223 | 224 | steps: 225 | - uses: actions/checkout@v5 226 | with: 227 | fetch-depth: 0 228 | 229 | - name: Set up Python 230 | uses: actions/setup-python@v6 231 | with: 232 | python-version: '3.11' 233 | 234 | - name: Save PR Info 235 | uses: ./.github/actions/Miscellaneous/Save_PR_Info 236 | 237 | - name: Restore cached LLVM-${{ matrix.clang-runtime }} and ${{ matrix.cling == 'On' && 'Cling' || 'Clang-REPL' }} build 238 | uses: actions/cache/restore@v4 239 | id: cache 240 | with: 241 | path: | 242 | llvm-project 243 | ${{ matrix.cling=='On' && 'cling' || '' }} 244 | key: ${{ env.CLING_HASH }}-${{ runner.os }}-${{ matrix.os }}-${{ matrix.compiler }}-clang-${{ matrix.clang-runtime }}.x-patch-${{ hashFiles(format('patches/llvm/clang{0}-*.patch', matrix.clang-runtime)) || 'none' }}${{ matrix.oop-jit == 'On' && '-oop' || '' }} 245 | 246 | - name: Setup default Build Type 247 | uses: ./.github/actions/Miscellaneous/Select_Default_Build_Type 248 | 249 | - name: Setup compiler 250 | uses: ./.github/actions/Miscellaneous/Setup_Compiler 251 | 252 | - name: Install dependencies 253 | uses: ./.github/actions/Miscellaneous/Install_Dependencies 254 | 255 | - name: Build LLVM-${{ matrix.clang-runtime }} and ${{ matrix.cling == 'On' && 'Cling' || 'Clang-REPL' }} 256 | uses: ./.github/actions/Build_LLVM 257 | with: 258 | cache-hit: ${{ steps.cache.outputs.cache-hit }} 259 | 260 | - name: Cache LLVM-${{ matrix.clang-runtime }} and ${{ matrix.cling == 'On' && 'Cling' || 'Clang-REPL' }} build 261 | uses: actions/cache/save@v4 262 | if: ${{ steps.cache.outputs.cache-hit != 'true' }} 263 | with: 264 | path: | 265 | llvm-project 266 | ${{ matrix.cling=='On' && 'cling' || '' }} 267 | key: ${{ steps.cache.outputs.cache-primary-key }} 268 | 269 | - name: Setup code coverage 270 | if: ${{ success() && (matrix.coverage == true) }} 271 | run: | 272 | sudo apt install lcov 273 | echo "CODE_COVERAGE=1" >> $GITHUB_ENV 274 | echo "BUILD_TYPE=Debug" >> $GITHUB_ENV 275 | 276 | - name: Build and test CppInterOp 277 | uses: ./.github/actions/Build_and_Test_CppInterOp 278 | 279 | - name: Prepare code coverage report 280 | if: ${{ success() && (matrix.coverage == true) }} 281 | run: | 282 | # Create lcov report 283 | # capture coverage info 284 | vers="${CC#*-}" 285 | lcov --directory build/ --capture --output-file coverage.info --gcov-tool /usr/bin/gcov-${vers} --ignore-errors mismatch 286 | lcov --remove coverage.info '/usr/*' ${{ github.workspace }}'/llvm-project/*' --ignore-errors unused --output-file coverage.info 287 | lcov --remove coverage.info '${{ github.workspace }}/unittests/*' --ignore-errors unused --output-file coverage.info 288 | lcov --remove coverage.info '${{ github.workspace }}/build/*' --ignore-errors unused --output-file coverage.info 289 | # output coverage data for debugging (optional) 290 | lcov --list coverage.info 291 | rm -rf ./build/ 292 | 293 | - name: Upload to codecov.io 294 | if: ${{ success() && (matrix.coverage == true) }} 295 | uses: codecov/codecov-action@v5 296 | with: 297 | files: ./coverage.info 298 | fail_ci_if_error: true 299 | verbose: true 300 | token: ${{ secrets.CODECOV_TOKEN }} 301 | 302 | - name: Build and test cppyy 303 | uses: ./.github/actions/Build_and_Test_cppyy 304 | 305 | - name: Show debug info 306 | if: ${{ failure() }} 307 | run: | 308 | export 309 | echo $GITHUB_ENV 310 | 311 | - name: Setup tmate session 312 | if: ${{ failure() && runner.debug }} 313 | uses: mxschmitt/action-tmate@v3 314 | # When debugging increase to a suitable value! 315 | timeout-minutes: 30 316 | 317 | --------------------------------------------------------------------------------